@jvittechs/j 1.0.57 → 1.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DVBQLA4R.js +43 -0
- package/dist/chunk-DVBQLA4R.js.map +1 -0
- package/dist/{chunk-FZBVI5AX.js → chunk-S7QUPWSM.js} +360 -14
- package/dist/chunk-S7QUPWSM.js.map +1 -0
- package/dist/chunk-Z464RBPB.js +247 -0
- package/dist/chunk-Z464RBPB.js.map +1 -0
- package/dist/cli.js +882 -777
- package/dist/cli.js.map +1 -1
- package/dist/{components.service-NWAWKII3.js → components.service-JUUV4CUI.js} +2 -1
- package/dist/show-AJ5M3SKQ.js +9 -0
- package/dist/show-AJ5M3SKQ.js.map +1 -0
- package/dist/summary-R4WPFJ2U.js +11 -0
- package/dist/summary-R4WPFJ2U.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-FZBVI5AX.js.map +0 -1
- package/dist/summary-4J2OCCSA.js +0 -9
- /package/dist/{components.service-NWAWKII3.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{summary-4J2OCCSA.js.map → components.service-JUUV4CUI.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -6,14 +6,22 @@ import {
|
|
|
6
6
|
NotFoundError,
|
|
7
7
|
ValidationError
|
|
8
8
|
} from "./chunk-XZ7VS36G.js";
|
|
9
|
+
import {
|
|
10
|
+
createSettingsShowCommand
|
|
11
|
+
} from "./chunk-DVBQLA4R.js";
|
|
9
12
|
import {
|
|
10
13
|
BLOCKED_ICON,
|
|
14
|
+
ConfigService,
|
|
11
15
|
PRIORITY_ICONS,
|
|
12
16
|
PRIORITY_LABELS,
|
|
13
17
|
STATUS_ICONS,
|
|
14
18
|
TaskService,
|
|
15
19
|
createTaskSummaryCommand
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-S7QUPWSM.js";
|
|
21
|
+
import {
|
|
22
|
+
SettingsService
|
|
23
|
+
} from "./chunk-Z464RBPB.js";
|
|
24
|
+
import "./chunk-DGUM43GV.js";
|
|
17
25
|
|
|
18
26
|
// src/utils/node-version-check.ts
|
|
19
27
|
import chalk from "chalk";
|
|
@@ -49,7 +57,7 @@ function checkNodeVersion() {
|
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
// src/cli.ts
|
|
52
|
-
import { Command as
|
|
60
|
+
import { Command as Command103 } from "commander";
|
|
53
61
|
|
|
54
62
|
// src/services/error-log.service.ts
|
|
55
63
|
import { promises as fs } from "fs";
|
|
@@ -149,7 +157,7 @@ import { basename as basename5 } from "path";
|
|
|
149
157
|
// package.json
|
|
150
158
|
var package_default = {
|
|
151
159
|
name: "@jvittechs/j",
|
|
152
|
-
version: "1.0.
|
|
160
|
+
version: "1.0.58",
|
|
153
161
|
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
162
|
type: "module",
|
|
155
163
|
bin: {
|
|
@@ -249,76 +257,6 @@ var package_default = {
|
|
|
249
257
|
}
|
|
250
258
|
};
|
|
251
259
|
|
|
252
|
-
// src/services/config.service.ts
|
|
253
|
-
import { promises as fs2 } from "fs";
|
|
254
|
-
import { join as join2 } from "path";
|
|
255
|
-
import { homedir as homedir2 } from "os";
|
|
256
|
-
var ConfigService = class {
|
|
257
|
-
configDir;
|
|
258
|
-
configPath;
|
|
259
|
-
constructor() {
|
|
260
|
-
this.configDir = join2(homedir2(), ".jai1");
|
|
261
|
-
this.configPath = join2(this.configDir, "config.json");
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Check if config file exists
|
|
265
|
-
*/
|
|
266
|
-
async exists() {
|
|
267
|
-
try {
|
|
268
|
-
await fs2.access(this.configPath);
|
|
269
|
-
return true;
|
|
270
|
-
} catch {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Load configuration from file
|
|
276
|
-
* @returns Config object or null if not found
|
|
277
|
-
*/
|
|
278
|
-
async load() {
|
|
279
|
-
if (!await this.exists()) {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
try {
|
|
283
|
-
const content = await fs2.readFile(this.configPath, "utf-8");
|
|
284
|
-
return JSON.parse(content);
|
|
285
|
-
} catch (error) {
|
|
286
|
-
throw new Error(
|
|
287
|
-
`Failed to load config: ${error instanceof Error ? error.message : String(error)}`
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Save configuration to file
|
|
293
|
-
* Creates directory if it doesn't exist
|
|
294
|
-
* Sets proper file permissions (600)
|
|
295
|
-
*/
|
|
296
|
-
async save(config) {
|
|
297
|
-
try {
|
|
298
|
-
await fs2.mkdir(this.configDir, { recursive: true, mode: 448 });
|
|
299
|
-
await fs2.writeFile(this.configPath, JSON.stringify(config, null, 2), {
|
|
300
|
-
mode: 384
|
|
301
|
-
});
|
|
302
|
-
} catch (error) {
|
|
303
|
-
throw new Error(
|
|
304
|
-
`Failed to save config: ${error instanceof Error ? error.message : String(error)}`
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Get config file path
|
|
310
|
-
*/
|
|
311
|
-
getConfigPath() {
|
|
312
|
-
return this.configPath;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Get config directory path
|
|
316
|
-
*/
|
|
317
|
-
getConfigDir() {
|
|
318
|
-
return this.configDir;
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
|
|
322
260
|
// src/utils/cli-name.ts
|
|
323
261
|
import { basename } from "path";
|
|
324
262
|
function getCliName() {
|
|
@@ -400,7 +338,7 @@ import chalk2 from "chalk";
|
|
|
400
338
|
import boxen from "boxen";
|
|
401
339
|
|
|
402
340
|
// src/services/tracking.service.ts
|
|
403
|
-
import { platform, release, arch, hostname, homedir as
|
|
341
|
+
import { platform, release, arch, hostname, homedir as homedir2 } from "os";
|
|
404
342
|
import { version as nodeVersion } from "process";
|
|
405
343
|
function getSystemInfo() {
|
|
406
344
|
return {
|
|
@@ -409,7 +347,7 @@ function getSystemInfo() {
|
|
|
409
347
|
arch: arch(),
|
|
410
348
|
nodeVersion,
|
|
411
349
|
hostname: hostname(),
|
|
412
|
-
homeDir:
|
|
350
|
+
homeDir: homedir2()
|
|
413
351
|
};
|
|
414
352
|
}
|
|
415
353
|
var TrackingService = class {
|
|
@@ -592,9 +530,9 @@ async function handleAuth(options) {
|
|
|
592
530
|
import { Command as Command2 } from "commander";
|
|
593
531
|
import chalk3 from "chalk";
|
|
594
532
|
import boxen2 from "boxen";
|
|
595
|
-
import { promises as
|
|
596
|
-
import { join as
|
|
597
|
-
import { homedir as
|
|
533
|
+
import { promises as fs2 } from "fs";
|
|
534
|
+
import { join as join2 } from "path";
|
|
535
|
+
import { homedir as homedir3 } from "os";
|
|
598
536
|
function createStatusCommand() {
|
|
599
537
|
const cmd = new Command2("status").description("Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh v\xE0 components").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
600
538
|
await handleStatus(options);
|
|
@@ -608,7 +546,7 @@ async function handleStatus(options) {
|
|
|
608
546
|
throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
|
|
609
547
|
}
|
|
610
548
|
const componentsService = new ComponentsService();
|
|
611
|
-
const frameworkPath =
|
|
549
|
+
const frameworkPath = join2(homedir3(), ".jai1", "framework");
|
|
612
550
|
const projectStatus = await getProjectStatus();
|
|
613
551
|
const installedComponents = await componentsService.getInstalled();
|
|
614
552
|
const componentCount = Object.keys(installedComponents).length;
|
|
@@ -665,9 +603,9 @@ function maskKey(key) {
|
|
|
665
603
|
return "****" + key.slice(-4);
|
|
666
604
|
}
|
|
667
605
|
async function getProjectStatus() {
|
|
668
|
-
const projectJai1 =
|
|
606
|
+
const projectJai1 = join2(process.cwd(), ".jai1");
|
|
669
607
|
try {
|
|
670
|
-
await
|
|
608
|
+
await fs2.access(projectJai1);
|
|
671
609
|
return { exists: true, version: "Synced" };
|
|
672
610
|
} catch {
|
|
673
611
|
return { exists: false };
|
|
@@ -686,7 +624,7 @@ import Spinner from "ink-spinner";
|
|
|
686
624
|
import TextInput from "ink-text-input";
|
|
687
625
|
|
|
688
626
|
// src/services/migrate-ide.service.ts
|
|
689
|
-
import { promises as
|
|
627
|
+
import { promises as fs3 } from "fs";
|
|
690
628
|
import path from "path";
|
|
691
629
|
import matter from "gray-matter";
|
|
692
630
|
|
|
@@ -876,18 +814,18 @@ var MigrateIdeService = class {
|
|
|
876
814
|
const items = [];
|
|
877
815
|
const presetDir = path.join(this.jai1Path, "rule-preset");
|
|
878
816
|
try {
|
|
879
|
-
await
|
|
817
|
+
await fs3.access(presetDir);
|
|
880
818
|
} catch {
|
|
881
819
|
return items;
|
|
882
820
|
}
|
|
883
|
-
const files = await
|
|
821
|
+
const files = await fs3.readdir(presetDir);
|
|
884
822
|
for (const file of files) {
|
|
885
823
|
if (!file.endsWith(".md") && !file.endsWith(".mdc")) continue;
|
|
886
824
|
if (file === "preset.json") continue;
|
|
887
825
|
const filepath = path.join(presetDir, file);
|
|
888
|
-
const stat = await
|
|
826
|
+
const stat = await fs3.stat(filepath);
|
|
889
827
|
if (!stat.isFile()) continue;
|
|
890
|
-
const content = await
|
|
828
|
+
const content = await fs3.readFile(filepath, "utf-8");
|
|
891
829
|
let frontmatter = {};
|
|
892
830
|
try {
|
|
893
831
|
const { data } = matter(content);
|
|
@@ -917,13 +855,13 @@ var MigrateIdeService = class {
|
|
|
917
855
|
const items = [];
|
|
918
856
|
const dirPath = path.join(this.jai1Path, type);
|
|
919
857
|
try {
|
|
920
|
-
await
|
|
858
|
+
await fs3.access(dirPath);
|
|
921
859
|
} catch {
|
|
922
860
|
return items;
|
|
923
861
|
}
|
|
924
862
|
const mdFiles = await this.collectMdFiles(dirPath, dirPath);
|
|
925
863
|
for (const { filepath, relativeName } of mdFiles) {
|
|
926
|
-
const content = await
|
|
864
|
+
const content = await fs3.readFile(filepath, "utf-8");
|
|
927
865
|
let frontmatter = {};
|
|
928
866
|
try {
|
|
929
867
|
const { data } = matter(content);
|
|
@@ -962,7 +900,7 @@ var MigrateIdeService = class {
|
|
|
962
900
|
alwaysApply: sourceItem.alwaysApply,
|
|
963
901
|
sourceFile: sourceItem.relativePath
|
|
964
902
|
});
|
|
965
|
-
const sourceContent = await
|
|
903
|
+
const sourceContent = await fs3.readFile(sourceItem.filepath, "utf-8");
|
|
966
904
|
const bodyContent = extractBody(sourceContent);
|
|
967
905
|
if (frontmatter) {
|
|
968
906
|
return `${frontmatter}
|
|
@@ -1008,7 +946,7 @@ ${bodyContent}
|
|
|
1008
946
|
try {
|
|
1009
947
|
let status = "created";
|
|
1010
948
|
try {
|
|
1011
|
-
await
|
|
949
|
+
await fs3.access(agentsPath);
|
|
1012
950
|
status = "updated";
|
|
1013
951
|
} catch {
|
|
1014
952
|
}
|
|
@@ -1017,7 +955,7 @@ ${bodyContent}
|
|
|
1017
955
|
""
|
|
1018
956
|
];
|
|
1019
957
|
for (const rule of rules) {
|
|
1020
|
-
const content = await
|
|
958
|
+
const content = await fs3.readFile(rule.filepath, "utf-8");
|
|
1021
959
|
const body = extractBody(content);
|
|
1022
960
|
if (body.trim()) {
|
|
1023
961
|
lines.push(downshiftHeadings(body));
|
|
@@ -1025,7 +963,7 @@ ${bodyContent}
|
|
|
1025
963
|
}
|
|
1026
964
|
}
|
|
1027
965
|
lines.push("");
|
|
1028
|
-
await
|
|
966
|
+
await fs3.writeFile(agentsPath, lines.join("\n"), "utf-8");
|
|
1029
967
|
return {
|
|
1030
968
|
source: {
|
|
1031
969
|
type: "rules",
|
|
@@ -1071,15 +1009,15 @@ ${bodyContent}
|
|
|
1071
1009
|
};
|
|
1072
1010
|
}
|
|
1073
1011
|
const targetDir = path.dirname(targetPath);
|
|
1074
|
-
await
|
|
1012
|
+
await fs3.mkdir(targetDir, { recursive: true });
|
|
1075
1013
|
let status = "created";
|
|
1076
1014
|
try {
|
|
1077
|
-
await
|
|
1015
|
+
await fs3.access(targetPath);
|
|
1078
1016
|
status = "updated";
|
|
1079
1017
|
} catch {
|
|
1080
1018
|
}
|
|
1081
1019
|
const copiedContent = await this.generateCopiedContent(ide, item);
|
|
1082
|
-
await
|
|
1020
|
+
await fs3.writeFile(targetPath, copiedContent, "utf-8");
|
|
1083
1021
|
return {
|
|
1084
1022
|
source: item,
|
|
1085
1023
|
targetIDE: ide,
|
|
@@ -1134,7 +1072,7 @@ ${bodyContent}
|
|
|
1134
1072
|
const results = [];
|
|
1135
1073
|
let entries;
|
|
1136
1074
|
try {
|
|
1137
|
-
entries = await
|
|
1075
|
+
entries = await fs3.readdir(currentDir, { withFileTypes: true });
|
|
1138
1076
|
} catch {
|
|
1139
1077
|
return results;
|
|
1140
1078
|
}
|
|
@@ -2352,7 +2290,7 @@ import { Command as Command7 } from "commander";
|
|
|
2352
2290
|
import chalk6 from "chalk";
|
|
2353
2291
|
|
|
2354
2292
|
// src/services/context-scanner.service.ts
|
|
2355
|
-
import { promises as
|
|
2293
|
+
import { promises as fs4 } from "fs";
|
|
2356
2294
|
import path2 from "path";
|
|
2357
2295
|
import matter2 from "gray-matter";
|
|
2358
2296
|
|
|
@@ -2573,7 +2511,7 @@ var ContextScannerService = class {
|
|
|
2573
2511
|
} else {
|
|
2574
2512
|
dirPath = path2.join(this.projectPath, config.basePath, relativePath);
|
|
2575
2513
|
try {
|
|
2576
|
-
await
|
|
2514
|
+
await fs4.access(dirPath);
|
|
2577
2515
|
} catch {
|
|
2578
2516
|
return [];
|
|
2579
2517
|
}
|
|
@@ -2584,10 +2522,10 @@ var ContextScannerService = class {
|
|
|
2584
2522
|
const skillItems = await this.scanSkills(dirPath, ide);
|
|
2585
2523
|
items.push(...skillItems);
|
|
2586
2524
|
} else {
|
|
2587
|
-
const files = await
|
|
2525
|
+
const files = await fs4.readdir(dirPath);
|
|
2588
2526
|
for (const file of files) {
|
|
2589
2527
|
const filepath = path2.join(dirPath, file);
|
|
2590
|
-
const stat = await
|
|
2528
|
+
const stat = await fs4.stat(filepath);
|
|
2591
2529
|
if (!stat.isFile()) continue;
|
|
2592
2530
|
const matchesExtension = extensions.some((ext) => file.endsWith(ext));
|
|
2593
2531
|
if (!matchesExtension) continue;
|
|
@@ -2607,13 +2545,13 @@ var ContextScannerService = class {
|
|
|
2607
2545
|
async scanSkills(skillsDir, ide) {
|
|
2608
2546
|
const items = [];
|
|
2609
2547
|
try {
|
|
2610
|
-
const entries = await
|
|
2548
|
+
const entries = await fs4.readdir(skillsDir, { withFileTypes: true });
|
|
2611
2549
|
for (const entry of entries) {
|
|
2612
2550
|
if (!entry.isDirectory()) continue;
|
|
2613
2551
|
const skillPath = path2.join(skillsDir, entry.name);
|
|
2614
2552
|
const skillFilePath = path2.join(skillPath, "SKILL.md");
|
|
2615
2553
|
try {
|
|
2616
|
-
await
|
|
2554
|
+
await fs4.access(skillFilePath);
|
|
2617
2555
|
} catch {
|
|
2618
2556
|
continue;
|
|
2619
2557
|
}
|
|
@@ -2648,8 +2586,8 @@ var ContextScannerService = class {
|
|
|
2648
2586
|
* Parse a context item from file
|
|
2649
2587
|
*/
|
|
2650
2588
|
async parseContextItem(filepath, ide, type) {
|
|
2651
|
-
const content = await
|
|
2652
|
-
const stat = await
|
|
2589
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
2590
|
+
const stat = await fs4.stat(filepath);
|
|
2653
2591
|
let frontmatter = {};
|
|
2654
2592
|
let bodyContent = content;
|
|
2655
2593
|
try {
|
|
@@ -2737,7 +2675,7 @@ var ContextScannerService = class {
|
|
|
2737
2675
|
*/
|
|
2738
2676
|
async pathExists(filepath) {
|
|
2739
2677
|
try {
|
|
2740
|
-
await
|
|
2678
|
+
await fs4.access(filepath);
|
|
2741
2679
|
return true;
|
|
2742
2680
|
} catch {
|
|
2743
2681
|
return false;
|
|
@@ -2902,7 +2840,7 @@ function createContextSubcommand() {
|
|
|
2902
2840
|
// src/commands/ide/setup.ts
|
|
2903
2841
|
import { Command as Command8 } from "commander";
|
|
2904
2842
|
import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
|
|
2905
|
-
import
|
|
2843
|
+
import fs5 from "fs/promises";
|
|
2906
2844
|
import path3 from "path";
|
|
2907
2845
|
import { existsSync } from "fs";
|
|
2908
2846
|
|
|
@@ -3154,13 +3092,13 @@ async function applyGroups(groupKeys, action) {
|
|
|
3154
3092
|
return;
|
|
3155
3093
|
}
|
|
3156
3094
|
if (!existsSync(vscodeDir)) {
|
|
3157
|
-
await
|
|
3095
|
+
await fs5.mkdir(vscodeDir, { recursive: true });
|
|
3158
3096
|
console.log("\u{1F4C1} Created .vscode/ directory");
|
|
3159
3097
|
}
|
|
3160
3098
|
let currentSettings = {};
|
|
3161
3099
|
if (existsSync(settingsPath)) {
|
|
3162
3100
|
try {
|
|
3163
|
-
const content = await
|
|
3101
|
+
const content = await fs5.readFile(settingsPath, "utf-8");
|
|
3164
3102
|
currentSettings = JSON.parse(content);
|
|
3165
3103
|
console.log("\u{1F4C4} Read current settings from settings.json");
|
|
3166
3104
|
} catch {
|
|
@@ -3200,7 +3138,7 @@ async function applyGroups(groupKeys, action) {
|
|
|
3200
3138
|
}
|
|
3201
3139
|
}
|
|
3202
3140
|
}
|
|
3203
|
-
await
|
|
3141
|
+
await fs5.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
3204
3142
|
console.log(`
|
|
3205
3143
|
\u2705 Updated IDE settings at: ${settingsPath}`);
|
|
3206
3144
|
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
@@ -3221,7 +3159,7 @@ async function resetSettings(groupKeys) {
|
|
|
3221
3159
|
return;
|
|
3222
3160
|
}
|
|
3223
3161
|
if (groupKeys.length === 0) {
|
|
3224
|
-
await
|
|
3162
|
+
await fs5.unlink(settingsPath);
|
|
3225
3163
|
console.log("\n\u2705 Deleted settings.json file");
|
|
3226
3164
|
} else {
|
|
3227
3165
|
await applyGroups(groupKeys, "disable");
|
|
@@ -3359,8 +3297,8 @@ async function runSync(options) {
|
|
|
3359
3297
|
import { Command as Command10 } from "commander";
|
|
3360
3298
|
|
|
3361
3299
|
// src/services/ide-detection.service.ts
|
|
3362
|
-
import { promises as
|
|
3363
|
-
import { join as
|
|
3300
|
+
import { promises as fs6 } from "fs";
|
|
3301
|
+
import { join as join3 } from "path";
|
|
3364
3302
|
|
|
3365
3303
|
// src/config/ide-formats.ts
|
|
3366
3304
|
var IDE_FORMATS = {
|
|
@@ -3474,7 +3412,7 @@ var IdeDetectionService = class {
|
|
|
3474
3412
|
confidence: "low"
|
|
3475
3413
|
};
|
|
3476
3414
|
if (ideId === "agentsmd") {
|
|
3477
|
-
const agentsPath =
|
|
3415
|
+
const agentsPath = join3(this.projectPath, "AGENTS.md");
|
|
3478
3416
|
const exists = await this.pathExists(agentsPath);
|
|
3479
3417
|
detection.detected = exists;
|
|
3480
3418
|
if (exists) {
|
|
@@ -3486,11 +3424,11 @@ var IdeDetectionService = class {
|
|
|
3486
3424
|
return detection;
|
|
3487
3425
|
}
|
|
3488
3426
|
if (ideId === "gemini") {
|
|
3489
|
-
const geminiPath =
|
|
3427
|
+
const geminiPath = join3(this.projectPath, "GEMINI.md");
|
|
3490
3428
|
const exists = await this.pathExists(geminiPath);
|
|
3491
3429
|
detection.detected = exists;
|
|
3492
3430
|
if (exists) {
|
|
3493
|
-
const agentsPath =
|
|
3431
|
+
const agentsPath = join3(this.projectPath, "AGENTS.md");
|
|
3494
3432
|
const agentsExists = await this.pathExists(agentsPath);
|
|
3495
3433
|
if (agentsExists) {
|
|
3496
3434
|
const customCount = await this.countAgentsMdCustomSections(agentsPath);
|
|
@@ -3502,9 +3440,9 @@ var IdeDetectionService = class {
|
|
|
3502
3440
|
return detection;
|
|
3503
3441
|
}
|
|
3504
3442
|
if (ideId === "opencode") {
|
|
3505
|
-
const agentsPath =
|
|
3443
|
+
const agentsPath = join3(this.projectPath, "AGENTS.md");
|
|
3506
3444
|
const hasAgents = await this.pathExists(agentsPath);
|
|
3507
|
-
const commandPath =
|
|
3445
|
+
const commandPath = join3(this.projectPath, ".opencode/command");
|
|
3508
3446
|
const hasCommands = await this.pathExists(commandPath);
|
|
3509
3447
|
if (hasAgents) {
|
|
3510
3448
|
const customCount = await this.countAgentsMdCustomSections(agentsPath);
|
|
@@ -3519,7 +3457,7 @@ var IdeDetectionService = class {
|
|
|
3519
3457
|
detection.confidence = detection.detected ? hasAgents && hasCommands ? "high" : "medium" : "low";
|
|
3520
3458
|
return detection;
|
|
3521
3459
|
}
|
|
3522
|
-
const rulesPath =
|
|
3460
|
+
const rulesPath = join3(this.projectPath, format.rulesPath);
|
|
3523
3461
|
const rulesExist = await this.pathExists(rulesPath);
|
|
3524
3462
|
if (rulesExist) {
|
|
3525
3463
|
const totalRules = await this.countFiles(rulesPath, format.fileExtension);
|
|
@@ -3531,7 +3469,7 @@ var IdeDetectionService = class {
|
|
|
3531
3469
|
}
|
|
3532
3470
|
}
|
|
3533
3471
|
if (format.workflowsPath) {
|
|
3534
|
-
const workflowsPath =
|
|
3472
|
+
const workflowsPath = join3(this.projectPath, format.workflowsPath);
|
|
3535
3473
|
const workflowsExist = await this.pathExists(workflowsPath);
|
|
3536
3474
|
if (workflowsExist) {
|
|
3537
3475
|
detection.hasWorkflows = true;
|
|
@@ -3599,13 +3537,13 @@ var IdeDetectionService = class {
|
|
|
3599
3537
|
}
|
|
3600
3538
|
} else if (ideId === "opencode") {
|
|
3601
3539
|
const hasAgents = await this.pathExists("AGENTS.md");
|
|
3602
|
-
const commandPath =
|
|
3540
|
+
const commandPath = join3(this.projectPath, ".opencode/command");
|
|
3603
3541
|
const hasCommands = await this.pathExists(commandPath);
|
|
3604
3542
|
if (hasAgents || hasCommands) {
|
|
3605
3543
|
detected.push(ideId);
|
|
3606
3544
|
}
|
|
3607
3545
|
} else {
|
|
3608
|
-
const rulesPath =
|
|
3546
|
+
const rulesPath = join3(this.projectPath, format.rulesPath);
|
|
3609
3547
|
if (await this.pathExists(rulesPath)) {
|
|
3610
3548
|
const count = await this.countFiles(rulesPath, format.fileExtension);
|
|
3611
3549
|
if (count > 0) {
|
|
@@ -3638,7 +3576,7 @@ var IdeDetectionService = class {
|
|
|
3638
3576
|
*/
|
|
3639
3577
|
async pathExists(path13) {
|
|
3640
3578
|
try {
|
|
3641
|
-
await
|
|
3579
|
+
await fs6.access(path13);
|
|
3642
3580
|
return true;
|
|
3643
3581
|
} catch {
|
|
3644
3582
|
return false;
|
|
@@ -3650,12 +3588,12 @@ var IdeDetectionService = class {
|
|
|
3650
3588
|
*/
|
|
3651
3589
|
async countFiles(dirPath, extension, excludeKeywords = []) {
|
|
3652
3590
|
try {
|
|
3653
|
-
const entries = await
|
|
3591
|
+
const entries = await fs6.readdir(dirPath, { withFileTypes: true });
|
|
3654
3592
|
let count = 0;
|
|
3655
3593
|
for (const entry of entries) {
|
|
3656
3594
|
if (entry.isDirectory()) {
|
|
3657
3595
|
count += await this.countFiles(
|
|
3658
|
-
|
|
3596
|
+
join3(dirPath, entry.name),
|
|
3659
3597
|
extension,
|
|
3660
3598
|
excludeKeywords
|
|
3661
3599
|
);
|
|
@@ -3680,7 +3618,7 @@ var IdeDetectionService = class {
|
|
|
3680
3618
|
*/
|
|
3681
3619
|
async countAgentsMdCustomSections(filePath) {
|
|
3682
3620
|
try {
|
|
3683
|
-
const content = await
|
|
3621
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
3684
3622
|
const h2Headings = content.match(/^## .+$/gm) || [];
|
|
3685
3623
|
return h2Headings.filter(
|
|
3686
3624
|
(h) => !h.toLowerCase().includes(DEFAULT_RULE_KEYWORD)
|
|
@@ -3700,7 +3638,7 @@ var IdeDetectionService = class {
|
|
|
3700
3638
|
*/
|
|
3701
3639
|
async suggestIdes() {
|
|
3702
3640
|
const suggestions = [];
|
|
3703
|
-
if (await this.pathExists(
|
|
3641
|
+
if (await this.pathExists(join3(this.projectPath, ".vscode"))) {
|
|
3704
3642
|
suggestions.push({
|
|
3705
3643
|
ideId: "cursor",
|
|
3706
3644
|
name: "Cursor",
|
|
@@ -3708,7 +3646,7 @@ var IdeDetectionService = class {
|
|
|
3708
3646
|
priority: "high"
|
|
3709
3647
|
});
|
|
3710
3648
|
}
|
|
3711
|
-
if (await this.pathExists(
|
|
3649
|
+
if (await this.pathExists(join3(this.projectPath, "package.json"))) {
|
|
3712
3650
|
suggestions.push({
|
|
3713
3651
|
ideId: "windsurf",
|
|
3714
3652
|
name: "Windsurf",
|
|
@@ -3722,7 +3660,7 @@ var IdeDetectionService = class {
|
|
|
3722
3660
|
reason: "Universal format, works with all IDEs",
|
|
3723
3661
|
priority: "high"
|
|
3724
3662
|
});
|
|
3725
|
-
const hasClaudeConfig = await this.pathExists(
|
|
3663
|
+
const hasClaudeConfig = await this.pathExists(join3(this.projectPath, ".claude"));
|
|
3726
3664
|
if (hasClaudeConfig) {
|
|
3727
3665
|
suggestions.push({
|
|
3728
3666
|
ideId: "claude",
|
|
@@ -3731,7 +3669,7 @@ var IdeDetectionService = class {
|
|
|
3731
3669
|
priority: "high"
|
|
3732
3670
|
});
|
|
3733
3671
|
}
|
|
3734
|
-
const hasOpenCodeConfig = await this.pathExists(
|
|
3672
|
+
const hasOpenCodeConfig = await this.pathExists(join3(this.projectPath, ".opencode"));
|
|
3735
3673
|
if (hasOpenCodeConfig) {
|
|
3736
3674
|
suggestions.push({
|
|
3737
3675
|
ideId: "opencode",
|
|
@@ -4005,8 +3943,8 @@ function showQuickstart(name) {
|
|
|
4005
3943
|
// src/commands/doctor.ts
|
|
4006
3944
|
import { Command as Command14 } from "commander";
|
|
4007
3945
|
import chalk10 from "chalk";
|
|
4008
|
-
import { promises as
|
|
4009
|
-
import { join as
|
|
3946
|
+
import { promises as fs7 } from "fs";
|
|
3947
|
+
import { join as join4 } from "path";
|
|
4010
3948
|
var CORE_FILES_FALLBACK = [
|
|
4011
3949
|
"context/jv-it-context.md",
|
|
4012
3950
|
"rules/jai1.md"
|
|
@@ -4114,9 +4052,9 @@ async function checkAuth(cliName) {
|
|
|
4114
4052
|
}
|
|
4115
4053
|
}
|
|
4116
4054
|
async function checkCoreFiles(cliName) {
|
|
4117
|
-
const jai1Dir =
|
|
4055
|
+
const jai1Dir = join4(process.cwd(), ".jai1");
|
|
4118
4056
|
try {
|
|
4119
|
-
await
|
|
4057
|
+
await fs7.access(jai1Dir);
|
|
4120
4058
|
} catch {
|
|
4121
4059
|
return {
|
|
4122
4060
|
name: "Core Package",
|
|
@@ -4127,8 +4065,8 @@ async function checkCoreFiles(cliName) {
|
|
|
4127
4065
|
}
|
|
4128
4066
|
let coreFiles = CORE_FILES_FALLBACK;
|
|
4129
4067
|
try {
|
|
4130
|
-
const manifestPath =
|
|
4131
|
-
const manifestContent = await
|
|
4068
|
+
const manifestPath = join4(jai1Dir, "manifest.json");
|
|
4069
|
+
const manifestContent = await fs7.readFile(manifestPath, "utf-8");
|
|
4132
4070
|
const manifest = JSON.parse(manifestContent);
|
|
4133
4071
|
const manifestFiles = Object.keys(manifest);
|
|
4134
4072
|
if (manifestFiles.length > 0) {
|
|
@@ -4139,9 +4077,9 @@ async function checkCoreFiles(cliName) {
|
|
|
4139
4077
|
const missing = [];
|
|
4140
4078
|
const found = [];
|
|
4141
4079
|
for (const file of coreFiles) {
|
|
4142
|
-
const filePath =
|
|
4080
|
+
const filePath = join4(jai1Dir, file);
|
|
4143
4081
|
try {
|
|
4144
|
-
await
|
|
4082
|
+
await fs7.access(filePath);
|
|
4145
4083
|
found.push(file);
|
|
4146
4084
|
} catch {
|
|
4147
4085
|
missing.push(file);
|
|
@@ -4904,7 +4842,7 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4904
4842
|
|
|
4905
4843
|
// src/server/web-chat-server.ts
|
|
4906
4844
|
import http from "http";
|
|
4907
|
-
import
|
|
4845
|
+
import fs9 from "fs";
|
|
4908
4846
|
import path5 from "path";
|
|
4909
4847
|
import { fileURLToPath } from "url";
|
|
4910
4848
|
|
|
@@ -5038,7 +4976,7 @@ var SessionManager = class {
|
|
|
5038
4976
|
};
|
|
5039
4977
|
|
|
5040
4978
|
// src/server/file-service.ts
|
|
5041
|
-
import
|
|
4979
|
+
import fs8 from "fs";
|
|
5042
4980
|
import path4 from "path";
|
|
5043
4981
|
import ignore from "ignore";
|
|
5044
4982
|
var ALWAYS_EXCLUDED = [
|
|
@@ -5247,8 +5185,8 @@ var FileService = class {
|
|
|
5247
5185
|
this.ignoreFilter.add(ALWAYS_EXCLUDED);
|
|
5248
5186
|
const gitignorePath = path4.join(this.workingDir, ".gitignore");
|
|
5249
5187
|
try {
|
|
5250
|
-
if (
|
|
5251
|
-
const content =
|
|
5188
|
+
if (fs8.existsSync(gitignorePath)) {
|
|
5189
|
+
const content = fs8.readFileSync(gitignorePath, "utf-8");
|
|
5252
5190
|
this.ignoreFilter.add(content);
|
|
5253
5191
|
}
|
|
5254
5192
|
} catch {
|
|
@@ -5289,7 +5227,7 @@ var FileService = class {
|
|
|
5289
5227
|
const searchDir = async (dirPath, depth = 0) => {
|
|
5290
5228
|
if (depth > 10 || results.length >= maxResults) return;
|
|
5291
5229
|
try {
|
|
5292
|
-
const entries = await
|
|
5230
|
+
const entries = await fs8.promises.readdir(dirPath, { withFileTypes: true });
|
|
5293
5231
|
for (const entry of entries) {
|
|
5294
5232
|
if (results.length >= maxResults) break;
|
|
5295
5233
|
const entryPath = path4.join(dirPath, entry.name);
|
|
@@ -5299,7 +5237,7 @@ var FileService = class {
|
|
|
5299
5237
|
if (normalizedRelPath.includes(normalizedQuery)) {
|
|
5300
5238
|
if (entry.isFile() && isTextFile(entry.name)) {
|
|
5301
5239
|
try {
|
|
5302
|
-
const stat = await
|
|
5240
|
+
const stat = await fs8.promises.stat(entryPath);
|
|
5303
5241
|
results.push({
|
|
5304
5242
|
path: relativePath,
|
|
5305
5243
|
name: entry.name,
|
|
@@ -5339,14 +5277,14 @@ var FileService = class {
|
|
|
5339
5277
|
if (!isTextFile(absolutePath)) {
|
|
5340
5278
|
throw new Error("Only text files can be read");
|
|
5341
5279
|
}
|
|
5342
|
-
const stat = await
|
|
5280
|
+
const stat = await fs8.promises.stat(absolutePath);
|
|
5343
5281
|
if (!stat.isFile()) {
|
|
5344
5282
|
throw new Error("Path is not a file");
|
|
5345
5283
|
}
|
|
5346
5284
|
if (stat.size > this.maxFileSize) {
|
|
5347
5285
|
throw new Error(`File exceeds maximum size of ${Math.round(this.maxFileSize / 1024)}KB`);
|
|
5348
5286
|
}
|
|
5349
|
-
const content = await
|
|
5287
|
+
const content = await fs8.promises.readFile(absolutePath, "utf-8");
|
|
5350
5288
|
return {
|
|
5351
5289
|
path: normalizedRelPath,
|
|
5352
5290
|
content,
|
|
@@ -5476,14 +5414,14 @@ function createWebChatServer(options) {
|
|
|
5476
5414
|
return;
|
|
5477
5415
|
}
|
|
5478
5416
|
try {
|
|
5479
|
-
const stat = await
|
|
5417
|
+
const stat = await fs9.promises.stat(fullPath);
|
|
5480
5418
|
if (!stat.isFile()) {
|
|
5481
5419
|
sendError(res, 404, "ERR-WC-007", "Not found");
|
|
5482
5420
|
return;
|
|
5483
5421
|
}
|
|
5484
5422
|
const ext = path5.extname(fullPath);
|
|
5485
5423
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
5486
|
-
const content = await
|
|
5424
|
+
const content = await fs9.promises.readFile(fullPath);
|
|
5487
5425
|
res.writeHead(200, { "Content-Type": contentType });
|
|
5488
5426
|
res.end(content);
|
|
5489
5427
|
} catch (error) {
|
|
@@ -6057,7 +5995,7 @@ function createStatsCommand() {
|
|
|
6057
5995
|
import { Command as Command18 } from "commander";
|
|
6058
5996
|
|
|
6059
5997
|
// src/services/translation.service.ts
|
|
6060
|
-
import { promises as
|
|
5998
|
+
import { promises as fs10 } from "fs";
|
|
6061
5999
|
import path6 from "path";
|
|
6062
6000
|
import pLimit from "p-limit";
|
|
6063
6001
|
import pRetry from "p-retry";
|
|
@@ -6076,7 +6014,7 @@ var TranslationService = class {
|
|
|
6076
6014
|
*/
|
|
6077
6015
|
async detectInputType(input5) {
|
|
6078
6016
|
try {
|
|
6079
|
-
const stat = await
|
|
6017
|
+
const stat = await fs10.stat(input5);
|
|
6080
6018
|
if (stat.isDirectory()) return "folder";
|
|
6081
6019
|
if (stat.isFile()) return "file";
|
|
6082
6020
|
} catch {
|
|
@@ -6113,13 +6051,13 @@ var TranslationService = class {
|
|
|
6113
6051
|
*/
|
|
6114
6052
|
async translateFile(filePath) {
|
|
6115
6053
|
try {
|
|
6116
|
-
const content = await
|
|
6054
|
+
const content = await fs10.readFile(filePath, "utf-8");
|
|
6117
6055
|
const ext = path6.extname(filePath).toLowerCase();
|
|
6118
6056
|
const fileType = this.getFileType(ext);
|
|
6119
6057
|
const translatedContent = await this.translateWithRetry(content, fileType);
|
|
6120
6058
|
const outputPath = this.generateOutputPath(filePath);
|
|
6121
6059
|
if (!this.options.dryRun) {
|
|
6122
|
-
await
|
|
6060
|
+
await fs10.writeFile(outputPath, translatedContent, "utf-8");
|
|
6123
6061
|
}
|
|
6124
6062
|
return {
|
|
6125
6063
|
inputPath: filePath,
|
|
@@ -6184,7 +6122,7 @@ var TranslationService = class {
|
|
|
6184
6122
|
* Discover translatable files in folder recursively
|
|
6185
6123
|
*/
|
|
6186
6124
|
async discoverFiles(folderPath) {
|
|
6187
|
-
const entries = await
|
|
6125
|
+
const entries = await fs10.readdir(folderPath, { withFileTypes: true });
|
|
6188
6126
|
const files = [];
|
|
6189
6127
|
for (const entry of entries) {
|
|
6190
6128
|
const fullPath = path6.join(folderPath, entry.name);
|
|
@@ -6717,20 +6655,20 @@ function createImageCommand() {
|
|
|
6717
6655
|
import { Command as Command24 } from "commander";
|
|
6718
6656
|
import { select as select2, input, confirm as confirm5 } from "@inquirer/prompts";
|
|
6719
6657
|
import os from "os";
|
|
6720
|
-
import { promises as
|
|
6721
|
-
import { join as
|
|
6658
|
+
import { promises as fs11 } from "fs";
|
|
6659
|
+
import { join as join5 } from "path";
|
|
6722
6660
|
async function collectContext() {
|
|
6723
6661
|
const context = {
|
|
6724
6662
|
os: `${os.platform()} ${os.release()}`
|
|
6725
6663
|
};
|
|
6726
6664
|
try {
|
|
6727
6665
|
const packageJsonPath = new URL("../../package.json", import.meta.url);
|
|
6728
|
-
const packageJson = JSON.parse(await
|
|
6666
|
+
const packageJson = JSON.parse(await fs11.readFile(packageJsonPath, "utf-8"));
|
|
6729
6667
|
context.cli_version = packageJson.version;
|
|
6730
6668
|
} catch {
|
|
6731
6669
|
}
|
|
6732
6670
|
try {
|
|
6733
|
-
const projectPackageJson = await
|
|
6671
|
+
const projectPackageJson = await fs11.readFile(join5(process.cwd(), "package.json"), "utf-8");
|
|
6734
6672
|
const projectData = JSON.parse(projectPackageJson);
|
|
6735
6673
|
context.project_name = projectData.name;
|
|
6736
6674
|
} catch {
|
|
@@ -6933,8 +6871,8 @@ function createFeedbackCommand() {
|
|
|
6933
6871
|
import { Command as Command25 } from "commander";
|
|
6934
6872
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
6935
6873
|
import os2 from "os";
|
|
6936
|
-
import { promises as
|
|
6937
|
-
import { basename as basename2, join as
|
|
6874
|
+
import { promises as fs12 } from "fs";
|
|
6875
|
+
import { basename as basename2, join as join6 } from "path";
|
|
6938
6876
|
import { version as nodeVersion2 } from "process";
|
|
6939
6877
|
function createClientInfoCommand() {
|
|
6940
6878
|
const cmd = new Command25("client-info").description("T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n jai1").option("--json", "Output as JSON").option("--full", "Include component list (top 20)").option("--submit", "Submit client info to Jai1 feedback endpoint").option("--message <text>", "Additional message when submitting").option("--show-paths", "Include full project path").action(async (options) => {
|
|
@@ -7049,9 +6987,9 @@ async function collectClientInfo(config, options) {
|
|
|
7049
6987
|
return payload;
|
|
7050
6988
|
}
|
|
7051
6989
|
async function hasProjectJai1() {
|
|
7052
|
-
const projectJai1 =
|
|
6990
|
+
const projectJai1 = join6(process.cwd(), ".jai1");
|
|
7053
6991
|
try {
|
|
7054
|
-
await
|
|
6992
|
+
await fs12.access(projectJai1);
|
|
7055
6993
|
return true;
|
|
7056
6994
|
} catch {
|
|
7057
6995
|
return false;
|
|
@@ -9400,7 +9338,7 @@ var HttpView = () => {
|
|
|
9400
9338
|
import React23, { useState as useState17 } from "react";
|
|
9401
9339
|
import { Box as Box19, Text as Text20, useInput as useInput15 } from "ink";
|
|
9402
9340
|
import TextInput14 from "ink-text-input";
|
|
9403
|
-
import * as
|
|
9341
|
+
import * as fs13 from "fs";
|
|
9404
9342
|
import * as path7 from "path";
|
|
9405
9343
|
var MarkdownView = () => {
|
|
9406
9344
|
const [filePath, setFilePath] = useState17("");
|
|
@@ -9422,11 +9360,11 @@ var MarkdownView = () => {
|
|
|
9422
9360
|
try {
|
|
9423
9361
|
setError("");
|
|
9424
9362
|
const resolvedPath = path7.resolve(filePath);
|
|
9425
|
-
if (!
|
|
9363
|
+
if (!fs13.existsSync(resolvedPath)) {
|
|
9426
9364
|
setError(`File not found: ${resolvedPath}`);
|
|
9427
9365
|
return;
|
|
9428
9366
|
}
|
|
9429
|
-
let fileContent =
|
|
9367
|
+
let fileContent = fs13.readFileSync(resolvedPath, "utf-8");
|
|
9430
9368
|
fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
|
|
9431
9369
|
return `
|
|
9432
9370
|
\u{1F3A8} **Mermaid Diagram**
|
|
@@ -9736,7 +9674,7 @@ import Table4 from "cli-table3";
|
|
|
9736
9674
|
import ora from "ora";
|
|
9737
9675
|
|
|
9738
9676
|
// src/services/deps-detector.service.ts
|
|
9739
|
-
import * as
|
|
9677
|
+
import * as fs14 from "fs/promises";
|
|
9740
9678
|
import * as path8 from "path";
|
|
9741
9679
|
var DepsDetectorService = class {
|
|
9742
9680
|
/**
|
|
@@ -9804,7 +9742,7 @@ var DepsDetectorService = class {
|
|
|
9804
9742
|
async detectLaravel(cwd) {
|
|
9805
9743
|
try {
|
|
9806
9744
|
const composerPath = path8.join(cwd, "composer.json");
|
|
9807
|
-
const content = await
|
|
9745
|
+
const content = await fs14.readFile(composerPath, "utf-8");
|
|
9808
9746
|
const composerJson = JSON.parse(content);
|
|
9809
9747
|
const deps = {
|
|
9810
9748
|
...composerJson.require,
|
|
@@ -9820,7 +9758,7 @@ var DepsDetectorService = class {
|
|
|
9820
9758
|
*/
|
|
9821
9759
|
async fileExists(cwd, filename) {
|
|
9822
9760
|
try {
|
|
9823
|
-
await
|
|
9761
|
+
await fs14.access(path8.join(cwd, filename));
|
|
9824
9762
|
return true;
|
|
9825
9763
|
} catch {
|
|
9826
9764
|
return false;
|
|
@@ -9829,7 +9767,7 @@ var DepsDetectorService = class {
|
|
|
9829
9767
|
};
|
|
9830
9768
|
|
|
9831
9769
|
// src/services/deps.service.ts
|
|
9832
|
-
import { promises as
|
|
9770
|
+
import { promises as fs15 } from "fs";
|
|
9833
9771
|
import path9 from "path";
|
|
9834
9772
|
import { execSync } from "child_process";
|
|
9835
9773
|
import pLimit2 from "p-limit";
|
|
@@ -9866,7 +9804,7 @@ var DepsService = class {
|
|
|
9866
9804
|
async readPackageJson(cwd) {
|
|
9867
9805
|
const pkgPath = path9.join(cwd, "package.json");
|
|
9868
9806
|
try {
|
|
9869
|
-
const content = await
|
|
9807
|
+
const content = await fs15.readFile(pkgPath, "utf-8");
|
|
9870
9808
|
return JSON.parse(content);
|
|
9871
9809
|
} catch (error) {
|
|
9872
9810
|
if (error.code === "ENOENT") {
|
|
@@ -9957,7 +9895,7 @@ var DepsService = class {
|
|
|
9957
9895
|
];
|
|
9958
9896
|
for (const { file, pm } of lockFiles) {
|
|
9959
9897
|
try {
|
|
9960
|
-
await
|
|
9898
|
+
await fs15.access(path9.join(cwd, file));
|
|
9961
9899
|
return pm;
|
|
9962
9900
|
} catch {
|
|
9963
9901
|
}
|
|
@@ -10028,7 +9966,7 @@ var DepsService = class {
|
|
|
10028
9966
|
|
|
10029
9967
|
// src/services/deps-php.service.ts
|
|
10030
9968
|
import { execSync as execSync2 } from "child_process";
|
|
10031
|
-
import { promises as
|
|
9969
|
+
import { promises as fs16 } from "fs";
|
|
10032
9970
|
import path10 from "path";
|
|
10033
9971
|
var LARAVEL_PROTECTED_PACKAGES = /^laravel\//;
|
|
10034
9972
|
var DepsPhpService = class {
|
|
@@ -10088,7 +10026,7 @@ var DepsPhpService = class {
|
|
|
10088
10026
|
async readComposerJson(cwd) {
|
|
10089
10027
|
const composerPath = path10.join(cwd, "composer.json");
|
|
10090
10028
|
try {
|
|
10091
|
-
const content = await
|
|
10029
|
+
const content = await fs16.readFile(composerPath, "utf-8");
|
|
10092
10030
|
return JSON.parse(content);
|
|
10093
10031
|
} catch (error) {
|
|
10094
10032
|
if (error.code === "ENOENT") {
|
|
@@ -10150,7 +10088,7 @@ var DepsPhpService = class {
|
|
|
10150
10088
|
|
|
10151
10089
|
// src/services/deps-python.service.ts
|
|
10152
10090
|
import { execSync as execSync3 } from "child_process";
|
|
10153
|
-
import { promises as
|
|
10091
|
+
import { promises as fs17 } from "fs";
|
|
10154
10092
|
import path11 from "path";
|
|
10155
10093
|
import pLimit3 from "p-limit";
|
|
10156
10094
|
var DepsPythonService = class {
|
|
@@ -10201,7 +10139,7 @@ var DepsPythonService = class {
|
|
|
10201
10139
|
async checkPip(cwd, onProgress) {
|
|
10202
10140
|
const requirementsPath = path11.join(cwd, "requirements.txt");
|
|
10203
10141
|
try {
|
|
10204
|
-
const content = await
|
|
10142
|
+
const content = await fs17.readFile(requirementsPath, "utf-8");
|
|
10205
10143
|
const packages = this.parseRequirementsTxt(content);
|
|
10206
10144
|
return await this.fetchBulkVersions(packages, onProgress);
|
|
10207
10145
|
} catch (error) {
|
|
@@ -10313,7 +10251,7 @@ var DepsPythonService = class {
|
|
|
10313
10251
|
}
|
|
10314
10252
|
async fileExists(cwd, filename) {
|
|
10315
10253
|
try {
|
|
10316
|
-
await
|
|
10254
|
+
await fs17.access(path11.join(cwd, filename));
|
|
10317
10255
|
return true;
|
|
10318
10256
|
} catch {
|
|
10319
10257
|
return false;
|
|
@@ -11235,7 +11173,7 @@ function createDevCommand() {
|
|
|
11235
11173
|
// src/commands/hooks/index.ts
|
|
11236
11174
|
import { Command as Command57 } from "commander";
|
|
11237
11175
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, unlinkSync, chmodSync, mkdirSync } from "fs";
|
|
11238
|
-
import { join as
|
|
11176
|
+
import { join as join8 } from "path";
|
|
11239
11177
|
import { execSync as execSync4 } from "child_process";
|
|
11240
11178
|
import chalk30 from "chalk";
|
|
11241
11179
|
var MARKER_START = "# >>> jai1-hooks";
|
|
@@ -11280,7 +11218,7 @@ function getGitHooksDir() {
|
|
|
11280
11218
|
encoding: "utf-8",
|
|
11281
11219
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11282
11220
|
}).trim();
|
|
11283
|
-
return
|
|
11221
|
+
return join8(gitDir, "hooks");
|
|
11284
11222
|
} catch {
|
|
11285
11223
|
throw new Error("Th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i kh\xF4ng ph\u1EA3i git repository. H\xE3y ch\u1EA1y l\u1EC7nh n\xE0y trong m\u1ED9t git repo.");
|
|
11286
11224
|
}
|
|
@@ -11324,7 +11262,7 @@ function setupHooks() {
|
|
|
11324
11262
|
let installed = 0;
|
|
11325
11263
|
let skipped = 0;
|
|
11326
11264
|
for (const def of HOOK_DEFINITIONS) {
|
|
11327
|
-
const hookPath =
|
|
11265
|
+
const hookPath = join8(hooksDir, def.hookName);
|
|
11328
11266
|
const section = buildSection(def.script);
|
|
11329
11267
|
try {
|
|
11330
11268
|
if (existsSync3(hookPath)) {
|
|
@@ -11366,7 +11304,7 @@ function removeHooks() {
|
|
|
11366
11304
|
const hooksDir = getGitHooksDir();
|
|
11367
11305
|
let removed = 0;
|
|
11368
11306
|
for (const def of HOOK_DEFINITIONS) {
|
|
11369
|
-
const hookPath =
|
|
11307
|
+
const hookPath = join8(hooksDir, def.hookName);
|
|
11370
11308
|
if (!existsSync3(hookPath)) {
|
|
11371
11309
|
continue;
|
|
11372
11310
|
}
|
|
@@ -11418,23 +11356,123 @@ function createHooksCommand() {
|
|
|
11418
11356
|
return cmd;
|
|
11419
11357
|
}
|
|
11420
11358
|
|
|
11421
|
-
// src/commands/
|
|
11422
|
-
import { Command as
|
|
11359
|
+
// src/commands/settings/index.ts
|
|
11360
|
+
import { Command as Command61 } from "commander";
|
|
11423
11361
|
|
|
11424
|
-
// src/commands/
|
|
11362
|
+
// src/commands/settings/init.ts
|
|
11425
11363
|
import { Command as Command58 } from "commander";
|
|
11426
11364
|
import chalk31 from "chalk";
|
|
11365
|
+
function createSettingsInitCommand() {
|
|
11366
|
+
return new Command58("init").description("T\u1EA1o .jai1/settings.yaml v\u1EDBi defaults").option("-f, --force", "Ghi \u0111\xE8 file c\u0169 n\u1EBFu \u0111\xE3 t\u1ED3n t\u1EA1i").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11367
|
+
try {
|
|
11368
|
+
const service = new SettingsService();
|
|
11369
|
+
const settings = await service.init(options.force);
|
|
11370
|
+
if (options.json) {
|
|
11371
|
+
console.log(JSON.stringify({ success: true, path: service.getSettingsPath(), settings }, null, 2));
|
|
11372
|
+
return;
|
|
11373
|
+
}
|
|
11374
|
+
console.log(chalk31.green("\u2705 Settings initialized"));
|
|
11375
|
+
console.log(` ${chalk31.dim("Path:")} ${service.getSettingsPath()}`);
|
|
11376
|
+
} catch (error) {
|
|
11377
|
+
if (options.json) {
|
|
11378
|
+
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
|
|
11379
|
+
} else {
|
|
11380
|
+
console.error(chalk31.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11381
|
+
}
|
|
11382
|
+
process.exit(1);
|
|
11383
|
+
}
|
|
11384
|
+
});
|
|
11385
|
+
}
|
|
11386
|
+
|
|
11387
|
+
// src/commands/settings/set.ts
|
|
11388
|
+
import { Command as Command59 } from "commander";
|
|
11389
|
+
import chalk32 from "chalk";
|
|
11390
|
+
function createSettingsSetCommand() {
|
|
11391
|
+
return new Command59("set").description("Set a setting value (dot-notation)").argument("<key>", "Setting key (e.g., tasks.cloud)").argument("<value>", "Setting value").option("-j, --json", "Output JSON").action(async (key, value, options) => {
|
|
11392
|
+
try {
|
|
11393
|
+
const service = new SettingsService();
|
|
11394
|
+
if (!service.exists()) {
|
|
11395
|
+
throw new Error("Settings file not found. Run `j settings init` first.");
|
|
11396
|
+
}
|
|
11397
|
+
const settings = await service.set(key, value);
|
|
11398
|
+
if (options.json) {
|
|
11399
|
+
console.log(JSON.stringify({ success: true, key, value: service.get(key), settings }, null, 2));
|
|
11400
|
+
return;
|
|
11401
|
+
}
|
|
11402
|
+
console.log(chalk32.green(`\u2705 ${key} = ${JSON.stringify(service.get(key))}`));
|
|
11403
|
+
} catch (error) {
|
|
11404
|
+
if (options.json) {
|
|
11405
|
+
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
|
|
11406
|
+
} else {
|
|
11407
|
+
console.error(chalk32.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11408
|
+
}
|
|
11409
|
+
process.exit(1);
|
|
11410
|
+
}
|
|
11411
|
+
});
|
|
11412
|
+
}
|
|
11413
|
+
|
|
11414
|
+
// src/commands/settings/get.ts
|
|
11415
|
+
import { Command as Command60 } from "commander";
|
|
11416
|
+
import chalk33 from "chalk";
|
|
11417
|
+
function createSettingsGetCommand() {
|
|
11418
|
+
return new Command60("get").description("Get a setting value (dot-notation)").argument("<key>", "Setting key (e.g., tasks.cloud)").option("-j, --json", "Output JSON").action(async (key, options) => {
|
|
11419
|
+
try {
|
|
11420
|
+
const service = new SettingsService();
|
|
11421
|
+
const value = service.get(key);
|
|
11422
|
+
if (options.json) {
|
|
11423
|
+
console.log(JSON.stringify({ key, value }));
|
|
11424
|
+
return;
|
|
11425
|
+
}
|
|
11426
|
+
if (value === void 0) {
|
|
11427
|
+
console.log(chalk33.dim(`${key} is not set`));
|
|
11428
|
+
} else {
|
|
11429
|
+
console.log(`${chalk33.cyan(key)} = ${JSON.stringify(value)}`);
|
|
11430
|
+
}
|
|
11431
|
+
} catch (error) {
|
|
11432
|
+
if (options.json) {
|
|
11433
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
11434
|
+
} else {
|
|
11435
|
+
console.error(chalk33.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11436
|
+
}
|
|
11437
|
+
process.exit(1);
|
|
11438
|
+
}
|
|
11439
|
+
});
|
|
11440
|
+
}
|
|
11441
|
+
|
|
11442
|
+
// src/commands/settings/index.ts
|
|
11443
|
+
function createSettingsCommand() {
|
|
11444
|
+
const cmd = new Command61("settings").description("Project settings \u2014 manage .jai1/settings.yaml");
|
|
11445
|
+
cmd.addCommand(createSettingsShowCommand());
|
|
11446
|
+
cmd.addCommand(createSettingsInitCommand());
|
|
11447
|
+
cmd.addCommand(createSettingsSetCommand());
|
|
11448
|
+
cmd.addCommand(createSettingsGetCommand());
|
|
11449
|
+
cmd.action(async () => {
|
|
11450
|
+
const { createSettingsShowCommand: showCmd } = await import("./show-AJ5M3SKQ.js");
|
|
11451
|
+
const show = showCmd();
|
|
11452
|
+
await show.parseAsync([], { from: "user" });
|
|
11453
|
+
});
|
|
11454
|
+
return cmd;
|
|
11455
|
+
}
|
|
11456
|
+
|
|
11457
|
+
// src/commands/tasks/index.ts
|
|
11458
|
+
import { Command as Command75 } from "commander";
|
|
11459
|
+
|
|
11460
|
+
// src/commands/tasks/add.ts
|
|
11461
|
+
import { Command as Command62 } from "commander";
|
|
11462
|
+
import chalk34 from "chalk";
|
|
11427
11463
|
function createTaskAddCommand() {
|
|
11428
|
-
return new
|
|
11464
|
+
return new Command62("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, bug/xxx, plan/xxx, task/xxx, prd/xxx").option("-t, --tags <tags>", "Comma-separated tags").option("--prompt", "Type: prompt \u2014 free-form instruction, no doc reference required").option("-j, --json", "Output JSON").action(async (title, options) => {
|
|
11429
11465
|
const service = new TaskService();
|
|
11430
11466
|
const priority = Number(options.priority ?? 2);
|
|
11431
11467
|
if (priority < 0 || priority > 3) {
|
|
11432
|
-
console.error(
|
|
11468
|
+
console.error(chalk34.red("\u274C Priority must be 0-3"));
|
|
11433
11469
|
process.exit(1);
|
|
11434
11470
|
}
|
|
11435
11471
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
11472
|
+
const taskType = options.prompt ? "prompt" : "task";
|
|
11436
11473
|
const task = await service.add({
|
|
11437
11474
|
title,
|
|
11475
|
+
type: taskType,
|
|
11438
11476
|
parent: options.parent || "",
|
|
11439
11477
|
priority,
|
|
11440
11478
|
tags
|
|
@@ -11445,23 +11483,24 @@ function createTaskAddCommand() {
|
|
|
11445
11483
|
}
|
|
11446
11484
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
11447
11485
|
const label = PRIORITY_LABELS[task.priority] || "Medium";
|
|
11448
|
-
|
|
11449
|
-
console.log(
|
|
11450
|
-
console.log(` ${
|
|
11486
|
+
const typeLabel = task.type === "prompt" ? chalk34.magenta(" [prompt]") : "";
|
|
11487
|
+
console.log(chalk34.green(`\u2705 Task added: ${chalk34.bold(task.id)}${typeLabel}`));
|
|
11488
|
+
console.log(` ${chalk34.dim("Title:")} ${task.title}`);
|
|
11489
|
+
console.log(` ${chalk34.dim("Priority:")} ${icon} ${label}`);
|
|
11451
11490
|
if (task.parent) {
|
|
11452
|
-
console.log(` ${
|
|
11491
|
+
console.log(` ${chalk34.dim("Parent:")} ${task.parent}`);
|
|
11453
11492
|
}
|
|
11454
11493
|
if (task.tags.length > 0) {
|
|
11455
|
-
console.log(` ${
|
|
11494
|
+
console.log(` ${chalk34.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
11456
11495
|
}
|
|
11457
11496
|
});
|
|
11458
11497
|
}
|
|
11459
11498
|
|
|
11460
11499
|
// src/commands/tasks/list.ts
|
|
11461
|
-
import { Command as
|
|
11462
|
-
import
|
|
11500
|
+
import { Command as Command63 } from "commander";
|
|
11501
|
+
import chalk35 from "chalk";
|
|
11463
11502
|
function createTaskListCommand() {
|
|
11464
|
-
return new
|
|
11503
|
+
return new Command63("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) => {
|
|
11465
11504
|
await handleTaskList(options);
|
|
11466
11505
|
});
|
|
11467
11506
|
}
|
|
@@ -11480,12 +11519,12 @@ async function handleTaskList(options) {
|
|
|
11480
11519
|
return;
|
|
11481
11520
|
}
|
|
11482
11521
|
if (tasks.length === 0) {
|
|
11483
|
-
console.log(
|
|
11522
|
+
console.log(chalk35.dim("No tasks found."));
|
|
11484
11523
|
return;
|
|
11485
11524
|
}
|
|
11486
11525
|
const resolvedIds = new Set(allTasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id));
|
|
11487
11526
|
const header = options.parent ? `\u{1F4CB} ${options.parent} (${tasks.length} tasks)` : `\u{1F4CB} All tasks (${tasks.length})`;
|
|
11488
|
-
console.log(
|
|
11527
|
+
console.log(chalk35.bold(header));
|
|
11489
11528
|
console.log();
|
|
11490
11529
|
for (const task of tasks) {
|
|
11491
11530
|
printTaskLine(task, resolvedIds);
|
|
@@ -11495,25 +11534,25 @@ function printTaskLine(task, resolvedIds) {
|
|
|
11495
11534
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => resolvedIds.has(id));
|
|
11496
11535
|
const statusIcon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11497
11536
|
const priorityIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
11498
|
-
let line = ` ${statusIcon} ${
|
|
11537
|
+
let line = ` ${statusIcon} ${chalk35.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
|
|
11499
11538
|
if (task.status === "in_progress" && task.assigned_to) {
|
|
11500
|
-
line +=
|
|
11539
|
+
line += chalk35.cyan(` @${task.assigned_to}`);
|
|
11501
11540
|
}
|
|
11502
11541
|
if (isBlocked) {
|
|
11503
11542
|
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
11504
|
-
line +=
|
|
11543
|
+
line += chalk35.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
11505
11544
|
}
|
|
11506
11545
|
if (task.parent) {
|
|
11507
|
-
line +=
|
|
11546
|
+
line += chalk35.dim(` [${task.parent}]`);
|
|
11508
11547
|
}
|
|
11509
11548
|
console.log(line);
|
|
11510
11549
|
}
|
|
11511
11550
|
|
|
11512
11551
|
// src/commands/tasks/ready.ts
|
|
11513
|
-
import { Command as
|
|
11514
|
-
import
|
|
11552
|
+
import { Command as Command64 } from "commander";
|
|
11553
|
+
import chalk36 from "chalk";
|
|
11515
11554
|
function createTaskReadyCommand() {
|
|
11516
|
-
return new
|
|
11555
|
+
return new Command64("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) => {
|
|
11517
11556
|
const service = new TaskService();
|
|
11518
11557
|
const tasks = await service.getReady(options.parent);
|
|
11519
11558
|
if (options.json) {
|
|
@@ -11521,37 +11560,37 @@ function createTaskReadyCommand() {
|
|
|
11521
11560
|
return;
|
|
11522
11561
|
}
|
|
11523
11562
|
if (tasks.length === 0) {
|
|
11524
|
-
console.log(
|
|
11525
|
-
console.log(
|
|
11563
|
+
console.log(chalk36.dim("No tasks ready to pick."));
|
|
11564
|
+
console.log(chalk36.dim("\u{1F4A1} Check blocked tasks: jai1 t list -s todo"));
|
|
11526
11565
|
return;
|
|
11527
11566
|
}
|
|
11528
|
-
console.log(
|
|
11567
|
+
console.log(chalk36.bold(`\u{1F4CB} Ready to pick (${tasks.length} tasks):`));
|
|
11529
11568
|
console.log();
|
|
11530
11569
|
for (const task of tasks) {
|
|
11531
11570
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
11532
|
-
let line = ` P${task.priority}${icon} ${
|
|
11571
|
+
let line = ` P${task.priority}${icon} ${chalk36.dim(task.id)} ${task.title}`;
|
|
11533
11572
|
if (task.parent) {
|
|
11534
|
-
line +=
|
|
11573
|
+
line += chalk36.dim(` [${task.parent}]`);
|
|
11535
11574
|
}
|
|
11536
11575
|
console.log(line);
|
|
11537
11576
|
}
|
|
11538
11577
|
console.log();
|
|
11539
|
-
console.log(
|
|
11578
|
+
console.log(chalk36.dim("\u{1F4A1} Run: jai1 t pick"));
|
|
11540
11579
|
});
|
|
11541
11580
|
}
|
|
11542
11581
|
|
|
11543
11582
|
// src/commands/tasks/update.ts
|
|
11544
|
-
import { Command as
|
|
11545
|
-
import
|
|
11583
|
+
import { Command as Command65 } from "commander";
|
|
11584
|
+
import chalk37 from "chalk";
|
|
11546
11585
|
var VALID_STATUSES = ["todo", "in_progress", "done", "cancelled"];
|
|
11547
11586
|
function createTaskUpdateCommand() {
|
|
11548
|
-
return new
|
|
11587
|
+
return new Command65("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) => {
|
|
11549
11588
|
if (!options.status && !options.notes) {
|
|
11550
|
-
console.error(
|
|
11589
|
+
console.error(chalk37.red("\u274C At least one of --status or --notes is required"));
|
|
11551
11590
|
process.exit(1);
|
|
11552
11591
|
}
|
|
11553
11592
|
if (options.status && !VALID_STATUSES.includes(options.status)) {
|
|
11554
|
-
console.error(
|
|
11593
|
+
console.error(chalk37.red(`\u274C Invalid status. Must be: ${VALID_STATUSES.join(", ")}`));
|
|
11555
11594
|
process.exit(1);
|
|
11556
11595
|
}
|
|
11557
11596
|
const service = new TaskService();
|
|
@@ -11561,8 +11600,8 @@ function createTaskUpdateCommand() {
|
|
|
11561
11600
|
if (existingTask) {
|
|
11562
11601
|
const { blocked, blockedBy } = await service.isBlocked(existingTask);
|
|
11563
11602
|
if (blocked) {
|
|
11564
|
-
console.log(
|
|
11565
|
-
console.log(
|
|
11603
|
+
console.log(chalk37.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
|
|
11604
|
+
console.log(chalk37.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
|
|
11566
11605
|
}
|
|
11567
11606
|
}
|
|
11568
11607
|
}
|
|
@@ -11582,24 +11621,24 @@ function createTaskUpdateCommand() {
|
|
|
11582
11621
|
if (options.notes) {
|
|
11583
11622
|
parts.push(`\u{1F4DD} notes updated`);
|
|
11584
11623
|
}
|
|
11585
|
-
console.log(
|
|
11624
|
+
console.log(chalk37.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
|
|
11586
11625
|
} catch (error) {
|
|
11587
|
-
console.error(
|
|
11626
|
+
console.error(chalk37.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11588
11627
|
process.exit(1);
|
|
11589
11628
|
}
|
|
11590
11629
|
});
|
|
11591
11630
|
}
|
|
11592
11631
|
|
|
11593
11632
|
// src/commands/tasks/show.ts
|
|
11594
|
-
import { Command as
|
|
11595
|
-
import
|
|
11633
|
+
import { Command as Command66 } from "commander";
|
|
11634
|
+
import chalk38 from "chalk";
|
|
11596
11635
|
function createTaskShowCommand() {
|
|
11597
|
-
return new
|
|
11636
|
+
return new Command66("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) => {
|
|
11598
11637
|
const service = new TaskService();
|
|
11599
11638
|
if (query.startsWith("T-")) {
|
|
11600
11639
|
const task = await service.findById(query);
|
|
11601
11640
|
if (!task) {
|
|
11602
|
-
console.error(
|
|
11641
|
+
console.error(chalk38.red(`\u274C Task ${query} not found`));
|
|
11603
11642
|
process.exit(1);
|
|
11604
11643
|
}
|
|
11605
11644
|
if (options.json) {
|
|
@@ -11610,34 +11649,34 @@ function createTaskShowCommand() {
|
|
|
11610
11649
|
const statusIcon = blocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11611
11650
|
const priIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
11612
11651
|
const priLabel = PRIORITY_LABELS[task.priority] || "Medium";
|
|
11613
|
-
console.log(
|
|
11652
|
+
console.log(chalk38.bold(`
|
|
11614
11653
|
\u{1F4CC} ${task.id}: ${task.title}
|
|
11615
11654
|
`));
|
|
11616
|
-
console.log(` ${
|
|
11617
|
-
console.log(` ${
|
|
11655
|
+
console.log(` ${chalk38.dim("Status:")} ${statusIcon} ${task.status}${blocked ? chalk38.red(" (BLOCKED)") : ""}`);
|
|
11656
|
+
console.log(` ${chalk38.dim("Priority:")} ${priIcon} P${task.priority} ${priLabel}`);
|
|
11618
11657
|
if (task.parent) {
|
|
11619
|
-
console.log(` ${
|
|
11658
|
+
console.log(` ${chalk38.dim("Parent:")} ${task.parent}`);
|
|
11620
11659
|
}
|
|
11621
11660
|
if (task.assigned_to) {
|
|
11622
|
-
console.log(` ${
|
|
11661
|
+
console.log(` ${chalk38.dim("Assigned:")} @${task.assigned_to} (${task.claimed_at})`);
|
|
11623
11662
|
}
|
|
11624
11663
|
if (task.depends_on.length > 0) {
|
|
11625
|
-
console.log(` ${
|
|
11664
|
+
console.log(` ${chalk38.dim("Depends on:")} ${task.depends_on.join(", ")}`);
|
|
11626
11665
|
if (blocked) {
|
|
11627
|
-
console.log(` ${
|
|
11666
|
+
console.log(` ${chalk38.dim("Blocked by:")} ${chalk38.red(blockedBy.join(", "))}`);
|
|
11628
11667
|
}
|
|
11629
11668
|
}
|
|
11630
11669
|
if (task.tags.length > 0) {
|
|
11631
|
-
console.log(` ${
|
|
11670
|
+
console.log(` ${chalk38.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
11632
11671
|
}
|
|
11633
11672
|
if (task.branch) {
|
|
11634
|
-
console.log(` ${
|
|
11673
|
+
console.log(` ${chalk38.dim("Branch:")} ${task.branch}`);
|
|
11635
11674
|
}
|
|
11636
11675
|
if (task.notes) {
|
|
11637
|
-
console.log(` ${
|
|
11676
|
+
console.log(` ${chalk38.dim("Notes:")} ${task.notes}`);
|
|
11638
11677
|
}
|
|
11639
|
-
console.log(` ${
|
|
11640
|
-
console.log(` ${
|
|
11678
|
+
console.log(` ${chalk38.dim("Created:")} ${task.created}`);
|
|
11679
|
+
console.log(` ${chalk38.dim("Updated:")} ${task.updated}`);
|
|
11641
11680
|
console.log();
|
|
11642
11681
|
} else {
|
|
11643
11682
|
const tasks = await service.filter({ parent: query });
|
|
@@ -11646,10 +11685,10 @@ function createTaskShowCommand() {
|
|
|
11646
11685
|
return;
|
|
11647
11686
|
}
|
|
11648
11687
|
if (tasks.length === 0) {
|
|
11649
|
-
console.log(
|
|
11688
|
+
console.log(chalk38.dim(`No tasks for parent: ${query}`));
|
|
11650
11689
|
return;
|
|
11651
11690
|
}
|
|
11652
|
-
console.log(
|
|
11691
|
+
console.log(chalk38.bold(`
|
|
11653
11692
|
\u{1F4CB} ${query} (${tasks.length} tasks)
|
|
11654
11693
|
`));
|
|
11655
11694
|
const allTasks = await service.readAll();
|
|
@@ -11657,11 +11696,11 @@ function createTaskShowCommand() {
|
|
|
11657
11696
|
for (const task of tasks) {
|
|
11658
11697
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => doneIds.has(id));
|
|
11659
11698
|
const icon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11660
|
-
let line = ` ${icon} ${
|
|
11661
|
-
if (task.assigned_to) line +=
|
|
11699
|
+
let line = ` ${icon} ${chalk38.dim(task.id)} P${task.priority} ${task.title}`;
|
|
11700
|
+
if (task.assigned_to) line += chalk38.cyan(` @${task.assigned_to}`);
|
|
11662
11701
|
if (isBlocked) {
|
|
11663
11702
|
const bb = task.depends_on.filter((id) => !doneIds.has(id));
|
|
11664
|
-
line +=
|
|
11703
|
+
line += chalk38.red(` (blocked: ${bb.join(", ")})`);
|
|
11665
11704
|
}
|
|
11666
11705
|
console.log(line);
|
|
11667
11706
|
}
|
|
@@ -11671,11 +11710,11 @@ function createTaskShowCommand() {
|
|
|
11671
11710
|
}
|
|
11672
11711
|
|
|
11673
11712
|
// src/commands/tasks/pick.ts
|
|
11674
|
-
import { Command as
|
|
11675
|
-
import
|
|
11713
|
+
import { Command as Command67 } from "commander";
|
|
11714
|
+
import chalk39 from "chalk";
|
|
11676
11715
|
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
11677
11716
|
function createTaskPickCommand() {
|
|
11678
|
-
return new
|
|
11717
|
+
return new Command67("pick").description("Claim the next available task").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11679
11718
|
const service = new TaskService();
|
|
11680
11719
|
const ready = await service.getReady();
|
|
11681
11720
|
if (ready.length === 0) {
|
|
@@ -11683,8 +11722,8 @@ function createTaskPickCommand() {
|
|
|
11683
11722
|
console.log(JSON.stringify({ picked: null, message: "No tasks ready" }));
|
|
11684
11723
|
return;
|
|
11685
11724
|
}
|
|
11686
|
-
console.log(
|
|
11687
|
-
console.log(
|
|
11725
|
+
console.log(chalk39.dim("No tasks ready to pick."));
|
|
11726
|
+
console.log(chalk39.dim('\u{1F4A1} Add tasks first: jai1 t add "..."'));
|
|
11688
11727
|
return;
|
|
11689
11728
|
}
|
|
11690
11729
|
const top = ready[0];
|
|
@@ -11694,13 +11733,13 @@ function createTaskPickCommand() {
|
|
|
11694
11733
|
console.log(JSON.stringify(picked2, null, 2));
|
|
11695
11734
|
return;
|
|
11696
11735
|
}
|
|
11697
|
-
console.log(
|
|
11698
|
-
console.log(` ${
|
|
11736
|
+
console.log(chalk39.bold("\n\u{1F4CC} Next available task:"));
|
|
11737
|
+
console.log(` ${chalk39.bold(top.id)} P${top.priority}${icon} ${top.title}`);
|
|
11699
11738
|
if (top.parent) {
|
|
11700
|
-
console.log(` ${
|
|
11739
|
+
console.log(` ${chalk39.dim("Parent:")} ${top.parent}`);
|
|
11701
11740
|
}
|
|
11702
11741
|
if (ready.length > 1) {
|
|
11703
|
-
console.log(
|
|
11742
|
+
console.log(chalk39.dim(`
|
|
11704
11743
|
+${ready.length - 1} more tasks ready`));
|
|
11705
11744
|
}
|
|
11706
11745
|
const proceed = await confirm11({
|
|
@@ -11708,20 +11747,20 @@ function createTaskPickCommand() {
|
|
|
11708
11747
|
default: true
|
|
11709
11748
|
});
|
|
11710
11749
|
if (!proceed) {
|
|
11711
|
-
console.log(
|
|
11750
|
+
console.log(chalk39.dim("\nCancelled."));
|
|
11712
11751
|
return;
|
|
11713
11752
|
}
|
|
11714
11753
|
const picked = await service.pick(top.id);
|
|
11715
|
-
console.log(
|
|
11754
|
+
console.log(chalk39.green(`
|
|
11716
11755
|
\u2705 ${picked.id} assigned to @${picked.assigned_to}, status \u2192 in_progress`));
|
|
11717
11756
|
});
|
|
11718
11757
|
}
|
|
11719
11758
|
|
|
11720
11759
|
// src/commands/tasks/done.ts
|
|
11721
|
-
import { Command as
|
|
11722
|
-
import
|
|
11760
|
+
import { Command as Command68 } from "commander";
|
|
11761
|
+
import chalk40 from "chalk";
|
|
11723
11762
|
function createTaskDoneCommand() {
|
|
11724
|
-
return new
|
|
11763
|
+
return new Command68("done").description("Mark task as done").argument("<id>", "Task ID (e.g. T-001)").option("-j, --json", "Output JSON").action(async (id, options) => {
|
|
11725
11764
|
const service = new TaskService();
|
|
11726
11765
|
try {
|
|
11727
11766
|
const task = await service.markDone(id);
|
|
@@ -11729,19 +11768,19 @@ function createTaskDoneCommand() {
|
|
|
11729
11768
|
console.log(JSON.stringify(task, null, 2));
|
|
11730
11769
|
return;
|
|
11731
11770
|
}
|
|
11732
|
-
console.log(
|
|
11771
|
+
console.log(chalk40.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
|
|
11733
11772
|
} catch (error) {
|
|
11734
|
-
console.error(
|
|
11773
|
+
console.error(chalk40.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11735
11774
|
process.exit(1);
|
|
11736
11775
|
}
|
|
11737
11776
|
});
|
|
11738
11777
|
}
|
|
11739
11778
|
|
|
11740
11779
|
// src/commands/tasks/dep.ts
|
|
11741
|
-
import { Command as
|
|
11742
|
-
import
|
|
11780
|
+
import { Command as Command69 } from "commander";
|
|
11781
|
+
import chalk41 from "chalk";
|
|
11743
11782
|
function createTaskDepCommand() {
|
|
11744
|
-
return new
|
|
11783
|
+
return new Command69("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) => {
|
|
11745
11784
|
const service = new TaskService();
|
|
11746
11785
|
try {
|
|
11747
11786
|
const task = await service.addDependency(childId, parentId);
|
|
@@ -11749,138 +11788,203 @@ function createTaskDepCommand() {
|
|
|
11749
11788
|
console.log(JSON.stringify(task, null, 2));
|
|
11750
11789
|
return;
|
|
11751
11790
|
}
|
|
11752
|
-
console.log(
|
|
11753
|
-
console.log(
|
|
11791
|
+
console.log(chalk41.green(`\u2705 ${childId} now depends on ${parentId}`));
|
|
11792
|
+
console.log(chalk41.dim(` ${task.title} \u2192 waits for ${parentId}`));
|
|
11754
11793
|
} catch (error) {
|
|
11755
|
-
console.error(
|
|
11794
|
+
console.error(chalk41.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11756
11795
|
process.exit(1);
|
|
11757
11796
|
}
|
|
11758
11797
|
});
|
|
11759
11798
|
}
|
|
11760
11799
|
|
|
11761
11800
|
// src/commands/tasks/sync.ts
|
|
11762
|
-
import { Command as
|
|
11763
|
-
import
|
|
11801
|
+
import { Command as Command70 } from "commander";
|
|
11802
|
+
import chalk42 from "chalk";
|
|
11764
11803
|
function createTaskSyncCommand() {
|
|
11765
|
-
return new
|
|
11766
|
-
|
|
11804
|
+
return new Command70("sync").description("Sync tasks between cloud and local (requires tasks.cloud = true)").option("--pull", "Pull: cloud \u2192 local (replaces tasks.jsonl)").option("--push", "Push: local \u2192 cloud (upserts all)").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11805
|
+
const settings = new SettingsService();
|
|
11806
|
+
if (!settings.isTaskCloudEnabled()) {
|
|
11807
|
+
console.error(chalk42.red("\u274C tasks.cloud is not enabled in .jai1/settings.yaml"));
|
|
11808
|
+
console.error(chalk42.dim(" Run: j settings set tasks.cloud true"));
|
|
11809
|
+
process.exit(1);
|
|
11810
|
+
}
|
|
11811
|
+
const service = new TaskService();
|
|
11812
|
+
await service._providerReady;
|
|
11813
|
+
if (!service.isCloud) {
|
|
11814
|
+
console.error(chalk42.red("\u274C Cloud provider failed to initialize."));
|
|
11815
|
+
console.error(chalk42.dim(" Check that you are logged in: j auth"));
|
|
11816
|
+
process.exit(1);
|
|
11817
|
+
}
|
|
11818
|
+
const doPull = options.pull || !options.pull && !options.push;
|
|
11819
|
+
const doPush = options.push || !options.pull && !options.push;
|
|
11820
|
+
const isFull = doPull && doPush;
|
|
11821
|
+
if (isFull) {
|
|
11822
|
+
console.log(chalk42.bold("\u{1F504} Full sync: merging cloud \u2194 local..."));
|
|
11823
|
+
} else if (doPull) {
|
|
11824
|
+
console.log(chalk42.bold("\u2B07\uFE0F Pull: cloud \u2192 local..."));
|
|
11825
|
+
} else {
|
|
11826
|
+
console.log(chalk42.bold("\u2B06\uFE0F Push: local \u2192 cloud..."));
|
|
11827
|
+
}
|
|
11828
|
+
try {
|
|
11829
|
+
if (doPull) {
|
|
11830
|
+
const cloudTasks = await service.cloud.pull();
|
|
11831
|
+
console.log(chalk42.dim(` \u2193 ${cloudTasks.length} tasks fetched from cloud`));
|
|
11832
|
+
if (isFull) {
|
|
11833
|
+
const localTasks = await service.readAll();
|
|
11834
|
+
const localMap = new Map(localTasks.map((t) => [t.id, t]));
|
|
11835
|
+
const cloudMap = new Map(cloudTasks.map((t) => [t.id, t]));
|
|
11836
|
+
const merged = /* @__PURE__ */ new Map();
|
|
11837
|
+
for (const [id, local] of localMap) merged.set(id, local);
|
|
11838
|
+
for (const [id, cloud] of cloudMap) {
|
|
11839
|
+
const local = merged.get(id);
|
|
11840
|
+
if (!local || cloud.updated > local.updated) {
|
|
11841
|
+
merged.set(id, cloud);
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
for (const [id, cloud] of cloudMap) {
|
|
11845
|
+
if (!localMap.has(id)) merged.set(id, cloud);
|
|
11846
|
+
}
|
|
11847
|
+
const mergedList = Array.from(merged.values());
|
|
11848
|
+
await service.writeAll(mergedList);
|
|
11849
|
+
console.log(chalk42.green(`\u2705 Merged: ${mergedList.length} tasks`));
|
|
11850
|
+
console.log(chalk42.dim(` \u2191 Pushing merged tasks to cloud...`));
|
|
11851
|
+
await service.cloud.push(mergedList);
|
|
11852
|
+
console.log(chalk42.green(`\u2705 Full sync complete`));
|
|
11853
|
+
} else {
|
|
11854
|
+
await service.writeAll(cloudTasks);
|
|
11855
|
+
console.log(chalk42.green(`\u2705 Pull complete: ${cloudTasks.length} tasks written`));
|
|
11856
|
+
}
|
|
11857
|
+
} else if (doPush) {
|
|
11858
|
+
const localTasks = await service.readAll();
|
|
11859
|
+
await service.cloud.push(localTasks);
|
|
11860
|
+
console.log(chalk42.green(`\u2705 Push complete: ${localTasks.length} tasks uploaded`));
|
|
11861
|
+
}
|
|
11862
|
+
} catch (error) {
|
|
11863
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
11864
|
+
if (options.json) {
|
|
11865
|
+
console.log(JSON.stringify({ success: false, error: msg }));
|
|
11866
|
+
} else {
|
|
11867
|
+
console.error(chalk42.red(`\u274C Sync failed: ${msg}`));
|
|
11868
|
+
}
|
|
11869
|
+
process.exit(1);
|
|
11870
|
+
}
|
|
11767
11871
|
});
|
|
11768
11872
|
}
|
|
11769
11873
|
|
|
11770
11874
|
// src/commands/tasks/guide.ts
|
|
11771
|
-
import { Command as
|
|
11772
|
-
import
|
|
11875
|
+
import { Command as Command71 } from "commander";
|
|
11876
|
+
import chalk43 from "chalk";
|
|
11773
11877
|
var GUIDE_TEXT = `
|
|
11774
|
-
${
|
|
11775
|
-
|
|
11776
|
-
${
|
|
11777
|
-
${
|
|
11778
|
-
${
|
|
11779
|
-
${
|
|
11780
|
-
${
|
|
11781
|
-
${
|
|
11782
|
-
|
|
11783
|
-
${
|
|
11784
|
-
${
|
|
11785
|
-
${
|
|
11786
|
-
${
|
|
11787
|
-
${
|
|
11788
|
-
|
|
11789
|
-
${
|
|
11790
|
-
${
|
|
11791
|
-
${
|
|
11792
|
-
${
|
|
11793
|
-
${
|
|
11794
|
-
|
|
11795
|
-
${
|
|
11796
|
-
${
|
|
11797
|
-
${
|
|
11798
|
-
${
|
|
11799
|
-
${
|
|
11800
|
-
${
|
|
11801
|
-
${
|
|
11802
|
-
|
|
11803
|
-
${
|
|
11804
|
-
${
|
|
11805
|
-
${
|
|
11806
|
-
|
|
11807
|
-
${
|
|
11808
|
-
${
|
|
11809
|
-
${
|
|
11810
|
-
${
|
|
11811
|
-
|
|
11812
|
-
${
|
|
11813
|
-
${
|
|
11814
|
-
|
|
11815
|
-
${
|
|
11816
|
-
${
|
|
11817
|
-
|
|
11818
|
-
${
|
|
11819
|
-
${
|
|
11820
|
-
|
|
11821
|
-
${
|
|
11822
|
-
${
|
|
11823
|
-
${
|
|
11824
|
-
${
|
|
11825
|
-
|
|
11826
|
-
${
|
|
11827
|
-
${
|
|
11828
|
-
${
|
|
11829
|
-
${
|
|
11830
|
-
|
|
11831
|
-
${
|
|
11832
|
-
${
|
|
11833
|
-
|
|
11834
|
-
${
|
|
11835
|
-
${
|
|
11836
|
-
${
|
|
11837
|
-
|
|
11838
|
-
${
|
|
11839
|
-
${
|
|
11840
|
-
|
|
11841
|
-
${
|
|
11842
|
-
${
|
|
11843
|
-
1. ${
|
|
11844
|
-
2. ${
|
|
11845
|
-
3. ${
|
|
11846
|
-
4. ${
|
|
11847
|
-
|
|
11848
|
-
${
|
|
11849
|
-
1. ${
|
|
11878
|
+
${chalk43.cyan.bold("\u{1F4D6} Jai1 Task Management Guide")}
|
|
11879
|
+
|
|
11880
|
+
${chalk43.bold("\u2501\u2501\u2501 STATUSES \u2501\u2501\u2501")}
|
|
11881
|
+
${chalk43.dim("todo")} \u{1F4CB} Ch\u01B0a b\u1EAFt \u0111\u1EA7u
|
|
11882
|
+
${chalk43.dim("in_progress")} \u{1F535} \u0110ang l\xE0m (bao g\u1ED3m review)
|
|
11883
|
+
${chalk43.dim("done")} \u2705 Ho\xE0n th\xE0nh
|
|
11884
|
+
${chalk43.dim("cancelled")} \u26AB Hu\u1EF7
|
|
11885
|
+
${chalk43.dim("(blocked)")} \u{1F534} Computed: depends_on ch\u01B0a done
|
|
11886
|
+
|
|
11887
|
+
${chalk43.bold("\u2501\u2501\u2501 PRIORITY \u2501\u2501\u2501")}
|
|
11888
|
+
${chalk43.dim("0")} = \u{1F525} Critical \u2014 Prod down, security, block c\u1EA3 team
|
|
11889
|
+
${chalk43.dim("1")} = \u{1F534} High \u2014 Feature ch\xEDnh, deadline g\u1EA7n
|
|
11890
|
+
${chalk43.dim("2")} = \u{1F7E1} Medium \u2014 B\xECnh th\u01B0\u1EDDng (default)
|
|
11891
|
+
${chalk43.dim("3")} = \u{1F7E2} Low \u2014 Nice-to-have, docs, refactor
|
|
11892
|
+
|
|
11893
|
+
${chalk43.bold("\u2501\u2501\u2501 QUICK START \u2501\u2501\u2501")}
|
|
11894
|
+
${chalk43.cyan("jai1 t add")} "Fix login bug" -p 1 -P bug/login
|
|
11895
|
+
${chalk43.cyan("jai1 t ready")} Show tasks s\u1EB5n s\xE0ng
|
|
11896
|
+
${chalk43.cyan("jai1 t pick")} Claim & start working
|
|
11897
|
+
${chalk43.cyan("jai1 t done")} T-003 Mark complete
|
|
11898
|
+
|
|
11899
|
+
${chalk43.bold("\u2501\u2501\u2501 DAILY WORKFLOW \u2501\u2501\u2501")}
|
|
11900
|
+
${chalk43.cyan("jai1 t sync --pull")} Pull latest tasks
|
|
11901
|
+
${chalk43.cyan("jai1 t summary")} Dashboard t\u1ED5ng quan
|
|
11902
|
+
${chalk43.cyan("jai1 t ready")} Xem tasks s\u1EB5n s\xE0ng
|
|
11903
|
+
${chalk43.cyan("jai1 t pick")} Claim task m\u1EDBi
|
|
11904
|
+
${chalk43.cyan("jai1 t done")} T-xxx Ho\xE0n th\xE0nh task
|
|
11905
|
+
${chalk43.cyan("jai1 t sync --push")} Push l\xEAn git
|
|
11906
|
+
|
|
11907
|
+
${chalk43.bold("\u2501\u2501\u2501 ADDING TASKS \u2501\u2501\u2501")}
|
|
11908
|
+
${chalk43.yellow("\u26A0 Lu\xF4n ki\u1EC3m tra duplicate tr\u01B0\u1EDBc khi add:")}
|
|
11909
|
+
${chalk43.cyan("jai1 t list -P")} feature/xxx
|
|
11910
|
+
|
|
11911
|
+
${chalk43.dim("Add cho feature:")}
|
|
11912
|
+
${chalk43.cyan("jai1 t add")} "Setup DB schema" -p 1 -P feature/xxx
|
|
11913
|
+
${chalk43.cyan("jai1 t add")} "Create API" -p 1 -P feature/xxx
|
|
11914
|
+
${chalk43.cyan("jai1 t add")} "Build UI" -p 2 -P feature/xxx
|
|
11915
|
+
|
|
11916
|
+
${chalk43.dim("Add cho plan:")}
|
|
11917
|
+
${chalk43.cyan("jai1 t add")} "Refactor middleware" -p 2 -P plan/xxx
|
|
11918
|
+
|
|
11919
|
+
${chalk43.dim("Add standalone:")}
|
|
11920
|
+
${chalk43.cyan("jai1 t add")} "Fix README typo" -p 3
|
|
11921
|
+
|
|
11922
|
+
${chalk43.dim("Add bug fix:")}
|
|
11923
|
+
${chalk43.cyan("jai1 t add")} "Fix login redirect" -p 1 -P bug/xxx
|
|
11924
|
+
|
|
11925
|
+
${chalk43.bold("\u2501\u2501\u2501 DEPENDENCY \u2501\u2501\u2501")}
|
|
11926
|
+
${chalk43.dim("Task dependency:")}
|
|
11927
|
+
${chalk43.cyan("jai1 t dep")} T-002 T-001 T-002 ch\u1EDD T-001 done
|
|
11928
|
+
${chalk43.cyan("jai1 t dep")} T-003 T-002 T-003 ch\u1EDD T-002 done
|
|
11929
|
+
|
|
11930
|
+
${chalk43.dim("Feature-level dependency:")}
|
|
11931
|
+
${chalk43.dim("# N\u1EBFu feature/auth ph\u1EE5 thu\u1ED9c feature/user-model:")}
|
|
11932
|
+
${chalk43.cyan("jai1 t add")} "[DEP] Wait for feature/user-model" -p 1 -P feature/auth
|
|
11933
|
+
${chalk43.dim("# R\u1ED3i dep n\xF3 v\u1EDBi tasks cu\u1ED1i c\u1EE7a user-model")}
|
|
11934
|
+
|
|
11935
|
+
${chalk43.dim("View deps:")}
|
|
11936
|
+
${chalk43.cyan("jai1 t show")} T-002 Hi\u1EC7n depends_on
|
|
11937
|
+
|
|
11938
|
+
${chalk43.bold("\u2501\u2501\u2501 TEAM COLLABORATION \u2501\u2501\u2501")}
|
|
11939
|
+
${chalk43.yellow("\u26A0 Assignment ch\u1EC9 qua pick \u2014 kh\xF4ng set th\u1EE7 c\xF4ng.")}
|
|
11940
|
+
${chalk43.dim("Khi b\u1EA1n pick \u2192 team th\u1EA5y task \u0111\xE3 c\xF3 ng\u01B0\u1EDDi nh\u1EADn.")}
|
|
11941
|
+
|
|
11942
|
+
${chalk43.dim("Sync morning:")} ${chalk43.cyan("jai1 t sync --pull")}
|
|
11943
|
+
${chalk43.dim("Sync evening:")} ${chalk43.cyan("jai1 t sync --push")}
|
|
11944
|
+
|
|
11945
|
+
${chalk43.bold("\u2501\u2501\u2501 FOR AI AGENTS (Workflow Integration) \u2501\u2501\u2501")}
|
|
11946
|
+
${chalk43.dim("Khi t\u1EA1o tasks t\u1EEB feature/plan:")}
|
|
11947
|
+
1. ${chalk43.cyan("jai1 t list -P")} <parent> Check existing (tr\xE1nh duplicate)
|
|
11948
|
+
2. ${chalk43.cyan("jai1 t add")} "..." -p <0-3> -P <parent> Add t\u1EEBng task
|
|
11949
|
+
3. ${chalk43.cyan("jai1 t dep")} <child> <parent> Set dependencies
|
|
11950
|
+
4. ${chalk43.cyan("jai1 t done")} <id> Mark complete
|
|
11951
|
+
|
|
11952
|
+
${chalk43.dim("Khi implement task ti\u1EBFp theo:")}
|
|
11953
|
+
1. ${chalk43.cyan("jai1 t pick")} (ho\u1EB7c ${chalk43.cyan("jai1 t ready -P")} <parent>)
|
|
11850
11954
|
2. Implement task
|
|
11851
|
-
3. ${
|
|
11955
|
+
3. ${chalk43.cyan("jai1 t done")} <id>
|
|
11852
11956
|
|
|
11853
|
-
${
|
|
11957
|
+
${chalk43.dim("Status transitions:")}
|
|
11854
11958
|
add \u2192 todo (default)
|
|
11855
11959
|
pick \u2192 in_progress (auto assign)
|
|
11856
11960
|
done \u2192 done
|
|
11857
11961
|
update -s \u2192 any valid status
|
|
11858
11962
|
|
|
11859
|
-
${
|
|
11860
|
-
${
|
|
11861
|
-
${
|
|
11862
|
-
${
|
|
11863
|
-
${
|
|
11864
|
-
${
|
|
11865
|
-
${
|
|
11866
|
-
${
|
|
11867
|
-
${
|
|
11868
|
-
${
|
|
11869
|
-
${
|
|
11870
|
-
${
|
|
11871
|
-
${
|
|
11872
|
-
|
|
11873
|
-
${
|
|
11963
|
+
${chalk43.bold("\u2501\u2501\u2501 ALL COMMANDS \u2501\u2501\u2501")}
|
|
11964
|
+
${chalk43.cyan("jai1 t list")} [-s status] [-P parent] [-j]
|
|
11965
|
+
${chalk43.cyan("jai1 t ready")} [-P parent] [-j]
|
|
11966
|
+
${chalk43.cyan("jai1 t add")} <title> [-p 0-3] [-P parent] [-t tags] [-j]
|
|
11967
|
+
${chalk43.cyan("jai1 t update")} <id> [-s <status>] [-n <notes>] [-j]
|
|
11968
|
+
${chalk43.cyan("jai1 t show")} <id|parent> [-j]
|
|
11969
|
+
${chalk43.cyan("jai1 t pick")} [-j]
|
|
11970
|
+
${chalk43.cyan("jai1 t done")} <id> [-j]
|
|
11971
|
+
${chalk43.cyan("jai1 t dep")} <childId> <parentId> [-j]
|
|
11972
|
+
${chalk43.cyan("jai1 t sync")} [--pull] [--push]
|
|
11973
|
+
${chalk43.cyan("jai1 t summary")} [-j]
|
|
11974
|
+
${chalk43.cyan("jai1 t groups")} [-s status] [-j] ${chalk43.dim("(alias: parents)")}
|
|
11975
|
+
${chalk43.cyan("jai1 t guide")}
|
|
11976
|
+
|
|
11977
|
+
${chalk43.dim("-j / --json available on all commands (except guide, sync)")}
|
|
11874
11978
|
`;
|
|
11875
11979
|
function createTaskGuideCommand() {
|
|
11876
|
-
return new
|
|
11980
|
+
return new Command71("guide").description("Show full task management guide").action(() => {
|
|
11877
11981
|
console.log(GUIDE_TEXT);
|
|
11878
11982
|
});
|
|
11879
11983
|
}
|
|
11880
11984
|
|
|
11881
11985
|
// src/commands/tasks/parents.ts
|
|
11882
|
-
import { Command as
|
|
11883
|
-
import
|
|
11986
|
+
import { Command as Command72 } from "commander";
|
|
11987
|
+
import chalk44 from "chalk";
|
|
11884
11988
|
var PARENT_STATUS_ICONS = {
|
|
11885
11989
|
done: "\u2705",
|
|
11886
11990
|
in_progress: "\u{1F535}",
|
|
@@ -11890,17 +11994,17 @@ var PARENT_STATUS_ICONS = {
|
|
|
11890
11994
|
function formatProgress(p) {
|
|
11891
11995
|
const completed = p.done + p.cancelled;
|
|
11892
11996
|
if (p.status === "done") {
|
|
11893
|
-
return
|
|
11997
|
+
return chalk44.green(`${completed}/${p.total} done`);
|
|
11894
11998
|
}
|
|
11895
11999
|
const parts = [];
|
|
11896
12000
|
if (p.in_progress > 0) parts.push(`${p.in_progress} in_progress`);
|
|
11897
12001
|
if (p.ready > 0) parts.push(`${p.ready} ready`);
|
|
11898
|
-
if (p.blocked > 0) parts.push(
|
|
12002
|
+
if (p.blocked > 0) parts.push(chalk44.red(`${p.blocked} blocked`));
|
|
11899
12003
|
if (p.done > 0) parts.push(`${p.done} done`);
|
|
11900
12004
|
return `${completed}/${p.total} tasks` + (parts.length > 0 ? ` (${parts.join(", ")})` : "");
|
|
11901
12005
|
}
|
|
11902
12006
|
function createTaskParentsCommand() {
|
|
11903
|
-
return new
|
|
12007
|
+
return new Command72("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) => {
|
|
11904
12008
|
const service = new TaskService();
|
|
11905
12009
|
const parents = await service.getParents(options.status);
|
|
11906
12010
|
if (options.json) {
|
|
@@ -11909,44 +12013,44 @@ function createTaskParentsCommand() {
|
|
|
11909
12013
|
}
|
|
11910
12014
|
if (parents.length === 0) {
|
|
11911
12015
|
if (options.status) {
|
|
11912
|
-
console.log(
|
|
12016
|
+
console.log(chalk44.dim(`No groups with status: ${options.status}`));
|
|
11913
12017
|
} else {
|
|
11914
|
-
console.log(
|
|
11915
|
-
console.log(
|
|
12018
|
+
console.log(chalk44.dim("No task groups found."));
|
|
12019
|
+
console.log(chalk44.dim('\u{1F4A1} Add tasks with parent: jai1 t add "..." -P feature/xxx'));
|
|
11916
12020
|
}
|
|
11917
12021
|
return;
|
|
11918
12022
|
}
|
|
11919
12023
|
const header = options.status ? `\u{1F4E6} Task Groups \u2014 ${options.status} (${parents.length})` : `\u{1F4E6} Task Groups (${parents.length})`;
|
|
11920
|
-
console.log(
|
|
12024
|
+
console.log(chalk44.bold(header));
|
|
11921
12025
|
console.log();
|
|
11922
12026
|
for (const p of parents) {
|
|
11923
12027
|
const icon = PARENT_STATUS_ICONS[p.status] || "\u{1F4CB}";
|
|
11924
12028
|
const progress = formatProgress(p);
|
|
11925
|
-
console.log(` ${icon} ${
|
|
12029
|
+
console.log(` ${icon} ${chalk44.bold(p.name)} ${chalk44.dim("\u2014")} ${progress}`);
|
|
11926
12030
|
}
|
|
11927
12031
|
console.log();
|
|
11928
12032
|
});
|
|
11929
12033
|
}
|
|
11930
12034
|
|
|
11931
12035
|
// src/commands/tasks/cancel.ts
|
|
11932
|
-
import { Command as
|
|
11933
|
-
import
|
|
12036
|
+
import { Command as Command73 } from "commander";
|
|
12037
|
+
import chalk45 from "chalk";
|
|
11934
12038
|
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
11935
12039
|
function createTaskCancelCommand() {
|
|
11936
|
-
return new
|
|
12040
|
+
return new Command73("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) => {
|
|
11937
12041
|
const service = new TaskService();
|
|
11938
12042
|
try {
|
|
11939
12043
|
const task = await service.findById(id);
|
|
11940
12044
|
if (!task) {
|
|
11941
|
-
console.error(
|
|
12045
|
+
console.error(chalk45.red(`\u274C Task ${id} not found`));
|
|
11942
12046
|
process.exit(1);
|
|
11943
12047
|
}
|
|
11944
12048
|
if (task.status === "cancelled") {
|
|
11945
|
-
console.error(
|
|
12049
|
+
console.error(chalk45.yellow(`\u26A0\uFE0F Task ${id} is already cancelled`));
|
|
11946
12050
|
process.exit(0);
|
|
11947
12051
|
}
|
|
11948
12052
|
if (task.status === "done") {
|
|
11949
|
-
console.error(
|
|
12053
|
+
console.error(chalk45.red(`\u274C Task ${id} is already done \u2014 cannot cancel`));
|
|
11950
12054
|
process.exit(1);
|
|
11951
12055
|
}
|
|
11952
12056
|
const dependents = await service.getDependents(id);
|
|
@@ -11954,17 +12058,17 @@ function createTaskCancelCommand() {
|
|
|
11954
12058
|
(t) => t.status !== "done" && t.status !== "cancelled"
|
|
11955
12059
|
);
|
|
11956
12060
|
const icon = STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11957
|
-
console.log(
|
|
12061
|
+
console.log(chalk45.bold(`
|
|
11958
12062
|
\u26AB Cancel: ${icon} ${task.id} \u2014 ${task.title}
|
|
11959
12063
|
`));
|
|
11960
12064
|
if (activeDependents.length > 0) {
|
|
11961
|
-
console.log(
|
|
12065
|
+
console.log(chalk45.yellow(`\u26A0\uFE0F ${activeDependents.length} task(s) ph\u1EE5 thu\u1ED9c v\xE0o ${id}:`));
|
|
11962
12066
|
for (const dep of activeDependents) {
|
|
11963
12067
|
const depIcon = STATUS_ICONS[dep.status] || "\u{1F4CB}";
|
|
11964
|
-
console.log(` ${depIcon} ${
|
|
12068
|
+
console.log(` ${depIcon} ${chalk45.dim(dep.id)} ${dep.title}`);
|
|
11965
12069
|
}
|
|
11966
12070
|
console.log(
|
|
11967
|
-
|
|
12071
|
+
chalk45.dim(
|
|
11968
12072
|
`
|
|
11969
12073
|
\u2192 Cancel s\u1EBD resolve dependency, c\xE1c task tr\xEAn c\xF3 th\u1EC3 \u0111\u01B0\u1EE3c unblock.`
|
|
11970
12074
|
)
|
|
@@ -11977,7 +12081,7 @@ function createTaskCancelCommand() {
|
|
|
11977
12081
|
default: false
|
|
11978
12082
|
});
|
|
11979
12083
|
if (!proceed) {
|
|
11980
|
-
console.log(
|
|
12084
|
+
console.log(chalk45.dim("\u0110\xE3 hu\u1EF7."));
|
|
11981
12085
|
return;
|
|
11982
12086
|
}
|
|
11983
12087
|
}
|
|
@@ -11986,10 +12090,10 @@ function createTaskCancelCommand() {
|
|
|
11986
12090
|
console.log(JSON.stringify(updated, null, 2));
|
|
11987
12091
|
return;
|
|
11988
12092
|
}
|
|
11989
|
-
console.log(
|
|
12093
|
+
console.log(chalk45.green(`\u2705 ${updated.id}: ${updated.title} \u2192 cancelled`));
|
|
11990
12094
|
} catch (error) {
|
|
11991
12095
|
console.error(
|
|
11992
|
-
|
|
12096
|
+
chalk45.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
11993
12097
|
);
|
|
11994
12098
|
process.exit(1);
|
|
11995
12099
|
}
|
|
@@ -11997,13 +12101,13 @@ function createTaskCancelCommand() {
|
|
|
11997
12101
|
}
|
|
11998
12102
|
|
|
11999
12103
|
// src/commands/tasks/delete.ts
|
|
12000
|
-
import { Command as
|
|
12001
|
-
import
|
|
12104
|
+
import { Command as Command74 } from "commander";
|
|
12105
|
+
import chalk46 from "chalk";
|
|
12002
12106
|
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
12003
12107
|
function printTaskList(tasks, indent = " ") {
|
|
12004
12108
|
for (const t of tasks) {
|
|
12005
12109
|
const icon = STATUS_ICONS[t.status] || "\u{1F4CB}";
|
|
12006
|
-
console.log(`${indent}${icon} ${
|
|
12110
|
+
console.log(`${indent}${icon} ${chalk46.dim(t.id)} ${t.title}`);
|
|
12007
12111
|
}
|
|
12008
12112
|
}
|
|
12009
12113
|
function printImpactSummary(tasksToDelete, allDependents) {
|
|
@@ -12011,21 +12115,21 @@ function printImpactSummary(tasksToDelete, allDependents) {
|
|
|
12011
12115
|
const todo = tasksToDelete.filter((t) => t.status === "todo");
|
|
12012
12116
|
const done = tasksToDelete.filter((t) => t.status === "done");
|
|
12013
12117
|
const cancelled = tasksToDelete.filter((t) => t.status === "cancelled");
|
|
12014
|
-
console.log(
|
|
12118
|
+
console.log(chalk46.bold(`
|
|
12015
12119
|
\u{1F5D1}\uFE0F Delete: ${tasksToDelete.length} task(s)
|
|
12016
12120
|
`));
|
|
12017
12121
|
if (inProgress.length > 0) {
|
|
12018
|
-
console.log(
|
|
12122
|
+
console.log(chalk46.yellow.bold(` \u26A0\uFE0F ${inProgress.length} task(s) \u0111ang IN_PROGRESS:`));
|
|
12019
12123
|
printTaskList(inProgress, " ");
|
|
12020
12124
|
console.log();
|
|
12021
12125
|
}
|
|
12022
12126
|
const parts = [];
|
|
12023
12127
|
if (todo.length > 0) parts.push(`${todo.length} todo`);
|
|
12024
|
-
if (inProgress.length > 0) parts.push(
|
|
12128
|
+
if (inProgress.length > 0) parts.push(chalk46.yellow(`${inProgress.length} in_progress`));
|
|
12025
12129
|
if (done.length > 0) parts.push(`${done.length} done`);
|
|
12026
12130
|
if (cancelled.length > 0) parts.push(`${cancelled.length} cancelled`);
|
|
12027
|
-
console.log(` ${
|
|
12028
|
-
console.log(` ${
|
|
12131
|
+
console.log(` ${chalk46.dim("Breakdown:")} ${parts.join(", ")}`);
|
|
12132
|
+
console.log(` ${chalk46.dim("Tasks:")}`);
|
|
12029
12133
|
printTaskList(tasksToDelete, " ");
|
|
12030
12134
|
console.log();
|
|
12031
12135
|
if (allDependents.length > 0) {
|
|
@@ -12033,12 +12137,12 @@ function printImpactSummary(tasksToDelete, allDependents) {
|
|
|
12033
12137
|
(t) => t.status !== "done" && t.status !== "cancelled"
|
|
12034
12138
|
);
|
|
12035
12139
|
console.log(
|
|
12036
|
-
|
|
12140
|
+
chalk46.yellow(` \u26A0\uFE0F ${allDependents.length} task(s) b\xEAn ngo\xE0i c\xF3 depends_on s\u1EBD b\u1ECB \u1EA3nh h\u01B0\u1EDFng:`)
|
|
12037
12141
|
);
|
|
12038
12142
|
printTaskList(allDependents, " ");
|
|
12039
12143
|
if (activeDependents.length > 0) {
|
|
12040
12144
|
console.log(
|
|
12041
|
-
|
|
12145
|
+
chalk46.dim(`
|
|
12042
12146
|
\u2192 ${activeDependents.length} task(s) ch\u01B0a done c\xF3 th\u1EC3 b\u1ECB unblock.`)
|
|
12043
12147
|
);
|
|
12044
12148
|
}
|
|
@@ -12046,7 +12150,7 @@ function printImpactSummary(tasksToDelete, allDependents) {
|
|
|
12046
12150
|
}
|
|
12047
12151
|
}
|
|
12048
12152
|
function createTaskDeleteCommand() {
|
|
12049
|
-
return new
|
|
12153
|
+
return new Command74("delete").description("Delete task(s) or entire group permanently (with safe delete)").argument("[ids...]", "Task ID(s) (e.g. T-001 T-002 T-003)").option("-g, --group <name>", "Delete all tasks in a group (e.g. feature/auth)").option("-y, --yes", "Skip confirmation").option("-j, --json", "Output JSON").action(async (ids, options) => {
|
|
12050
12154
|
const service = new TaskService();
|
|
12051
12155
|
try {
|
|
12052
12156
|
let tasksToDelete;
|
|
@@ -12054,23 +12158,23 @@ function createTaskDeleteCommand() {
|
|
|
12054
12158
|
const allTasks = await service.readAll();
|
|
12055
12159
|
tasksToDelete = allTasks.filter((t) => t.parent === options.group);
|
|
12056
12160
|
if (tasksToDelete.length === 0) {
|
|
12057
|
-
console.error(
|
|
12161
|
+
console.error(chalk46.red(`\u274C No tasks found in group: ${options.group}`));
|
|
12058
12162
|
process.exit(1);
|
|
12059
12163
|
}
|
|
12060
12164
|
} else {
|
|
12061
12165
|
if (!ids || ids.length === 0) {
|
|
12062
|
-
console.error(
|
|
12063
|
-
console.error(
|
|
12064
|
-
console.error(
|
|
12065
|
-
console.error(
|
|
12066
|
-
console.error(
|
|
12166
|
+
console.error(chalk46.red("\u274C C\u1EA7n ch\u1EC9 \u0111\u1ECBnh task ID(s) ho\u1EB7c --group <name>"));
|
|
12167
|
+
console.error(chalk46.dim("\nV\xED d\u1EE5:"));
|
|
12168
|
+
console.error(chalk46.dim(" j t delete T-001"));
|
|
12169
|
+
console.error(chalk46.dim(" j t delete T-001 T-002 T-003"));
|
|
12170
|
+
console.error(chalk46.dim(" j t delete --group feature/auth"));
|
|
12067
12171
|
process.exit(1);
|
|
12068
12172
|
}
|
|
12069
12173
|
tasksToDelete = [];
|
|
12070
12174
|
for (const id of ids) {
|
|
12071
12175
|
const task = await service.findById(id);
|
|
12072
12176
|
if (!task) {
|
|
12073
|
-
console.error(
|
|
12177
|
+
console.error(chalk46.red(`\u274C Task ${id} not found`));
|
|
12074
12178
|
process.exit(1);
|
|
12075
12179
|
}
|
|
12076
12180
|
tasksToDelete.push(task);
|
|
@@ -12089,7 +12193,7 @@ function createTaskDeleteCommand() {
|
|
|
12089
12193
|
}
|
|
12090
12194
|
}
|
|
12091
12195
|
if (options.group) {
|
|
12092
|
-
console.log(
|
|
12196
|
+
console.log(chalk46.bold.cyan(`
|
|
12093
12197
|
\u{1F4E6} Group: ${options.group}`));
|
|
12094
12198
|
}
|
|
12095
12199
|
printImpactSummary(tasksToDelete, allDependents);
|
|
@@ -12100,7 +12204,7 @@ function createTaskDeleteCommand() {
|
|
|
12100
12204
|
default: false
|
|
12101
12205
|
});
|
|
12102
12206
|
if (!proceed) {
|
|
12103
|
-
console.log(
|
|
12207
|
+
console.log(chalk46.dim("\u0110\xE3 hu\u1EF7."));
|
|
12104
12208
|
return;
|
|
12105
12209
|
}
|
|
12106
12210
|
}
|
|
@@ -12115,15 +12219,15 @@ function createTaskDeleteCommand() {
|
|
|
12115
12219
|
await service.deleteTasks(idsToDelete);
|
|
12116
12220
|
if (!options.json) {
|
|
12117
12221
|
if (options.group) {
|
|
12118
|
-
console.log(
|
|
12222
|
+
console.log(chalk46.green(`\u2705 \u0110\xE3 xo\xE1 group "${options.group}" (${tasksToDelete.length} tasks)`));
|
|
12119
12223
|
} else if (tasksToDelete.length === 1) {
|
|
12120
|
-
console.log(
|
|
12224
|
+
console.log(chalk46.green(`\u2705 \u0110\xE3 xo\xE1 ${tasksToDelete[0].id}: ${tasksToDelete[0].title}`));
|
|
12121
12225
|
} else {
|
|
12122
|
-
console.log(
|
|
12226
|
+
console.log(chalk46.green(`\u2705 \u0110\xE3 xo\xE1 ${tasksToDelete.length} task(s): ${idsToDelete.join(", ")}`));
|
|
12123
12227
|
}
|
|
12124
12228
|
if (allDependents.length > 0) {
|
|
12125
12229
|
console.log(
|
|
12126
|
-
|
|
12230
|
+
chalk46.dim(
|
|
12127
12231
|
` \u0110\xE3 c\u1EADp nh\u1EADt depends_on cho: ${allDependents.map((d) => d.id).join(", ")}`
|
|
12128
12232
|
)
|
|
12129
12233
|
);
|
|
@@ -12131,7 +12235,7 @@ function createTaskDeleteCommand() {
|
|
|
12131
12235
|
}
|
|
12132
12236
|
} catch (error) {
|
|
12133
12237
|
console.error(
|
|
12134
|
-
|
|
12238
|
+
chalk46.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
12135
12239
|
);
|
|
12136
12240
|
process.exit(1);
|
|
12137
12241
|
}
|
|
@@ -12140,7 +12244,7 @@ function createTaskDeleteCommand() {
|
|
|
12140
12244
|
|
|
12141
12245
|
// src/commands/tasks/index.ts
|
|
12142
12246
|
function createTasksCommand() {
|
|
12143
|
-
const cmd = new
|
|
12247
|
+
const cmd = new Command75("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
|
|
12144
12248
|
if (actionCommand.name() !== "guide") {
|
|
12145
12249
|
TaskService.ensureJai1Dir();
|
|
12146
12250
|
}
|
|
@@ -12160,24 +12264,24 @@ function createTasksCommand() {
|
|
|
12160
12264
|
cmd.addCommand(createTaskDeleteCommand());
|
|
12161
12265
|
cmd.addCommand(createTaskGuideCommand());
|
|
12162
12266
|
cmd.action(async () => {
|
|
12163
|
-
const { handleTaskSummary } = await import("./summary-
|
|
12267
|
+
const { handleTaskSummary } = await import("./summary-R4WPFJ2U.js");
|
|
12164
12268
|
await handleTaskSummary({ json: false });
|
|
12165
12269
|
});
|
|
12166
12270
|
return cmd;
|
|
12167
12271
|
}
|
|
12168
12272
|
|
|
12169
12273
|
// src/commands/kit/index.ts
|
|
12170
|
-
import { Command as
|
|
12171
|
-
import
|
|
12274
|
+
import { Command as Command79 } from "commander";
|
|
12275
|
+
import chalk48 from "chalk";
|
|
12172
12276
|
|
|
12173
12277
|
// src/commands/kit/list.ts
|
|
12174
|
-
import { Command as
|
|
12175
|
-
import
|
|
12278
|
+
import { Command as Command76 } from "commander";
|
|
12279
|
+
import chalk47 from "chalk";
|
|
12176
12280
|
import Table6 from "cli-table3";
|
|
12177
12281
|
|
|
12178
12282
|
// src/services/starter-kit.service.ts
|
|
12179
|
-
import { promises as
|
|
12180
|
-
import { join as
|
|
12283
|
+
import { promises as fs18 } from "fs";
|
|
12284
|
+
import { join as join9 } from "path";
|
|
12181
12285
|
import AdmZip from "adm-zip";
|
|
12182
12286
|
var StarterKitService = class {
|
|
12183
12287
|
/**
|
|
@@ -12224,29 +12328,29 @@ var StarterKitService = class {
|
|
|
12224
12328
|
throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
|
|
12225
12329
|
}
|
|
12226
12330
|
if (onProgress) onProgress(30);
|
|
12227
|
-
const tmpDir =
|
|
12228
|
-
await
|
|
12229
|
-
const tmpFile =
|
|
12331
|
+
const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
|
|
12332
|
+
await fs18.mkdir(tmpDir, { recursive: true });
|
|
12333
|
+
const tmpFile = join9(tmpDir, `${slug}.zip`);
|
|
12230
12334
|
const buffer = await response.arrayBuffer();
|
|
12231
|
-
await
|
|
12335
|
+
await fs18.writeFile(tmpFile, Buffer.from(buffer));
|
|
12232
12336
|
if (onProgress) onProgress(60);
|
|
12233
12337
|
const zip = new AdmZip(tmpFile);
|
|
12234
|
-
await
|
|
12338
|
+
await fs18.mkdir(targetDir, { recursive: true });
|
|
12235
12339
|
zip.extractAllTo(targetDir, true);
|
|
12236
12340
|
if (onProgress) onProgress(100);
|
|
12237
|
-
await
|
|
12341
|
+
await fs18.unlink(tmpFile);
|
|
12238
12342
|
}
|
|
12239
12343
|
};
|
|
12240
12344
|
|
|
12241
12345
|
// src/commands/kit/list.ts
|
|
12242
12346
|
function createKitListCommand() {
|
|
12243
|
-
return new
|
|
12347
|
+
return new Command76("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) => {
|
|
12244
12348
|
const configService = new ConfigService();
|
|
12245
12349
|
const config = await configService.load();
|
|
12246
12350
|
if (!config) {
|
|
12247
12351
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
12248
12352
|
}
|
|
12249
|
-
console.log(
|
|
12353
|
+
console.log(chalk47.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
|
|
12250
12354
|
console.log();
|
|
12251
12355
|
const kitService = new StarterKitService();
|
|
12252
12356
|
const kits = await kitService.list(config, {
|
|
@@ -12254,9 +12358,9 @@ function createKitListCommand() {
|
|
|
12254
12358
|
search: options.search
|
|
12255
12359
|
});
|
|
12256
12360
|
if (kits.length === 0) {
|
|
12257
|
-
console.log(
|
|
12361
|
+
console.log(chalk47.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
|
|
12258
12362
|
if (options.category || options.search) {
|
|
12259
|
-
console.log(
|
|
12363
|
+
console.log(chalk47.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
|
|
12260
12364
|
}
|
|
12261
12365
|
return;
|
|
12262
12366
|
}
|
|
@@ -12280,35 +12384,35 @@ function createKitListCommand() {
|
|
|
12280
12384
|
const categoryKits = byCategory[category];
|
|
12281
12385
|
const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
|
|
12282
12386
|
console.log(
|
|
12283
|
-
|
|
12387
|
+
chalk47.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
12284
12388
|
);
|
|
12285
12389
|
const table = new Table6({
|
|
12286
12390
|
head: [
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12391
|
+
chalk47.cyan("Slug"),
|
|
12392
|
+
chalk47.cyan("M\xF4 t\u1EA3"),
|
|
12393
|
+
chalk47.cyan("Version")
|
|
12290
12394
|
],
|
|
12291
12395
|
style: { head: [], border: ["gray"] }
|
|
12292
12396
|
});
|
|
12293
12397
|
for (const kit of categoryKits) {
|
|
12294
12398
|
table.push([
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12399
|
+
chalk47.white(kit.slug),
|
|
12400
|
+
chalk47.dim(kit.description.slice(0, 50)),
|
|
12401
|
+
chalk47.green(`v${kit.version}`)
|
|
12298
12402
|
]);
|
|
12299
12403
|
}
|
|
12300
12404
|
console.log(table.toString());
|
|
12301
12405
|
console.log();
|
|
12302
12406
|
}
|
|
12303
|
-
console.log(
|
|
12304
|
-
console.log(
|
|
12407
|
+
console.log(chalk47.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
|
|
12408
|
+
console.log(chalk47.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
|
|
12305
12409
|
});
|
|
12306
12410
|
}
|
|
12307
12411
|
|
|
12308
12412
|
// src/commands/kit/info.ts
|
|
12309
|
-
import { Command as
|
|
12413
|
+
import { Command as Command77 } from "commander";
|
|
12310
12414
|
function createKitInfoCommand() {
|
|
12311
|
-
return new
|
|
12415
|
+
return new Command77("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
|
|
12312
12416
|
const configService = new ConfigService();
|
|
12313
12417
|
const config = await configService.load();
|
|
12314
12418
|
if (!config) {
|
|
@@ -12357,9 +12461,9 @@ Post-Init Commands:`);
|
|
|
12357
12461
|
}
|
|
12358
12462
|
|
|
12359
12463
|
// src/commands/kit/create.ts
|
|
12360
|
-
import { Command as
|
|
12361
|
-
import { promises as
|
|
12362
|
-
import { join as
|
|
12464
|
+
import { Command as Command78 } from "commander";
|
|
12465
|
+
import { promises as fs19 } from "fs";
|
|
12466
|
+
import { join as join10 } from "path";
|
|
12363
12467
|
import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
12364
12468
|
import { execa as execa2 } from "execa";
|
|
12365
12469
|
|
|
@@ -12402,7 +12506,7 @@ var HookExecutor = class {
|
|
|
12402
12506
|
|
|
12403
12507
|
// src/commands/kit/create.ts
|
|
12404
12508
|
function createKitCreateCommand() {
|
|
12405
|
-
return new
|
|
12509
|
+
return new Command78("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) => {
|
|
12406
12510
|
const configService = new ConfigService();
|
|
12407
12511
|
const config = await configService.load();
|
|
12408
12512
|
if (!config) {
|
|
@@ -12422,9 +12526,9 @@ function createKitCreateCommand() {
|
|
|
12422
12526
|
}
|
|
12423
12527
|
}
|
|
12424
12528
|
}
|
|
12425
|
-
const targetDir = directory ||
|
|
12529
|
+
const targetDir = directory || join10(process.cwd(), options.name || slug);
|
|
12426
12530
|
try {
|
|
12427
|
-
await
|
|
12531
|
+
await fs19.access(targetDir);
|
|
12428
12532
|
throw new Error(`Directory already exists: ${targetDir}`);
|
|
12429
12533
|
} catch (error) {
|
|
12430
12534
|
if (error.code !== "ENOENT") {
|
|
@@ -12487,7 +12591,7 @@ function createKitCreateCommand() {
|
|
|
12487
12591
|
);
|
|
12488
12592
|
for (const filepath of expandedPaths) {
|
|
12489
12593
|
console.log(` \u{1F4E5} Installing ${filepath}...`);
|
|
12490
|
-
await componentsService.install(config, filepath,
|
|
12594
|
+
await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
|
|
12491
12595
|
}
|
|
12492
12596
|
console.log(" \u2713 Framework components applied");
|
|
12493
12597
|
}
|
|
@@ -12549,7 +12653,7 @@ function createKitCreateCommand() {
|
|
|
12549
12653
|
async function applyVariableSubstitution(dir, variables) {
|
|
12550
12654
|
const files = await getAllFiles(dir);
|
|
12551
12655
|
for (const file of files) {
|
|
12552
|
-
let content = await
|
|
12656
|
+
let content = await fs19.readFile(file, "utf-8");
|
|
12553
12657
|
let modified = false;
|
|
12554
12658
|
for (const [key, value] of Object.entries(variables)) {
|
|
12555
12659
|
const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
@@ -12559,15 +12663,15 @@ async function applyVariableSubstitution(dir, variables) {
|
|
|
12559
12663
|
}
|
|
12560
12664
|
}
|
|
12561
12665
|
if (modified) {
|
|
12562
|
-
await
|
|
12666
|
+
await fs19.writeFile(file, content, "utf-8");
|
|
12563
12667
|
}
|
|
12564
12668
|
}
|
|
12565
12669
|
}
|
|
12566
12670
|
async function getAllFiles(dir) {
|
|
12567
12671
|
const files = [];
|
|
12568
|
-
const entries = await
|
|
12672
|
+
const entries = await fs19.readdir(dir, { withFileTypes: true });
|
|
12569
12673
|
for (const entry of entries) {
|
|
12570
|
-
const fullPath =
|
|
12674
|
+
const fullPath = join10(dir, entry.name);
|
|
12571
12675
|
if (entry.isDirectory()) {
|
|
12572
12676
|
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
12573
12677
|
files.push(...await getAllFiles(fullPath));
|
|
@@ -12581,23 +12685,23 @@ async function getAllFiles(dir) {
|
|
|
12581
12685
|
|
|
12582
12686
|
// src/commands/kit/index.ts
|
|
12583
12687
|
function showKitHelp() {
|
|
12584
|
-
console.log(
|
|
12688
|
+
console.log(chalk48.bold.cyan("\u{1F4E6} jai1 kit") + chalk48.dim(" - Qu\u1EA3n l\xFD starter kits"));
|
|
12585
12689
|
console.log();
|
|
12586
|
-
console.log(
|
|
12587
|
-
console.log(` ${
|
|
12588
|
-
console.log(` ${
|
|
12589
|
-
console.log(` ${
|
|
12690
|
+
console.log(chalk48.bold("C\xE1c l\u1EC7nh:"));
|
|
12691
|
+
console.log(` ${chalk48.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
|
|
12692
|
+
console.log(` ${chalk48.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
|
|
12693
|
+
console.log(` ${chalk48.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
|
|
12590
12694
|
console.log();
|
|
12591
|
-
console.log(
|
|
12592
|
-
console.log(
|
|
12593
|
-
console.log(
|
|
12594
|
-
console.log(
|
|
12595
|
-
console.log(
|
|
12695
|
+
console.log(chalk48.bold("V\xED d\u1EE5:"));
|
|
12696
|
+
console.log(chalk48.dim(" $ jai1 kit list"));
|
|
12697
|
+
console.log(chalk48.dim(" $ jai1 kit list --category frontend"));
|
|
12698
|
+
console.log(chalk48.dim(" $ jai1 kit info next-tw4-shadcn"));
|
|
12699
|
+
console.log(chalk48.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
|
|
12596
12700
|
console.log();
|
|
12597
|
-
console.log(
|
|
12701
|
+
console.log(chalk48.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
12598
12702
|
}
|
|
12599
12703
|
function createKitCommand() {
|
|
12600
|
-
const cmd = new
|
|
12704
|
+
const cmd = new Command79("kit").description("Manage starter kits for new projects").action(() => {
|
|
12601
12705
|
showKitHelp();
|
|
12602
12706
|
});
|
|
12603
12707
|
cmd.addCommand(createKitListCommand());
|
|
@@ -12607,21 +12711,21 @@ function createKitCommand() {
|
|
|
12607
12711
|
}
|
|
12608
12712
|
|
|
12609
12713
|
// src/commands/rules/index.ts
|
|
12610
|
-
import { Command as
|
|
12611
|
-
import
|
|
12714
|
+
import { Command as Command86 } from "commander";
|
|
12715
|
+
import chalk50 from "chalk";
|
|
12612
12716
|
|
|
12613
12717
|
// src/commands/rules/list.ts
|
|
12614
|
-
import { Command as
|
|
12615
|
-
import
|
|
12718
|
+
import { Command as Command80 } from "commander";
|
|
12719
|
+
import chalk49 from "chalk";
|
|
12616
12720
|
import Table7 from "cli-table3";
|
|
12617
12721
|
function createRulesListCommand() {
|
|
12618
|
-
return new
|
|
12722
|
+
return new Command80("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
|
|
12619
12723
|
const configService = new ConfigService();
|
|
12620
12724
|
const config = await configService.load();
|
|
12621
12725
|
if (!config) {
|
|
12622
12726
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
12623
12727
|
}
|
|
12624
|
-
console.log(
|
|
12728
|
+
console.log(chalk49.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
|
|
12625
12729
|
console.log();
|
|
12626
12730
|
try {
|
|
12627
12731
|
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
@@ -12638,23 +12742,23 @@ function createRulesListCommand() {
|
|
|
12638
12742
|
return;
|
|
12639
12743
|
}
|
|
12640
12744
|
if (data.total === 0) {
|
|
12641
|
-
console.log(
|
|
12745
|
+
console.log(chalk49.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
|
|
12642
12746
|
return;
|
|
12643
12747
|
}
|
|
12644
12748
|
console.log(
|
|
12645
|
-
|
|
12749
|
+
chalk49.green(`\u2713 T\xECm th\u1EA5y ${chalk49.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
|
|
12646
12750
|
);
|
|
12647
12751
|
console.log();
|
|
12648
12752
|
for (const preset of data.presets) {
|
|
12649
|
-
console.log(
|
|
12753
|
+
console.log(chalk49.bold.cyan(`\u{1F4E6} ${preset.slug}`));
|
|
12650
12754
|
const table = new Table7({
|
|
12651
12755
|
style: { head: [], border: ["gray"], compact: true },
|
|
12652
12756
|
colWidths: [15, 55]
|
|
12653
12757
|
});
|
|
12654
12758
|
table.push(
|
|
12655
|
-
[
|
|
12656
|
-
[
|
|
12657
|
-
[
|
|
12759
|
+
[chalk49.dim("T\xEAn"), chalk49.white(preset.name)],
|
|
12760
|
+
[chalk49.dim("M\xF4 t\u1EA3"), chalk49.white(preset.description)],
|
|
12761
|
+
[chalk49.dim("Version"), chalk49.green(`v${preset.version}`)]
|
|
12658
12762
|
);
|
|
12659
12763
|
const stackParts = [];
|
|
12660
12764
|
if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
|
|
@@ -12662,16 +12766,16 @@ function createRulesListCommand() {
|
|
|
12662
12766
|
if (preset.stack.css) stackParts.push(preset.stack.css);
|
|
12663
12767
|
if (preset.stack.database) stackParts.push(preset.stack.database);
|
|
12664
12768
|
if (stackParts.length > 0) {
|
|
12665
|
-
table.push([
|
|
12769
|
+
table.push([chalk49.dim("Stack"), chalk49.yellow(stackParts.join(" + "))]);
|
|
12666
12770
|
}
|
|
12667
12771
|
table.push(
|
|
12668
|
-
[
|
|
12669
|
-
[
|
|
12772
|
+
[chalk49.dim("Tags"), chalk49.dim(preset.tags.join(", ") || "-")],
|
|
12773
|
+
[chalk49.dim("Downloads"), chalk49.white(preset.downloads.toString())]
|
|
12670
12774
|
);
|
|
12671
12775
|
console.log(table.toString());
|
|
12672
12776
|
console.log();
|
|
12673
12777
|
}
|
|
12674
|
-
console.log(
|
|
12778
|
+
console.log(chalk49.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
|
|
12675
12779
|
} catch (error) {
|
|
12676
12780
|
throw new Error(
|
|
12677
12781
|
`L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -12681,29 +12785,29 @@ function createRulesListCommand() {
|
|
|
12681
12785
|
}
|
|
12682
12786
|
|
|
12683
12787
|
// src/commands/rules/init.ts
|
|
12684
|
-
import { Command as
|
|
12685
|
-
import { promises as
|
|
12686
|
-
import { join as
|
|
12788
|
+
import { Command as Command81 } from "commander";
|
|
12789
|
+
import { promises as fs21 } from "fs";
|
|
12790
|
+
import { join as join12 } from "path";
|
|
12687
12791
|
import { select as select4, confirm as confirm14 } from "@inquirer/prompts";
|
|
12688
12792
|
|
|
12689
12793
|
// src/services/project-config.service.ts
|
|
12690
|
-
import { promises as
|
|
12691
|
-
import { join as
|
|
12794
|
+
import { promises as fs20 } from "fs";
|
|
12795
|
+
import { join as join11 } from "path";
|
|
12692
12796
|
var ProjectConfigService = class {
|
|
12693
12797
|
projectRoot;
|
|
12694
12798
|
configDir;
|
|
12695
12799
|
configPath;
|
|
12696
12800
|
constructor(projectRoot = process.cwd()) {
|
|
12697
12801
|
this.projectRoot = projectRoot;
|
|
12698
|
-
this.configDir =
|
|
12699
|
-
this.configPath =
|
|
12802
|
+
this.configDir = join11(this.projectRoot, ".jai1");
|
|
12803
|
+
this.configPath = join11(this.configDir, "project.json");
|
|
12700
12804
|
}
|
|
12701
12805
|
/**
|
|
12702
12806
|
* Check if config file exists
|
|
12703
12807
|
*/
|
|
12704
12808
|
async exists() {
|
|
12705
12809
|
try {
|
|
12706
|
-
await
|
|
12810
|
+
await fs20.access(this.configPath);
|
|
12707
12811
|
return true;
|
|
12708
12812
|
} catch {
|
|
12709
12813
|
return false;
|
|
@@ -12718,7 +12822,7 @@ var ProjectConfigService = class {
|
|
|
12718
12822
|
return null;
|
|
12719
12823
|
}
|
|
12720
12824
|
try {
|
|
12721
|
-
const content = await
|
|
12825
|
+
const content = await fs20.readFile(this.configPath, "utf-8");
|
|
12722
12826
|
return JSON.parse(content);
|
|
12723
12827
|
} catch (error) {
|
|
12724
12828
|
throw new Error(
|
|
@@ -12732,8 +12836,8 @@ var ProjectConfigService = class {
|
|
|
12732
12836
|
*/
|
|
12733
12837
|
async save(config) {
|
|
12734
12838
|
try {
|
|
12735
|
-
await
|
|
12736
|
-
await
|
|
12839
|
+
await fs20.mkdir(this.configDir, { recursive: true });
|
|
12840
|
+
await fs20.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
12737
12841
|
} catch (error) {
|
|
12738
12842
|
throw new Error(
|
|
12739
12843
|
`Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -12806,7 +12910,7 @@ var ProjectConfigService = class {
|
|
|
12806
12910
|
|
|
12807
12911
|
// src/commands/rules/init.ts
|
|
12808
12912
|
function createRulesInitCommand() {
|
|
12809
|
-
return new
|
|
12913
|
+
return new Command81("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) => {
|
|
12810
12914
|
const configService = new ConfigService();
|
|
12811
12915
|
const config = await configService.load();
|
|
12812
12916
|
if (!config) {
|
|
@@ -12902,11 +13006,11 @@ function createRulesInitCommand() {
|
|
|
12902
13006
|
});
|
|
12903
13007
|
}
|
|
12904
13008
|
async function applyCursorFormat(bundle) {
|
|
12905
|
-
const rulesDir =
|
|
12906
|
-
await
|
|
13009
|
+
const rulesDir = join12(process.cwd(), ".cursor", "rules");
|
|
13010
|
+
await fs21.mkdir(rulesDir, { recursive: true });
|
|
12907
13011
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
12908
|
-
const filePath =
|
|
12909
|
-
await
|
|
13012
|
+
const filePath = join12(rulesDir, filename);
|
|
13013
|
+
await fs21.writeFile(filePath, content, "utf-8");
|
|
12910
13014
|
console.log(`\u2713 Created .cursor/rules/${filename}`);
|
|
12911
13015
|
}
|
|
12912
13016
|
}
|
|
@@ -12933,14 +13037,14 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
12933
13037
|
}
|
|
12934
13038
|
}
|
|
12935
13039
|
const agentsMd = sections.join("\n");
|
|
12936
|
-
await
|
|
13040
|
+
await fs21.writeFile("AGENTS.md", agentsMd, "utf-8");
|
|
12937
13041
|
console.log("\u2713 Created AGENTS.md");
|
|
12938
13042
|
}
|
|
12939
13043
|
|
|
12940
13044
|
// src/commands/rules/apply.ts
|
|
12941
|
-
import { Command as
|
|
12942
|
-
import { promises as
|
|
12943
|
-
import { join as
|
|
13045
|
+
import { Command as Command82 } from "commander";
|
|
13046
|
+
import { promises as fs23 } from "fs";
|
|
13047
|
+
import { join as join14 } from "path";
|
|
12944
13048
|
import { search, confirm as confirm15, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
12945
13049
|
|
|
12946
13050
|
// src/services/rules-generator.service.ts
|
|
@@ -13231,8 +13335,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
|
|
|
13231
13335
|
};
|
|
13232
13336
|
|
|
13233
13337
|
// src/services/backup.service.ts
|
|
13234
|
-
import { promises as
|
|
13235
|
-
import { join as
|
|
13338
|
+
import { promises as fs22 } from "fs";
|
|
13339
|
+
import { join as join13, dirname } from "path";
|
|
13236
13340
|
var BackupService = class {
|
|
13237
13341
|
backupDir = ".jai1/backups";
|
|
13238
13342
|
/**
|
|
@@ -13240,7 +13344,7 @@ var BackupService = class {
|
|
|
13240
13344
|
*/
|
|
13241
13345
|
async createBackup(ides, presetSlug) {
|
|
13242
13346
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13243
|
-
const backupPath =
|
|
13347
|
+
const backupPath = join13(this.backupDir, timestamp);
|
|
13244
13348
|
const backedUpFiles = [];
|
|
13245
13349
|
let hasContent = false;
|
|
13246
13350
|
for (const ideId of ides) {
|
|
@@ -13249,7 +13353,7 @@ var BackupService = class {
|
|
|
13249
13353
|
console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
|
|
13250
13354
|
continue;
|
|
13251
13355
|
}
|
|
13252
|
-
const rulesPath = format.rulesPath === "." ? process.cwd() :
|
|
13356
|
+
const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
|
|
13253
13357
|
try {
|
|
13254
13358
|
const exists = await this.pathExists(rulesPath);
|
|
13255
13359
|
if (!exists) {
|
|
@@ -13262,19 +13366,19 @@ var BackupService = class {
|
|
|
13262
13366
|
await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
|
|
13263
13367
|
hasContent = true;
|
|
13264
13368
|
} else {
|
|
13265
|
-
const stats = await
|
|
13369
|
+
const stats = await fs22.stat(rulesPath);
|
|
13266
13370
|
if (stats.isDirectory()) {
|
|
13267
|
-
const files = await
|
|
13371
|
+
const files = await fs22.readdir(rulesPath);
|
|
13268
13372
|
for (const file of files) {
|
|
13269
13373
|
if (file.endsWith(format.fileExtension)) {
|
|
13270
|
-
const originalPath =
|
|
13271
|
-
const relativePath =
|
|
13272
|
-
const destPath =
|
|
13273
|
-
await
|
|
13274
|
-
await
|
|
13374
|
+
const originalPath = join13(rulesPath, file);
|
|
13375
|
+
const relativePath = join13(format.rulesPath, file);
|
|
13376
|
+
const destPath = join13(backupPath, ideId, file);
|
|
13377
|
+
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13378
|
+
await fs22.copyFile(originalPath, destPath);
|
|
13275
13379
|
backedUpFiles.push({
|
|
13276
13380
|
originalPath: relativePath,
|
|
13277
|
-
backupPath:
|
|
13381
|
+
backupPath: join13(ideId, file),
|
|
13278
13382
|
ide: ideId
|
|
13279
13383
|
});
|
|
13280
13384
|
hasContent = true;
|
|
@@ -13295,9 +13399,9 @@ var BackupService = class {
|
|
|
13295
13399
|
ides,
|
|
13296
13400
|
files: backedUpFiles
|
|
13297
13401
|
};
|
|
13298
|
-
await
|
|
13299
|
-
await
|
|
13300
|
-
|
|
13402
|
+
await fs22.mkdir(backupPath, { recursive: true });
|
|
13403
|
+
await fs22.writeFile(
|
|
13404
|
+
join13(backupPath, "metadata.json"),
|
|
13301
13405
|
JSON.stringify(metadata, null, 2),
|
|
13302
13406
|
"utf-8"
|
|
13303
13407
|
);
|
|
@@ -13307,18 +13411,18 @@ var BackupService = class {
|
|
|
13307
13411
|
* Backup a single file (for AGENTS.md, GEMINI.md)
|
|
13308
13412
|
*/
|
|
13309
13413
|
async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
|
|
13310
|
-
const originalPath =
|
|
13414
|
+
const originalPath = join13(process.cwd(), filename);
|
|
13311
13415
|
try {
|
|
13312
13416
|
const exists = await this.pathExists(originalPath);
|
|
13313
13417
|
if (!exists) {
|
|
13314
13418
|
return;
|
|
13315
13419
|
}
|
|
13316
|
-
const destPath =
|
|
13317
|
-
await
|
|
13318
|
-
await
|
|
13420
|
+
const destPath = join13(backupPath, ideId, filename);
|
|
13421
|
+
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13422
|
+
await fs22.copyFile(originalPath, destPath);
|
|
13319
13423
|
backedUpFiles.push({
|
|
13320
13424
|
originalPath: filename,
|
|
13321
|
-
backupPath:
|
|
13425
|
+
backupPath: join13(ideId, filename),
|
|
13322
13426
|
ide: ideId
|
|
13323
13427
|
});
|
|
13324
13428
|
} catch (error) {
|
|
@@ -13328,16 +13432,16 @@ var BackupService = class {
|
|
|
13328
13432
|
* Restore files from a backup
|
|
13329
13433
|
*/
|
|
13330
13434
|
async restoreBackup(backupPath) {
|
|
13331
|
-
const metadataPath =
|
|
13332
|
-
const metadataContent = await
|
|
13435
|
+
const metadataPath = join13(backupPath, "metadata.json");
|
|
13436
|
+
const metadataContent = await fs22.readFile(metadataPath, "utf-8");
|
|
13333
13437
|
const metadata = JSON.parse(metadataContent);
|
|
13334
13438
|
console.log(`
|
|
13335
13439
|
Restoring backup from ${metadata.timestamp}...`);
|
|
13336
13440
|
for (const file of metadata.files) {
|
|
13337
|
-
const sourcePath =
|
|
13338
|
-
const destPath =
|
|
13339
|
-
await
|
|
13340
|
-
await
|
|
13441
|
+
const sourcePath = join13(backupPath, file.backupPath);
|
|
13442
|
+
const destPath = join13(process.cwd(), file.originalPath);
|
|
13443
|
+
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13444
|
+
await fs22.copyFile(sourcePath, destPath);
|
|
13341
13445
|
console.log(`\u2713 Restored ${file.originalPath}`);
|
|
13342
13446
|
}
|
|
13343
13447
|
console.log("\n\u2705 Backup restored successfully!");
|
|
@@ -13347,18 +13451,18 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13347
13451
|
*/
|
|
13348
13452
|
async listBackups() {
|
|
13349
13453
|
try {
|
|
13350
|
-
const backupDirPath =
|
|
13454
|
+
const backupDirPath = join13(process.cwd(), this.backupDir);
|
|
13351
13455
|
const exists = await this.pathExists(backupDirPath);
|
|
13352
13456
|
if (!exists) {
|
|
13353
13457
|
return [];
|
|
13354
13458
|
}
|
|
13355
|
-
const entries = await
|
|
13459
|
+
const entries = await fs22.readdir(backupDirPath, { withFileTypes: true });
|
|
13356
13460
|
const backups = [];
|
|
13357
13461
|
for (const entry of entries) {
|
|
13358
13462
|
if (entry.isDirectory()) {
|
|
13359
|
-
const metadataPath =
|
|
13463
|
+
const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
|
|
13360
13464
|
try {
|
|
13361
|
-
const metadataContent = await
|
|
13465
|
+
const metadataContent = await fs22.readFile(metadataPath, "utf-8");
|
|
13362
13466
|
const metadata = JSON.parse(metadataContent);
|
|
13363
13467
|
backups.push(metadata);
|
|
13364
13468
|
} catch {
|
|
@@ -13375,7 +13479,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13375
13479
|
* Delete a specific backup
|
|
13376
13480
|
*/
|
|
13377
13481
|
async deleteBackup(timestamp) {
|
|
13378
|
-
const backupPath =
|
|
13482
|
+
const backupPath = join13(process.cwd(), this.backupDir, timestamp);
|
|
13379
13483
|
await this.deleteDirectory(backupPath);
|
|
13380
13484
|
}
|
|
13381
13485
|
/**
|
|
@@ -13407,7 +13511,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13407
13511
|
*/
|
|
13408
13512
|
async pathExists(path13) {
|
|
13409
13513
|
try {
|
|
13410
|
-
await
|
|
13514
|
+
await fs22.access(path13);
|
|
13411
13515
|
return true;
|
|
13412
13516
|
} catch {
|
|
13413
13517
|
return false;
|
|
@@ -13422,16 +13526,16 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13422
13526
|
if (!exists) {
|
|
13423
13527
|
return;
|
|
13424
13528
|
}
|
|
13425
|
-
const entries = await
|
|
13529
|
+
const entries = await fs22.readdir(path13, { withFileTypes: true });
|
|
13426
13530
|
for (const entry of entries) {
|
|
13427
|
-
const fullPath =
|
|
13531
|
+
const fullPath = join13(path13, entry.name);
|
|
13428
13532
|
if (entry.isDirectory()) {
|
|
13429
13533
|
await this.deleteDirectory(fullPath);
|
|
13430
13534
|
} else {
|
|
13431
|
-
await
|
|
13535
|
+
await fs22.unlink(fullPath);
|
|
13432
13536
|
}
|
|
13433
13537
|
}
|
|
13434
|
-
await
|
|
13538
|
+
await fs22.rmdir(path13);
|
|
13435
13539
|
} catch (error) {
|
|
13436
13540
|
}
|
|
13437
13541
|
}
|
|
@@ -13439,20 +13543,20 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13439
13543
|
* Get backup directory path
|
|
13440
13544
|
*/
|
|
13441
13545
|
getBackupDir() {
|
|
13442
|
-
return
|
|
13546
|
+
return join13(process.cwd(), this.backupDir);
|
|
13443
13547
|
}
|
|
13444
13548
|
/**
|
|
13445
13549
|
* Ensure backup directory exists
|
|
13446
13550
|
*/
|
|
13447
13551
|
async ensureBackupDir() {
|
|
13448
|
-
const backupDirPath =
|
|
13449
|
-
await
|
|
13552
|
+
const backupDirPath = join13(process.cwd(), this.backupDir);
|
|
13553
|
+
await fs22.mkdir(backupDirPath, { recursive: true });
|
|
13450
13554
|
}
|
|
13451
13555
|
};
|
|
13452
13556
|
|
|
13453
13557
|
// src/commands/rules/apply.ts
|
|
13454
13558
|
function createRulesApplyCommand() {
|
|
13455
|
-
return new
|
|
13559
|
+
return new Command82("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) => {
|
|
13456
13560
|
const configService = new ConfigService();
|
|
13457
13561
|
const config = await configService.load();
|
|
13458
13562
|
if (!config) {
|
|
@@ -13618,21 +13722,21 @@ function createRulesApplyCommand() {
|
|
|
13618
13722
|
}
|
|
13619
13723
|
}
|
|
13620
13724
|
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
13621
|
-
const rulePresetDir =
|
|
13725
|
+
const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
|
|
13622
13726
|
try {
|
|
13623
|
-
await
|
|
13727
|
+
await fs23.rm(rulePresetDir, { recursive: true, force: true });
|
|
13624
13728
|
} catch {
|
|
13625
13729
|
}
|
|
13626
|
-
await
|
|
13627
|
-
await
|
|
13628
|
-
|
|
13730
|
+
await fs23.mkdir(rulePresetDir, { recursive: true });
|
|
13731
|
+
await fs23.writeFile(
|
|
13732
|
+
join14(rulePresetDir, "preset.json"),
|
|
13629
13733
|
JSON.stringify(bundle.preset, null, 2),
|
|
13630
13734
|
"utf-8"
|
|
13631
13735
|
);
|
|
13632
13736
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
13633
|
-
const filePath =
|
|
13634
|
-
await
|
|
13635
|
-
await
|
|
13737
|
+
const filePath = join14(rulePresetDir, filename);
|
|
13738
|
+
await fs23.mkdir(join14(filePath, ".."), { recursive: true });
|
|
13739
|
+
await fs23.writeFile(filePath, content, "utf-8");
|
|
13636
13740
|
}
|
|
13637
13741
|
console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
|
|
13638
13742
|
const allGeneratedFiles = [];
|
|
@@ -13640,9 +13744,9 @@ function createRulesApplyCommand() {
|
|
|
13640
13744
|
try {
|
|
13641
13745
|
const files = generatorService.generateForIde(bundle, ideId);
|
|
13642
13746
|
for (const file of files) {
|
|
13643
|
-
const fullPath =
|
|
13644
|
-
await
|
|
13645
|
-
await
|
|
13747
|
+
const fullPath = join14(process.cwd(), file.path);
|
|
13748
|
+
await fs23.mkdir(join14(fullPath, ".."), { recursive: true });
|
|
13749
|
+
await fs23.writeFile(fullPath, file.content, "utf-8");
|
|
13646
13750
|
console.log(`\u2713 [${ideId}] ${file.path}`);
|
|
13647
13751
|
allGeneratedFiles.push({
|
|
13648
13752
|
ide: ideId,
|
|
@@ -13747,11 +13851,11 @@ function createRulesApplyCommand() {
|
|
|
13747
13851
|
}
|
|
13748
13852
|
|
|
13749
13853
|
// src/commands/rules/restore.ts
|
|
13750
|
-
import { Command as
|
|
13751
|
-
import { join as
|
|
13854
|
+
import { Command as Command83 } from "commander";
|
|
13855
|
+
import { join as join15 } from "path";
|
|
13752
13856
|
import { select as select5, confirm as confirm16 } from "@inquirer/prompts";
|
|
13753
13857
|
function createRulesRestoreCommand() {
|
|
13754
|
-
return new
|
|
13858
|
+
return new Command83("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
13755
13859
|
const backupService = new BackupService();
|
|
13756
13860
|
const backups = await backupService.listBackups();
|
|
13757
13861
|
if (backups.length === 0) {
|
|
@@ -13795,7 +13899,7 @@ function createRulesRestoreCommand() {
|
|
|
13795
13899
|
}
|
|
13796
13900
|
console.log("\n\u{1F504} Restoring backup...\n");
|
|
13797
13901
|
try {
|
|
13798
|
-
const backupPath =
|
|
13902
|
+
const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
|
|
13799
13903
|
await backupService.restoreBackup(backupPath);
|
|
13800
13904
|
console.log("\n\u2705 Backup restored successfully!\n");
|
|
13801
13905
|
console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
|
|
@@ -13820,18 +13924,18 @@ function formatTimestamp(timestamp) {
|
|
|
13820
13924
|
}
|
|
13821
13925
|
|
|
13822
13926
|
// src/commands/rules/sync.ts
|
|
13823
|
-
import { Command as
|
|
13824
|
-
import { promises as
|
|
13825
|
-
import { join as
|
|
13927
|
+
import { Command as Command84 } from "commander";
|
|
13928
|
+
import { promises as fs24 } from "fs";
|
|
13929
|
+
import { join as join16 } from "path";
|
|
13826
13930
|
import { checkbox as checkbox6, confirm as confirm17, Separator } from "@inquirer/prompts";
|
|
13827
13931
|
function createRulesSyncCommand() {
|
|
13828
|
-
return new
|
|
13829
|
-
const rulePresetDir =
|
|
13830
|
-
const presetJsonPath =
|
|
13932
|
+
return new Command84("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) => {
|
|
13933
|
+
const rulePresetDir = join16(process.cwd(), ".jai1", "rule-preset");
|
|
13934
|
+
const presetJsonPath = join16(rulePresetDir, "preset.json");
|
|
13831
13935
|
let presetExists = false;
|
|
13832
13936
|
let presetData = null;
|
|
13833
13937
|
try {
|
|
13834
|
-
const presetContent = await
|
|
13938
|
+
const presetContent = await fs24.readFile(presetJsonPath, "utf-8");
|
|
13835
13939
|
presetData = JSON.parse(presetContent);
|
|
13836
13940
|
presetExists = true;
|
|
13837
13941
|
} catch {
|
|
@@ -13961,11 +14065,11 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
13961
14065
|
throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
|
|
13962
14066
|
}
|
|
13963
14067
|
const bundle = await presetResponse.json();
|
|
13964
|
-
const files = await
|
|
14068
|
+
const files = await fs24.readdir(rulePresetDir);
|
|
13965
14069
|
for (const file of files) {
|
|
13966
14070
|
if (file.endsWith(".mdc") || file.endsWith(".md")) {
|
|
13967
|
-
const filePath =
|
|
13968
|
-
const content = await
|
|
14071
|
+
const filePath = join16(rulePresetDir, file);
|
|
14072
|
+
const content = await fs24.readFile(filePath, "utf-8");
|
|
13969
14073
|
bundle.files[file] = content;
|
|
13970
14074
|
}
|
|
13971
14075
|
}
|
|
@@ -13979,9 +14083,9 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
13979
14083
|
}
|
|
13980
14084
|
const files2 = generatorService.generateForIde(bundle, ideId);
|
|
13981
14085
|
for (const file of files2) {
|
|
13982
|
-
const fullPath =
|
|
13983
|
-
await
|
|
13984
|
-
await
|
|
14086
|
+
const fullPath = join16(process.cwd(), file.path);
|
|
14087
|
+
await fs24.mkdir(join16(fullPath, ".."), { recursive: true });
|
|
14088
|
+
await fs24.writeFile(fullPath, file.content, "utf-8");
|
|
13985
14089
|
}
|
|
13986
14090
|
console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
|
|
13987
14091
|
} catch (error) {
|
|
@@ -14043,11 +14147,11 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
|
|
|
14043
14147
|
}
|
|
14044
14148
|
|
|
14045
14149
|
// src/commands/rules/info.ts
|
|
14046
|
-
import { Command as
|
|
14047
|
-
import { promises as
|
|
14048
|
-
import { join as
|
|
14150
|
+
import { Command as Command85 } from "commander";
|
|
14151
|
+
import { promises as fs25 } from "fs";
|
|
14152
|
+
import { join as join17 } from "path";
|
|
14049
14153
|
function createRulesInfoCommand() {
|
|
14050
|
-
return new
|
|
14154
|
+
return new Command85("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
14051
14155
|
const projectConfigService = new ProjectConfigService();
|
|
14052
14156
|
const rulesConfig = await projectConfigService.loadRules();
|
|
14053
14157
|
if (!rulesConfig) {
|
|
@@ -14060,14 +14164,14 @@ function createRulesInfoCommand() {
|
|
|
14060
14164
|
return;
|
|
14061
14165
|
}
|
|
14062
14166
|
console.log("\u{1F4CB} Current Preset Information\n");
|
|
14063
|
-
const rulePresetDir =
|
|
14064
|
-
const presetJsonPath =
|
|
14167
|
+
const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
|
|
14168
|
+
const presetJsonPath = join17(rulePresetDir, "preset.json");
|
|
14065
14169
|
let presetMetadata = null;
|
|
14066
14170
|
let presetFiles = [];
|
|
14067
14171
|
try {
|
|
14068
|
-
const presetContent = await
|
|
14172
|
+
const presetContent = await fs25.readFile(presetJsonPath, "utf-8");
|
|
14069
14173
|
presetMetadata = JSON.parse(presetContent);
|
|
14070
|
-
const files = await
|
|
14174
|
+
const files = await fs25.readdir(rulePresetDir);
|
|
14071
14175
|
presetFiles = files.filter((f) => f.endsWith(".mdc"));
|
|
14072
14176
|
} catch {
|
|
14073
14177
|
}
|
|
@@ -14128,7 +14232,7 @@ Available Backups (${rulesConfig.backups.length}):`);
|
|
|
14128
14232
|
}
|
|
14129
14233
|
async function checkPathExists(path13) {
|
|
14130
14234
|
try {
|
|
14131
|
-
await
|
|
14235
|
+
await fs25.access(join17(process.cwd(), path13));
|
|
14132
14236
|
return true;
|
|
14133
14237
|
} catch {
|
|
14134
14238
|
return false;
|
|
@@ -14150,26 +14254,26 @@ async function checkIdeFilesExist(ideId, format) {
|
|
|
14150
14254
|
|
|
14151
14255
|
// src/commands/rules/index.ts
|
|
14152
14256
|
function showRulesHelp() {
|
|
14153
|
-
console.log(
|
|
14257
|
+
console.log(chalk50.bold.cyan("\u{1F4CB} jai1 rules") + chalk50.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
|
|
14154
14258
|
console.log();
|
|
14155
|
-
console.log(
|
|
14156
|
-
console.log(` ${
|
|
14157
|
-
console.log(` ${
|
|
14158
|
-
console.log(` ${
|
|
14159
|
-
console.log(` ${
|
|
14160
|
-
console.log(` ${
|
|
14161
|
-
console.log(` ${
|
|
14259
|
+
console.log(chalk50.bold("C\xE1c l\u1EC7nh:"));
|
|
14260
|
+
console.log(` ${chalk50.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
|
|
14261
|
+
console.log(` ${chalk50.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
|
|
14262
|
+
console.log(` ${chalk50.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
|
|
14263
|
+
console.log(` ${chalk50.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
|
|
14264
|
+
console.log(` ${chalk50.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
|
|
14265
|
+
console.log(` ${chalk50.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
|
|
14162
14266
|
console.log();
|
|
14163
|
-
console.log(
|
|
14164
|
-
console.log(
|
|
14165
|
-
console.log(
|
|
14166
|
-
console.log(
|
|
14167
|
-
console.log(
|
|
14267
|
+
console.log(chalk50.bold("V\xED d\u1EE5:"));
|
|
14268
|
+
console.log(chalk50.dim(" $ jai1 rules list"));
|
|
14269
|
+
console.log(chalk50.dim(" $ jai1 rules info react-typescript"));
|
|
14270
|
+
console.log(chalk50.dim(" $ jai1 rules init --preset=react-typescript"));
|
|
14271
|
+
console.log(chalk50.dim(" $ jai1 rules apply react-typescript"));
|
|
14168
14272
|
console.log();
|
|
14169
|
-
console.log(
|
|
14273
|
+
console.log(chalk50.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
14170
14274
|
}
|
|
14171
14275
|
function createRulesCommand() {
|
|
14172
|
-
const rulesCommand = new
|
|
14276
|
+
const rulesCommand = new Command86("rules").description("Manage rule presets for AI agents").action(() => {
|
|
14173
14277
|
showRulesHelp();
|
|
14174
14278
|
});
|
|
14175
14279
|
rulesCommand.addCommand(createRulesListCommand());
|
|
@@ -14182,16 +14286,16 @@ function createRulesCommand() {
|
|
|
14182
14286
|
}
|
|
14183
14287
|
|
|
14184
14288
|
// src/commands/skills/index.ts
|
|
14185
|
-
import { Command as
|
|
14186
|
-
import
|
|
14289
|
+
import { Command as Command92 } from "commander";
|
|
14290
|
+
import chalk56 from "chalk";
|
|
14187
14291
|
|
|
14188
14292
|
// src/commands/skills/find.ts
|
|
14189
|
-
import { Command as
|
|
14190
|
-
import
|
|
14293
|
+
import { Command as Command87 } from "commander";
|
|
14294
|
+
import chalk51 from "chalk";
|
|
14191
14295
|
|
|
14192
14296
|
// src/services/skills.service.ts
|
|
14193
|
-
import { promises as
|
|
14194
|
-
import { join as
|
|
14297
|
+
import { promises as fs26 } from "fs";
|
|
14298
|
+
import { join as join18 } from "path";
|
|
14195
14299
|
import { execFile } from "child_process";
|
|
14196
14300
|
import { promisify } from "util";
|
|
14197
14301
|
var execFileAsync = promisify(execFile);
|
|
@@ -14227,20 +14331,20 @@ var SkillsService = class {
|
|
|
14227
14331
|
* List locally installed skills from .jai1/skills/
|
|
14228
14332
|
*/
|
|
14229
14333
|
async listLocal(projectRoot) {
|
|
14230
|
-
const skillsDir =
|
|
14334
|
+
const skillsDir = join18(projectRoot, ".jai1", "skills");
|
|
14231
14335
|
const skills = [];
|
|
14232
14336
|
try {
|
|
14233
|
-
const entries = await
|
|
14337
|
+
const entries = await fs26.readdir(skillsDir, { withFileTypes: true });
|
|
14234
14338
|
for (const entry of entries) {
|
|
14235
14339
|
if (!entry.isDirectory()) continue;
|
|
14236
|
-
const skillPath =
|
|
14237
|
-
const skillMd =
|
|
14340
|
+
const skillPath = join18(skillsDir, entry.name);
|
|
14341
|
+
const skillMd = join18(skillPath, "SKILL.md");
|
|
14238
14342
|
try {
|
|
14239
|
-
await
|
|
14343
|
+
await fs26.access(skillMd);
|
|
14240
14344
|
} catch {
|
|
14241
14345
|
continue;
|
|
14242
14346
|
}
|
|
14243
|
-
const content = await
|
|
14347
|
+
const content = await fs26.readFile(skillMd, "utf-8");
|
|
14244
14348
|
const { name, description } = this.parseFrontmatter(content);
|
|
14245
14349
|
const fileCount = await this.countFiles(skillPath);
|
|
14246
14350
|
skills.push({
|
|
@@ -14259,14 +14363,14 @@ var SkillsService = class {
|
|
|
14259
14363
|
* Get detailed info for a single skill
|
|
14260
14364
|
*/
|
|
14261
14365
|
async getSkillInfo(projectRoot, skillName) {
|
|
14262
|
-
const skillPath =
|
|
14263
|
-
const skillMd =
|
|
14366
|
+
const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
|
|
14367
|
+
const skillMd = join18(skillPath, "SKILL.md");
|
|
14264
14368
|
try {
|
|
14265
|
-
await
|
|
14369
|
+
await fs26.access(skillMd);
|
|
14266
14370
|
} catch {
|
|
14267
14371
|
return null;
|
|
14268
14372
|
}
|
|
14269
|
-
const content = await
|
|
14373
|
+
const content = await fs26.readFile(skillMd, "utf-8");
|
|
14270
14374
|
const { name, description } = this.parseFrontmatter(content);
|
|
14271
14375
|
const fileCount = await this.countFiles(skillPath);
|
|
14272
14376
|
return {
|
|
@@ -14335,26 +14439,26 @@ var SkillsService = class {
|
|
|
14335
14439
|
* After npx skills add, copy newly installed skills from .agents/skills/ into .jai1/skills/
|
|
14336
14440
|
*/
|
|
14337
14441
|
async copySkillshResultsToJai1(projectRoot, specificSkill) {
|
|
14338
|
-
const jai1SkillsDir =
|
|
14339
|
-
await
|
|
14340
|
-
const universalDir =
|
|
14442
|
+
const jai1SkillsDir = join18(projectRoot, ".jai1", "skills");
|
|
14443
|
+
await fs26.mkdir(jai1SkillsDir, { recursive: true });
|
|
14444
|
+
const universalDir = join18(projectRoot, ".agents", "skills");
|
|
14341
14445
|
try {
|
|
14342
|
-
const entries = await
|
|
14446
|
+
const entries = await fs26.readdir(universalDir, { withFileTypes: true });
|
|
14343
14447
|
for (const entry of entries) {
|
|
14344
14448
|
if (!entry.isDirectory()) continue;
|
|
14345
14449
|
if (specificSkill && entry.name !== specificSkill) continue;
|
|
14346
|
-
const srcSkill =
|
|
14347
|
-
const skillMd =
|
|
14450
|
+
const srcSkill = join18(universalDir, entry.name);
|
|
14451
|
+
const skillMd = join18(srcSkill, "SKILL.md");
|
|
14348
14452
|
try {
|
|
14349
|
-
await
|
|
14453
|
+
await fs26.access(skillMd);
|
|
14350
14454
|
} catch {
|
|
14351
14455
|
continue;
|
|
14352
14456
|
}
|
|
14353
|
-
const targetSkill =
|
|
14457
|
+
const targetSkill = join18(jai1SkillsDir, entry.name);
|
|
14354
14458
|
try {
|
|
14355
|
-
await
|
|
14459
|
+
await fs26.access(targetSkill);
|
|
14356
14460
|
} catch {
|
|
14357
|
-
await
|
|
14461
|
+
await fs26.mkdir(targetSkill, { recursive: true });
|
|
14358
14462
|
await this.copyDir(srcSkill, targetSkill);
|
|
14359
14463
|
}
|
|
14360
14464
|
}
|
|
@@ -14389,16 +14493,16 @@ var SkillsService = class {
|
|
|
14389
14493
|
const target = this.getIDETarget(ide);
|
|
14390
14494
|
if (!target) continue;
|
|
14391
14495
|
for (const skill of skillsToSync) {
|
|
14392
|
-
const targetPath =
|
|
14496
|
+
const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
|
|
14393
14497
|
try {
|
|
14394
14498
|
let status = "created";
|
|
14395
14499
|
try {
|
|
14396
|
-
await
|
|
14500
|
+
await fs26.access(targetPath);
|
|
14397
14501
|
status = "updated";
|
|
14398
|
-
await
|
|
14502
|
+
await fs26.rm(targetPath, { recursive: true, force: true });
|
|
14399
14503
|
} catch {
|
|
14400
14504
|
}
|
|
14401
|
-
await
|
|
14505
|
+
await fs26.mkdir(targetPath, { recursive: true });
|
|
14402
14506
|
await this.copyDir(skill.path, targetPath);
|
|
14403
14507
|
if (status === "created") created++;
|
|
14404
14508
|
else updated++;
|
|
@@ -14450,10 +14554,10 @@ var SkillsService = class {
|
|
|
14450
14554
|
*/
|
|
14451
14555
|
async countFiles(dirPath) {
|
|
14452
14556
|
let count = 0;
|
|
14453
|
-
const entries = await
|
|
14557
|
+
const entries = await fs26.readdir(dirPath, { withFileTypes: true });
|
|
14454
14558
|
for (const entry of entries) {
|
|
14455
14559
|
if (entry.isDirectory()) {
|
|
14456
|
-
count += await this.countFiles(
|
|
14560
|
+
count += await this.countFiles(join18(dirPath, entry.name));
|
|
14457
14561
|
} else {
|
|
14458
14562
|
count++;
|
|
14459
14563
|
}
|
|
@@ -14464,15 +14568,15 @@ var SkillsService = class {
|
|
|
14464
14568
|
* Copy directory recursively
|
|
14465
14569
|
*/
|
|
14466
14570
|
async copyDir(source, target) {
|
|
14467
|
-
const entries = await
|
|
14571
|
+
const entries = await fs26.readdir(source, { withFileTypes: true });
|
|
14468
14572
|
for (const entry of entries) {
|
|
14469
|
-
const srcPath =
|
|
14470
|
-
const tgtPath =
|
|
14573
|
+
const srcPath = join18(source, entry.name);
|
|
14574
|
+
const tgtPath = join18(target, entry.name);
|
|
14471
14575
|
if (entry.isDirectory()) {
|
|
14472
|
-
await
|
|
14576
|
+
await fs26.mkdir(tgtPath, { recursive: true });
|
|
14473
14577
|
await this.copyDir(srcPath, tgtPath);
|
|
14474
14578
|
} else {
|
|
14475
|
-
await
|
|
14579
|
+
await fs26.copyFile(srcPath, tgtPath);
|
|
14476
14580
|
}
|
|
14477
14581
|
}
|
|
14478
14582
|
}
|
|
@@ -14480,7 +14584,7 @@ var SkillsService = class {
|
|
|
14480
14584
|
|
|
14481
14585
|
// src/commands/skills/find.ts
|
|
14482
14586
|
function createSkillsFindCommand() {
|
|
14483
|
-
return new
|
|
14587
|
+
return new Command87("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) => {
|
|
14484
14588
|
const searchNpm = options.skillsh || options.all;
|
|
14485
14589
|
const searchServer = !options.skillsh || options.all;
|
|
14486
14590
|
if (searchServer) {
|
|
@@ -14489,83 +14593,83 @@ function createSkillsFindCommand() {
|
|
|
14489
14593
|
if (!config) {
|
|
14490
14594
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
14491
14595
|
}
|
|
14492
|
-
console.log(
|
|
14596
|
+
console.log(chalk51.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
|
|
14493
14597
|
console.log();
|
|
14494
14598
|
const skillsService = new SkillsService();
|
|
14495
14599
|
const results = await skillsService.searchFromServer(config, query);
|
|
14496
14600
|
if (results.length === 0) {
|
|
14497
|
-
console.log(
|
|
14601
|
+
console.log(chalk51.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
|
|
14498
14602
|
} else {
|
|
14499
|
-
console.log(
|
|
14603
|
+
console.log(chalk51.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)
|
|
14500
14604
|
`));
|
|
14501
14605
|
for (const skill of results) {
|
|
14502
14606
|
const name = skill.filepath.replace("skills/", "");
|
|
14503
|
-
const version = skill.version ?
|
|
14504
|
-
const downloads =
|
|
14607
|
+
const version = skill.version ? chalk51.green(`v${skill.version}`) : chalk51.dim("-");
|
|
14608
|
+
const downloads = chalk51.dim(`${skill.downloads || 0} downloads`);
|
|
14505
14609
|
const desc = skill.description || "";
|
|
14506
14610
|
const maxDesc = 120;
|
|
14507
14611
|
const truncatedDesc = desc.length > maxDesc ? desc.slice(0, maxDesc) + "\u2026" : desc;
|
|
14508
|
-
console.log(` ${
|
|
14612
|
+
console.log(` ${chalk51.bold.white(name)} ${version} \xB7 ${downloads}`);
|
|
14509
14613
|
if (truncatedDesc) {
|
|
14510
|
-
console.log(` ${
|
|
14614
|
+
console.log(` ${chalk51.dim(truncatedDesc)}`);
|
|
14511
14615
|
}
|
|
14512
14616
|
console.log();
|
|
14513
14617
|
}
|
|
14514
14618
|
}
|
|
14515
14619
|
}
|
|
14516
14620
|
if (searchNpm) {
|
|
14517
|
-
console.log(
|
|
14621
|
+
console.log(chalk51.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
|
|
14518
14622
|
console.log();
|
|
14519
14623
|
const skillsService = new SkillsService();
|
|
14520
14624
|
try {
|
|
14521
14625
|
const output = await skillsService.npmSkillsFind(query);
|
|
14522
|
-
console.log(
|
|
14626
|
+
console.log(chalk51.bold("\u{1F310} npm Skills Registry"));
|
|
14523
14627
|
console.log(output);
|
|
14524
14628
|
} catch (error) {
|
|
14525
|
-
console.log(
|
|
14629
|
+
console.log(chalk51.yellow(
|
|
14526
14630
|
`Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
|
|
14527
14631
|
));
|
|
14528
14632
|
}
|
|
14529
14633
|
}
|
|
14530
14634
|
if (searchServer && !searchNpm) {
|
|
14531
|
-
console.log(
|
|
14635
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
|
|
14532
14636
|
} else if (searchNpm && !searchServer) {
|
|
14533
|
-
console.log(
|
|
14637
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14534
14638
|
} else {
|
|
14535
|
-
console.log(
|
|
14536
|
-
console.log(
|
|
14537
|
-
console.log(
|
|
14639
|
+
console.log(chalk51.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
|
|
14640
|
+
console.log(chalk51.dim(" Server: j skills add <t\xEAn>"));
|
|
14641
|
+
console.log(chalk51.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
|
|
14538
14642
|
}
|
|
14539
14643
|
});
|
|
14540
14644
|
}
|
|
14541
14645
|
|
|
14542
14646
|
// src/commands/skills/add.ts
|
|
14543
|
-
import { Command as
|
|
14544
|
-
import { join as
|
|
14545
|
-
import
|
|
14647
|
+
import { Command as Command88 } from "commander";
|
|
14648
|
+
import { join as join19 } from "path";
|
|
14649
|
+
import chalk52 from "chalk";
|
|
14546
14650
|
import { checkbox as checkbox7 } from "@inquirer/prompts";
|
|
14547
14651
|
function createSkillsAddCommand() {
|
|
14548
|
-
return new
|
|
14652
|
+
return new Command88("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) => {
|
|
14549
14653
|
const skillsService = new SkillsService();
|
|
14550
14654
|
const projectRoot = process.cwd();
|
|
14551
14655
|
const headless = options.yes === true;
|
|
14552
14656
|
if (options.skillsh) {
|
|
14553
|
-
console.log(
|
|
14657
|
+
console.log(chalk52.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
|
|
14554
14658
|
console.log();
|
|
14555
14659
|
const output = await skillsService.npmSkillsAdd(name, projectRoot);
|
|
14556
14660
|
console.log(output);
|
|
14557
|
-
console.log(
|
|
14661
|
+
console.log(chalk52.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
|
|
14558
14662
|
} else {
|
|
14559
14663
|
const configService = new ConfigService();
|
|
14560
14664
|
const config = await configService.load();
|
|
14561
14665
|
if (!config) {
|
|
14562
14666
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
14563
14667
|
}
|
|
14564
|
-
console.log(
|
|
14668
|
+
console.log(chalk52.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
|
|
14565
14669
|
console.log();
|
|
14566
|
-
const targetDir =
|
|
14670
|
+
const targetDir = join19(projectRoot, ".jai1");
|
|
14567
14671
|
await skillsService.installFromServer(config, name, targetDir);
|
|
14568
|
-
console.log(
|
|
14672
|
+
console.log(chalk52.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
|
|
14569
14673
|
}
|
|
14570
14674
|
console.log();
|
|
14571
14675
|
if (options.sync) {
|
|
@@ -14590,7 +14694,7 @@ function createSkillsAddCommand() {
|
|
|
14590
14694
|
});
|
|
14591
14695
|
}
|
|
14592
14696
|
if (selectedIdes.length > 0) {
|
|
14593
|
-
console.log(
|
|
14697
|
+
console.log(chalk52.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
|
|
14594
14698
|
console.log();
|
|
14595
14699
|
const slug = name.includes("/") ? name.split("/").pop() : name;
|
|
14596
14700
|
const result = await skillsService.syncToIdes(
|
|
@@ -14603,24 +14707,24 @@ function createSkillsAddCommand() {
|
|
|
14603
14707
|
}
|
|
14604
14708
|
);
|
|
14605
14709
|
console.log();
|
|
14606
|
-
console.log(
|
|
14710
|
+
console.log(chalk52.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
|
|
14607
14711
|
if (result.errors > 0) {
|
|
14608
|
-
console.log(
|
|
14712
|
+
console.log(chalk52.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
|
|
14609
14713
|
}
|
|
14610
14714
|
}
|
|
14611
14715
|
} else {
|
|
14612
|
-
console.log(
|
|
14613
|
-
console.log(
|
|
14716
|
+
console.log(chalk52.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14717
|
+
console.log(chalk52.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
|
|
14614
14718
|
}
|
|
14615
14719
|
});
|
|
14616
14720
|
}
|
|
14617
14721
|
|
|
14618
14722
|
// src/commands/skills/list.ts
|
|
14619
|
-
import { Command as
|
|
14620
|
-
import
|
|
14723
|
+
import { Command as Command89 } from "commander";
|
|
14724
|
+
import chalk53 from "chalk";
|
|
14621
14725
|
import Table8 from "cli-table3";
|
|
14622
14726
|
function createSkillsListCommand() {
|
|
14623
|
-
return new
|
|
14727
|
+
return new Command89("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) => {
|
|
14624
14728
|
const skillsService = new SkillsService();
|
|
14625
14729
|
if (options.available) {
|
|
14626
14730
|
const configService = new ConfigService();
|
|
@@ -14628,19 +14732,19 @@ function createSkillsListCommand() {
|
|
|
14628
14732
|
if (!config) {
|
|
14629
14733
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
14630
14734
|
}
|
|
14631
|
-
console.log(
|
|
14735
|
+
console.log(chalk53.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
|
|
14632
14736
|
console.log();
|
|
14633
14737
|
const results = await skillsService.searchFromServer(config, options.search);
|
|
14634
14738
|
if (results.length === 0) {
|
|
14635
|
-
console.log(
|
|
14739
|
+
console.log(chalk53.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
|
|
14636
14740
|
return;
|
|
14637
14741
|
}
|
|
14638
14742
|
const table = new Table8({
|
|
14639
14743
|
head: [
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
|
|
14643
|
-
|
|
14744
|
+
chalk53.cyan("T\xEAn"),
|
|
14745
|
+
chalk53.cyan("M\xF4 t\u1EA3"),
|
|
14746
|
+
chalk53.cyan("Version"),
|
|
14747
|
+
chalk53.cyan("Downloads")
|
|
14644
14748
|
],
|
|
14645
14749
|
style: { head: [], border: ["gray"] },
|
|
14646
14750
|
colWidths: [28, 40, 10, 12]
|
|
@@ -14648,72 +14752,72 @@ function createSkillsListCommand() {
|
|
|
14648
14752
|
for (const skill of results) {
|
|
14649
14753
|
const name = skill.filepath.replace("skills/", "");
|
|
14650
14754
|
table.push([
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14755
|
+
chalk53.white(name),
|
|
14756
|
+
chalk53.dim((skill.description || "").slice(0, 38)),
|
|
14757
|
+
chalk53.green(skill.version || "-"),
|
|
14758
|
+
chalk53.dim(String(skill.downloads || 0))
|
|
14655
14759
|
]);
|
|
14656
14760
|
}
|
|
14657
14761
|
console.log(table.toString());
|
|
14658
14762
|
console.log();
|
|
14659
|
-
console.log(
|
|
14660
|
-
console.log(
|
|
14763
|
+
console.log(chalk53.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
|
|
14764
|
+
console.log(chalk53.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14661
14765
|
} else {
|
|
14662
14766
|
const projectRoot = process.cwd();
|
|
14663
14767
|
const skills = await skillsService.listLocal(projectRoot);
|
|
14664
14768
|
if (skills.length === 0) {
|
|
14665
|
-
console.log(
|
|
14769
|
+
console.log(chalk53.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
|
|
14666
14770
|
console.log();
|
|
14667
|
-
console.log(
|
|
14668
|
-
console.log(
|
|
14771
|
+
console.log(chalk53.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14772
|
+
console.log(chalk53.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
|
|
14669
14773
|
return;
|
|
14670
14774
|
}
|
|
14671
|
-
console.log(
|
|
14775
|
+
console.log(chalk53.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
|
|
14672
14776
|
console.log();
|
|
14673
14777
|
const table = new Table8({
|
|
14674
14778
|
head: [
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14779
|
+
chalk53.cyan("T\xEAn"),
|
|
14780
|
+
chalk53.cyan("M\xF4 t\u1EA3"),
|
|
14781
|
+
chalk53.cyan("Files")
|
|
14678
14782
|
],
|
|
14679
14783
|
style: { head: [], border: ["gray"] },
|
|
14680
14784
|
colWidths: [28, 45, 8]
|
|
14681
14785
|
});
|
|
14682
14786
|
for (const skill of skills) {
|
|
14683
14787
|
table.push([
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14788
|
+
chalk53.white(skill.slug),
|
|
14789
|
+
chalk53.dim(skill.description.slice(0, 43)),
|
|
14790
|
+
chalk53.dim(String(skill.fileCount))
|
|
14687
14791
|
]);
|
|
14688
14792
|
}
|
|
14689
14793
|
console.log(table.toString());
|
|
14690
14794
|
console.log();
|
|
14691
|
-
console.log(
|
|
14692
|
-
console.log(
|
|
14795
|
+
console.log(chalk53.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
|
|
14796
|
+
console.log(chalk53.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14693
14797
|
}
|
|
14694
14798
|
});
|
|
14695
14799
|
}
|
|
14696
14800
|
|
|
14697
14801
|
// src/commands/skills/info.ts
|
|
14698
|
-
import { Command as
|
|
14699
|
-
import
|
|
14802
|
+
import { Command as Command90 } from "commander";
|
|
14803
|
+
import chalk54 from "chalk";
|
|
14700
14804
|
function createSkillsInfoCommand() {
|
|
14701
|
-
return new
|
|
14805
|
+
return new Command90("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) => {
|
|
14702
14806
|
const skillsService = new SkillsService();
|
|
14703
14807
|
if (options.server) {
|
|
14704
14808
|
const configService = new ConfigService();
|
|
14705
14809
|
const config = await configService.load();
|
|
14706
14810
|
if (!config) {
|
|
14707
|
-
console.log(
|
|
14811
|
+
console.log(chalk54.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
|
|
14708
14812
|
process.exit(1);
|
|
14709
14813
|
}
|
|
14710
14814
|
const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
|
|
14711
|
-
const { ComponentsService: ComponentsService2 } = await import("./components.service-
|
|
14815
|
+
const { ComponentsService: ComponentsService2 } = await import("./components.service-JUUV4CUI.js");
|
|
14712
14816
|
const componentsService = new ComponentsService2();
|
|
14713
14817
|
try {
|
|
14714
14818
|
const component = await componentsService.get(config, filepath);
|
|
14715
14819
|
console.log(`
|
|
14716
|
-
\u{1F6E0} ${
|
|
14820
|
+
\u{1F6E0} ${chalk54.bold(component.name || name)}
|
|
14717
14821
|
`);
|
|
14718
14822
|
console.log(`Filepath: ${component.filepath}`);
|
|
14719
14823
|
console.log(`Version: ${component.version}`);
|
|
@@ -14726,47 +14830,47 @@ function createSkillsInfoCommand() {
|
|
|
14726
14830
|
}
|
|
14727
14831
|
console.log(`Type: ${component.contentType}`);
|
|
14728
14832
|
console.log();
|
|
14729
|
-
console.log(
|
|
14833
|
+
console.log(chalk54.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14730
14834
|
} catch (error) {
|
|
14731
|
-
console.log(
|
|
14835
|
+
console.log(chalk54.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
|
|
14732
14836
|
process.exit(1);
|
|
14733
14837
|
}
|
|
14734
14838
|
} else {
|
|
14735
14839
|
const projectRoot = process.cwd();
|
|
14736
14840
|
const skill = await skillsService.getSkillInfo(projectRoot, name);
|
|
14737
14841
|
if (!skill) {
|
|
14738
|
-
console.log(
|
|
14739
|
-
console.log(
|
|
14740
|
-
console.log(
|
|
14842
|
+
console.log(chalk54.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
|
|
14843
|
+
console.log(chalk54.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
|
|
14844
|
+
console.log(chalk54.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14741
14845
|
process.exit(1);
|
|
14742
14846
|
}
|
|
14743
14847
|
console.log(`
|
|
14744
|
-
\u{1F6E0} ${
|
|
14848
|
+
\u{1F6E0} ${chalk54.bold(skill.name)}
|
|
14745
14849
|
`);
|
|
14746
14850
|
console.log(`Slug: ${skill.slug}`);
|
|
14747
|
-
console.log(`Description: ${skill.description ||
|
|
14851
|
+
console.log(`Description: ${skill.description || chalk54.dim("(none)")}`);
|
|
14748
14852
|
console.log(`Path: ${skill.path}`);
|
|
14749
14853
|
console.log(`Files: ${skill.fileCount}`);
|
|
14750
14854
|
console.log();
|
|
14751
|
-
console.log(
|
|
14855
|
+
console.log(chalk54.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14752
14856
|
}
|
|
14753
14857
|
});
|
|
14754
14858
|
}
|
|
14755
14859
|
|
|
14756
14860
|
// src/commands/skills/sync.ts
|
|
14757
|
-
import { Command as
|
|
14758
|
-
import
|
|
14861
|
+
import { Command as Command91 } from "commander";
|
|
14862
|
+
import chalk55 from "chalk";
|
|
14759
14863
|
import { confirm as confirm19, checkbox as checkbox8 } from "@inquirer/prompts";
|
|
14760
14864
|
function createSkillsSyncCommand() {
|
|
14761
|
-
return new
|
|
14865
|
+
return new Command91("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) => {
|
|
14762
14866
|
const skillsService = new SkillsService();
|
|
14763
14867
|
const projectRoot = process.cwd();
|
|
14764
14868
|
const headless = options.yes === true;
|
|
14765
|
-
console.log(
|
|
14869
|
+
console.log(chalk55.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
|
|
14766
14870
|
const localSkills = await skillsService.listLocal(projectRoot);
|
|
14767
14871
|
if (localSkills.length === 0) {
|
|
14768
|
-
console.log(
|
|
14769
|
-
console.log(
|
|
14872
|
+
console.log(chalk55.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
|
|
14873
|
+
console.log(chalk55.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
|
|
14770
14874
|
process.exit(1);
|
|
14771
14875
|
}
|
|
14772
14876
|
console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
|
|
@@ -14798,7 +14902,7 @@ function createSkillsSyncCommand() {
|
|
|
14798
14902
|
theme: checkboxTheme
|
|
14799
14903
|
});
|
|
14800
14904
|
if (selectedIdes.length === 0) {
|
|
14801
|
-
console.log(
|
|
14905
|
+
console.log(chalk55.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
|
|
14802
14906
|
process.exit(0);
|
|
14803
14907
|
}
|
|
14804
14908
|
}
|
|
@@ -14813,7 +14917,7 @@ function createSkillsSyncCommand() {
|
|
|
14813
14917
|
console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
|
|
14814
14918
|
`);
|
|
14815
14919
|
if (options.dryRun) {
|
|
14816
|
-
console.log(
|
|
14920
|
+
console.log(chalk55.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
|
|
14817
14921
|
return;
|
|
14818
14922
|
}
|
|
14819
14923
|
if (!headless) {
|
|
@@ -14822,25 +14926,25 @@ function createSkillsSyncCommand() {
|
|
|
14822
14926
|
default: true
|
|
14823
14927
|
});
|
|
14824
14928
|
if (!confirmed) {
|
|
14825
|
-
console.log(
|
|
14929
|
+
console.log(chalk55.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
|
|
14826
14930
|
process.exit(0);
|
|
14827
14931
|
}
|
|
14828
14932
|
}
|
|
14829
|
-
console.log(
|
|
14933
|
+
console.log(chalk55.cyan("\n\u{1F504} \u0110ang sync...\n"));
|
|
14830
14934
|
const result = await skillsService.syncToIdes(
|
|
14831
14935
|
projectRoot,
|
|
14832
14936
|
selectedIdes,
|
|
14833
14937
|
selectedSlugs,
|
|
14834
14938
|
(res) => {
|
|
14835
14939
|
const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
|
|
14836
|
-
const statusColor = res.status === "error" ?
|
|
14837
|
-
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${
|
|
14940
|
+
const statusColor = res.status === "error" ? chalk55.red : chalk55.green;
|
|
14941
|
+
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk55.dim(res.path)}`);
|
|
14838
14942
|
if (res.status === "error" && res.error) {
|
|
14839
|
-
console.log(` ${
|
|
14943
|
+
console.log(` ${chalk55.red("Error:")} ${res.error}`);
|
|
14840
14944
|
}
|
|
14841
14945
|
}
|
|
14842
14946
|
);
|
|
14843
|
-
console.log(
|
|
14947
|
+
console.log(chalk55.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
|
|
14844
14948
|
console.log(` Created: ${result.created}`);
|
|
14845
14949
|
console.log(` Updated: ${result.updated}`);
|
|
14846
14950
|
if (result.errors > 0) {
|
|
@@ -14853,27 +14957,27 @@ function createSkillsSyncCommand() {
|
|
|
14853
14957
|
// src/commands/skills/index.ts
|
|
14854
14958
|
function showSkillsHelp() {
|
|
14855
14959
|
const cli = getCliName();
|
|
14856
|
-
console.log(
|
|
14960
|
+
console.log(chalk56.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk56.dim(" - Qu\u1EA3n l\xFD agent skills"));
|
|
14857
14961
|
console.log();
|
|
14858
|
-
console.log(
|
|
14859
|
-
console.log(` ${
|
|
14860
|
-
console.log(` ${
|
|
14861
|
-
console.log(` ${
|
|
14862
|
-
console.log(` ${
|
|
14863
|
-
console.log(` ${
|
|
14962
|
+
console.log(chalk56.bold("C\xE1c l\u1EC7nh:"));
|
|
14963
|
+
console.log(` ${chalk56.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
|
|
14964
|
+
console.log(` ${chalk56.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
|
|
14965
|
+
console.log(` ${chalk56.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
|
|
14966
|
+
console.log(` ${chalk56.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
|
|
14967
|
+
console.log(` ${chalk56.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
|
|
14864
14968
|
console.log();
|
|
14865
|
-
console.log(
|
|
14866
|
-
console.log(
|
|
14867
|
-
console.log(
|
|
14868
|
-
console.log(
|
|
14869
|
-
console.log(
|
|
14870
|
-
console.log(
|
|
14871
|
-
console.log(
|
|
14969
|
+
console.log(chalk56.bold("V\xED d\u1EE5:"));
|
|
14970
|
+
console.log(chalk56.dim(` $ ${cli} skills find audit`));
|
|
14971
|
+
console.log(chalk56.dim(` $ ${cli} skills find "react" --skillsh`));
|
|
14972
|
+
console.log(chalk56.dim(` $ ${cli} skills add brainstorming`));
|
|
14973
|
+
console.log(chalk56.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
|
|
14974
|
+
console.log(chalk56.dim(` $ ${cli} skills list`));
|
|
14975
|
+
console.log(chalk56.dim(` $ ${cli} skills sync --all -y`));
|
|
14872
14976
|
console.log();
|
|
14873
|
-
console.log(
|
|
14977
|
+
console.log(chalk56.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
14874
14978
|
}
|
|
14875
14979
|
function createSkillsCommand() {
|
|
14876
|
-
const cmd = new
|
|
14980
|
+
const cmd = new Command92("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
|
|
14877
14981
|
showSkillsHelp();
|
|
14878
14982
|
});
|
|
14879
14983
|
cmd.addCommand(createSkillsFindCommand());
|
|
@@ -14885,7 +14989,7 @@ function createSkillsCommand() {
|
|
|
14885
14989
|
}
|
|
14886
14990
|
|
|
14887
14991
|
// src/commands/upgrade.ts
|
|
14888
|
-
import { Command as
|
|
14992
|
+
import { Command as Command93 } from "commander";
|
|
14889
14993
|
import { confirm as confirm20 } from "@inquirer/prompts";
|
|
14890
14994
|
import { execSync as execSync5 } from "child_process";
|
|
14891
14995
|
var colors2 = {
|
|
@@ -14897,7 +15001,7 @@ var colors2 = {
|
|
|
14897
15001
|
bold: "\x1B[1m"
|
|
14898
15002
|
};
|
|
14899
15003
|
function createUpgradeCommand() {
|
|
14900
|
-
return new
|
|
15004
|
+
return new Command93("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) => {
|
|
14901
15005
|
await handleUpgrade(options);
|
|
14902
15006
|
});
|
|
14903
15007
|
}
|
|
@@ -15050,13 +15154,13 @@ function getInstallCommand(packageManager2) {
|
|
|
15050
15154
|
}
|
|
15051
15155
|
|
|
15052
15156
|
// src/commands/clean.ts
|
|
15053
|
-
import { Command as
|
|
15157
|
+
import { Command as Command94 } from "commander";
|
|
15054
15158
|
import { confirm as confirm21, select as select6 } from "@inquirer/prompts";
|
|
15055
|
-
import { promises as
|
|
15056
|
-
import { join as
|
|
15159
|
+
import { promises as fs27 } from "fs";
|
|
15160
|
+
import { join as join20 } from "path";
|
|
15057
15161
|
import { existsSync as existsSync4 } from "fs";
|
|
15058
15162
|
function createCleanCommand() {
|
|
15059
|
-
return new
|
|
15163
|
+
return new Command94("clean").description("Clean up backups, cache, IDE configs, and .jai1 directory").option("-y, --yes", "Skip confirmation").option("--backups", "Clean only backup files").option("--jai1", "Clean only .jai1/ directory").option("--ide", "Clean only IDE directories (.cursor, .windsurf, .agent, .claude, .opencode)").option("--all", "Clean all (backups + .jai1 + IDE dirs)").action(async (options) => {
|
|
15060
15164
|
await handleClean(options);
|
|
15061
15165
|
});
|
|
15062
15166
|
}
|
|
@@ -15067,7 +15171,7 @@ async function handleClean(options) {
|
|
|
15067
15171
|
{
|
|
15068
15172
|
name: "Backups",
|
|
15069
15173
|
description: "Component backup files (.jai1_backup/)",
|
|
15070
|
-
path:
|
|
15174
|
+
path: join20(cwd, ".jai1_backup"),
|
|
15071
15175
|
check: async () => {
|
|
15072
15176
|
const backups = await service.listBackups(cwd);
|
|
15073
15177
|
return { exists: backups.length > 0, count: backups.length };
|
|
@@ -15079,13 +15183,13 @@ async function handleClean(options) {
|
|
|
15079
15183
|
{
|
|
15080
15184
|
name: "Jai1 Config",
|
|
15081
15185
|
description: "Jai1 framework config (.jai1/)",
|
|
15082
|
-
path:
|
|
15186
|
+
path: join20(cwd, ".jai1"),
|
|
15083
15187
|
check: async () => {
|
|
15084
|
-
const exists = existsSync4(
|
|
15188
|
+
const exists = existsSync4(join20(cwd, ".jai1"));
|
|
15085
15189
|
return { exists };
|
|
15086
15190
|
},
|
|
15087
15191
|
clean: async () => {
|
|
15088
|
-
await
|
|
15192
|
+
await fs27.rm(join20(cwd, ".jai1"), { recursive: true, force: true });
|
|
15089
15193
|
}
|
|
15090
15194
|
}
|
|
15091
15195
|
];
|
|
@@ -15097,7 +15201,7 @@ async function handleClean(options) {
|
|
|
15097
15201
|
{ name: "OpenCode", dir: ".opencode" }
|
|
15098
15202
|
];
|
|
15099
15203
|
for (const ide of ideDirectories) {
|
|
15100
|
-
const idePath =
|
|
15204
|
+
const idePath = join20(cwd, ide.dir);
|
|
15101
15205
|
if (existsSync4(idePath)) {
|
|
15102
15206
|
targets.push({
|
|
15103
15207
|
name: `IDE: ${ide.name}`,
|
|
@@ -15105,7 +15209,7 @@ async function handleClean(options) {
|
|
|
15105
15209
|
path: idePath,
|
|
15106
15210
|
check: async () => ({ exists: true }),
|
|
15107
15211
|
clean: async () => {
|
|
15108
|
-
await
|
|
15212
|
+
await fs27.rm(idePath, { recursive: true, force: true });
|
|
15109
15213
|
}
|
|
15110
15214
|
});
|
|
15111
15215
|
}
|
|
@@ -15216,7 +15320,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
15216
15320
|
}
|
|
15217
15321
|
|
|
15218
15322
|
// src/commands/redmine/check.ts
|
|
15219
|
-
import { Command as
|
|
15323
|
+
import { Command as Command95 } from "commander";
|
|
15220
15324
|
|
|
15221
15325
|
// src/services/redmine-config.service.ts
|
|
15222
15326
|
import { readFile as readFile7 } from "fs/promises";
|
|
@@ -15523,7 +15627,7 @@ async function checkConnectivity(config) {
|
|
|
15523
15627
|
|
|
15524
15628
|
// src/commands/redmine/check.ts
|
|
15525
15629
|
function createRedmineCheckCommand() {
|
|
15526
|
-
const cmd = new
|
|
15630
|
+
const cmd = new Command95("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
|
|
15527
15631
|
await handleRedmineCheck(options);
|
|
15528
15632
|
});
|
|
15529
15633
|
return cmd;
|
|
@@ -15551,7 +15655,7 @@ async function handleRedmineCheck(options) {
|
|
|
15551
15655
|
}
|
|
15552
15656
|
|
|
15553
15657
|
// src/commands/redmine/sync-issue.ts
|
|
15554
|
-
import { Command as
|
|
15658
|
+
import { Command as Command96 } from "commander";
|
|
15555
15659
|
|
|
15556
15660
|
// src/sync-issue.ts
|
|
15557
15661
|
import { resolve as resolve3, relative } from "path";
|
|
@@ -15935,7 +16039,7 @@ function extractIssueIdFromUrl(url) {
|
|
|
15935
16039
|
|
|
15936
16040
|
// src/commands/redmine/sync-issue.ts
|
|
15937
16041
|
function createSyncIssueCommand() {
|
|
15938
|
-
const cmd = new
|
|
16042
|
+
const cmd = new Command96("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) => {
|
|
15939
16043
|
await handleSyncIssue(options);
|
|
15940
16044
|
});
|
|
15941
16045
|
return cmd;
|
|
@@ -15979,7 +16083,7 @@ async function handleSyncIssue(options) {
|
|
|
15979
16083
|
}
|
|
15980
16084
|
|
|
15981
16085
|
// src/commands/redmine/sync-project.ts
|
|
15982
|
-
import { Command as
|
|
16086
|
+
import { Command as Command97 } from "commander";
|
|
15983
16087
|
|
|
15984
16088
|
// src/sync-project.ts
|
|
15985
16089
|
async function syncProject(config, options = {}) {
|
|
@@ -16049,7 +16153,7 @@ async function syncProject(config, options = {}) {
|
|
|
16049
16153
|
|
|
16050
16154
|
// src/commands/redmine/sync-project.ts
|
|
16051
16155
|
function createSyncProjectCommand() {
|
|
16052
|
-
const cmd = new
|
|
16156
|
+
const cmd = new Command97("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) => {
|
|
16053
16157
|
await handleSyncProject(options);
|
|
16054
16158
|
});
|
|
16055
16159
|
return cmd;
|
|
@@ -16104,12 +16208,12 @@ async function handleSyncProject(options) {
|
|
|
16104
16208
|
}
|
|
16105
16209
|
|
|
16106
16210
|
// src/commands/framework/info.ts
|
|
16107
|
-
import { Command as
|
|
16108
|
-
import { promises as
|
|
16109
|
-
import { join as
|
|
16110
|
-
import { homedir as
|
|
16211
|
+
import { Command as Command98 } from "commander";
|
|
16212
|
+
import { promises as fs28 } from "fs";
|
|
16213
|
+
import { join as join21 } from "path";
|
|
16214
|
+
import { homedir as homedir4 } from "os";
|
|
16111
16215
|
function createInfoCommand() {
|
|
16112
|
-
const cmd = new
|
|
16216
|
+
const cmd = new Command98("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
16113
16217
|
await handleInfo(options);
|
|
16114
16218
|
});
|
|
16115
16219
|
return cmd;
|
|
@@ -16120,7 +16224,7 @@ async function handleInfo(options) {
|
|
|
16120
16224
|
if (!config) {
|
|
16121
16225
|
throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
|
|
16122
16226
|
}
|
|
16123
|
-
const frameworkPath =
|
|
16227
|
+
const frameworkPath = join21(homedir4(), ".jai1", "framework");
|
|
16124
16228
|
const projectStatus = await getProjectStatus2();
|
|
16125
16229
|
const info = {
|
|
16126
16230
|
configPath: configService.getConfigPath(),
|
|
@@ -16155,9 +16259,9 @@ function maskKey4(key) {
|
|
|
16155
16259
|
return "****" + key.slice(-4);
|
|
16156
16260
|
}
|
|
16157
16261
|
async function getProjectStatus2() {
|
|
16158
|
-
const projectJai1 =
|
|
16262
|
+
const projectJai1 = join21(process.cwd(), ".jai1");
|
|
16159
16263
|
try {
|
|
16160
|
-
await
|
|
16264
|
+
await fs28.access(projectJai1);
|
|
16161
16265
|
return { exists: true, version: "Synced" };
|
|
16162
16266
|
} catch {
|
|
16163
16267
|
return { exists: false };
|
|
@@ -16165,7 +16269,7 @@ async function getProjectStatus2() {
|
|
|
16165
16269
|
}
|
|
16166
16270
|
|
|
16167
16271
|
// src/commands/self-update.ts
|
|
16168
|
-
import { Command as
|
|
16272
|
+
import { Command as Command99 } from "commander";
|
|
16169
16273
|
import { confirm as confirm22 } from "@inquirer/prompts";
|
|
16170
16274
|
import { execSync as execSync6 } from "child_process";
|
|
16171
16275
|
var colors3 = {
|
|
@@ -16177,7 +16281,7 @@ var colors3 = {
|
|
|
16177
16281
|
bold: "\x1B[1m"
|
|
16178
16282
|
};
|
|
16179
16283
|
function createSelfUpdateCommand() {
|
|
16180
|
-
return new
|
|
16284
|
+
return new Command99("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) => {
|
|
16181
16285
|
await handleSelfUpdate(options);
|
|
16182
16286
|
});
|
|
16183
16287
|
}
|
|
@@ -16317,10 +16421,10 @@ function getInstallCommand2(packageManager2) {
|
|
|
16317
16421
|
}
|
|
16318
16422
|
|
|
16319
16423
|
// src/commands/clear-backups.ts
|
|
16320
|
-
import { Command as
|
|
16424
|
+
import { Command as Command100 } from "commander";
|
|
16321
16425
|
import { confirm as confirm23 } from "@inquirer/prompts";
|
|
16322
16426
|
function createClearBackupsCommand() {
|
|
16323
|
-
return new
|
|
16427
|
+
return new Command100("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
16324
16428
|
const service = new ComponentsService();
|
|
16325
16429
|
const backups = await service.listBackups(process.cwd());
|
|
16326
16430
|
if (backups.length === 0) {
|
|
@@ -16345,9 +16449,9 @@ function createClearBackupsCommand() {
|
|
|
16345
16449
|
}
|
|
16346
16450
|
|
|
16347
16451
|
// src/commands/vscode/index.ts
|
|
16348
|
-
import { Command as
|
|
16452
|
+
import { Command as Command101 } from "commander";
|
|
16349
16453
|
import { checkbox as checkbox9, confirm as confirm24, select as select7 } from "@inquirer/prompts";
|
|
16350
|
-
import
|
|
16454
|
+
import fs29 from "fs/promises";
|
|
16351
16455
|
import path12 from "path";
|
|
16352
16456
|
import { existsSync as existsSync5 } from "fs";
|
|
16353
16457
|
var PERFORMANCE_GROUPS2 = {
|
|
@@ -16485,7 +16589,7 @@ var PERFORMANCE_GROUPS2 = {
|
|
|
16485
16589
|
}
|
|
16486
16590
|
};
|
|
16487
16591
|
function createVSCodeCommand() {
|
|
16488
|
-
const vscodeCommand = new
|
|
16592
|
+
const vscodeCommand = new Command101("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
|
|
16489
16593
|
vscodeCommand.action(async () => {
|
|
16490
16594
|
await interactiveMode2();
|
|
16491
16595
|
});
|
|
@@ -16580,13 +16684,13 @@ async function applyGroups2(groupKeys, action) {
|
|
|
16580
16684
|
return;
|
|
16581
16685
|
}
|
|
16582
16686
|
if (!existsSync5(vscodeDir)) {
|
|
16583
|
-
await
|
|
16687
|
+
await fs29.mkdir(vscodeDir, { recursive: true });
|
|
16584
16688
|
console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
|
|
16585
16689
|
}
|
|
16586
16690
|
let currentSettings = {};
|
|
16587
16691
|
if (existsSync5(settingsPath)) {
|
|
16588
16692
|
try {
|
|
16589
|
-
const content = await
|
|
16693
|
+
const content = await fs29.readFile(settingsPath, "utf-8");
|
|
16590
16694
|
currentSettings = JSON.parse(content);
|
|
16591
16695
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
16592
16696
|
} catch {
|
|
@@ -16626,7 +16730,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
16626
16730
|
}
|
|
16627
16731
|
}
|
|
16628
16732
|
}
|
|
16629
|
-
await
|
|
16733
|
+
await fs29.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
16630
16734
|
console.log(`
|
|
16631
16735
|
\u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
|
|
16632
16736
|
console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
|
|
@@ -16647,7 +16751,7 @@ async function resetSettings2(groupKeys) {
|
|
|
16647
16751
|
return;
|
|
16648
16752
|
}
|
|
16649
16753
|
if (groupKeys.length === 0) {
|
|
16650
|
-
await
|
|
16754
|
+
await fs29.unlink(settingsPath);
|
|
16651
16755
|
console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
|
|
16652
16756
|
} else {
|
|
16653
16757
|
await applyGroups2(groupKeys, "disable");
|
|
@@ -16656,10 +16760,10 @@ async function resetSettings2(groupKeys) {
|
|
|
16656
16760
|
}
|
|
16657
16761
|
|
|
16658
16762
|
// src/commands/migrate-ide.ts
|
|
16659
|
-
import { Command as
|
|
16763
|
+
import { Command as Command102 } from "commander";
|
|
16660
16764
|
import { checkbox as checkbox10, confirm as confirm25 } from "@inquirer/prompts";
|
|
16661
16765
|
function createMigrateIdeCommand() {
|
|
16662
|
-
const cmd = new
|
|
16766
|
+
const cmd = new Command102("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) => {
|
|
16663
16767
|
await runMigrateIde(options);
|
|
16664
16768
|
});
|
|
16665
16769
|
return cmd;
|
|
@@ -16768,20 +16872,20 @@ async function runMigrateIde(options) {
|
|
|
16768
16872
|
|
|
16769
16873
|
// src/utils/help-formatter.ts
|
|
16770
16874
|
import boxen4 from "boxen";
|
|
16771
|
-
import
|
|
16875
|
+
import chalk57 from "chalk";
|
|
16772
16876
|
import gradient from "gradient-string";
|
|
16773
16877
|
import figlet from "figlet";
|
|
16774
16878
|
function showCustomHelp(version) {
|
|
16775
16879
|
const title = figlet.textSync("JAI1", { font: "Small" });
|
|
16776
16880
|
console.log(gradient.pastel(title));
|
|
16777
16881
|
console.log(
|
|
16778
|
-
boxen4(
|
|
16882
|
+
boxen4(chalk57.cyan(`Agentic Coding CLI v${version}`), {
|
|
16779
16883
|
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
|
16780
16884
|
borderStyle: "round",
|
|
16781
16885
|
borderColor: "cyan"
|
|
16782
16886
|
})
|
|
16783
16887
|
);
|
|
16784
|
-
console.log(
|
|
16888
|
+
console.log(chalk57.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
|
|
16785
16889
|
console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
|
|
16786
16890
|
console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
|
|
16787
16891
|
console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
|
|
@@ -16789,43 +16893,43 @@ function showCustomHelp(version) {
|
|
|
16789
16893
|
console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
|
|
16790
16894
|
console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
|
|
16791
16895
|
console.log(" doctor Chu\u1EA9n \u0111o\xE1n project hi\u1EC7n t\u1EA1i");
|
|
16792
|
-
console.log(
|
|
16896
|
+
console.log(chalk57.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
|
|
16793
16897
|
console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
|
|
16794
16898
|
console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
|
|
16795
16899
|
console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
|
|
16796
|
-
console.log(
|
|
16900
|
+
console.log(chalk57.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
|
|
16797
16901
|
console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
|
|
16798
16902
|
console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
|
|
16799
16903
|
console.log(" openai-keys Th\xF4ng tin API credentials");
|
|
16800
|
-
console.log(
|
|
16904
|
+
console.log(chalk57.bold("\n\u{1F916} AI Tools"));
|
|
16801
16905
|
console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
|
|
16802
16906
|
console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
|
|
16803
16907
|
console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
|
|
16804
16908
|
console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
|
|
16805
|
-
console.log(
|
|
16909
|
+
console.log(chalk57.bold("\n\u{1F4C1} Project"));
|
|
16806
16910
|
console.log(" kit Qu\u1EA3n l\xFD starter kits");
|
|
16807
16911
|
console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
|
|
16808
16912
|
console.log(" rules Qu\u1EA3n l\xFD rule presets");
|
|
16809
16913
|
console.log(" deps Qu\u1EA3n l\xFD dependencies");
|
|
16810
16914
|
console.log(" redmine Redmine context sync");
|
|
16811
|
-
console.log(
|
|
16915
|
+
console.log(chalk57.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
|
|
16812
16916
|
console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
|
|
16813
16917
|
console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
|
|
16814
16918
|
console.log(" utils Developer utilities");
|
|
16815
16919
|
const name = getCliName();
|
|
16816
|
-
console.log(
|
|
16920
|
+
console.log(chalk57.dim(`
|
|
16817
16921
|
S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
16818
16922
|
}
|
|
16819
16923
|
function showUnknownCommand(commandName) {
|
|
16820
|
-
console.error(
|
|
16924
|
+
console.error(chalk57.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
|
|
16821
16925
|
const name = getCliName();
|
|
16822
|
-
console.error(
|
|
16926
|
+
console.error(chalk57.dim(`
|
|
16823
16927
|
G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
|
|
16824
16928
|
}
|
|
16825
16929
|
|
|
16826
16930
|
// src/cli.ts
|
|
16827
16931
|
checkNodeVersion();
|
|
16828
|
-
var program = new
|
|
16932
|
+
var program = new Command103();
|
|
16829
16933
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
16830
16934
|
console.log(package_default.version);
|
|
16831
16935
|
if (!process.argv.includes("--skip-update-check")) {
|
|
@@ -16861,6 +16965,7 @@ program.addCommand(createErrorsCommand());
|
|
|
16861
16965
|
program.addCommand(createUtilsCommand());
|
|
16862
16966
|
program.addCommand(createDepsCommand());
|
|
16863
16967
|
program.addCommand(createDevCommand());
|
|
16968
|
+
program.addCommand(createSettingsCommand());
|
|
16864
16969
|
program.addCommand(createTasksCommand());
|
|
16865
16970
|
program.addCommand(createKitCommand());
|
|
16866
16971
|
program.addCommand(createRulesCommand());
|
|
@@ -16868,9 +16973,9 @@ program.addCommand(createSkillsCommand());
|
|
|
16868
16973
|
program.addCommand(createHooksCommand());
|
|
16869
16974
|
program.addCommand(createUpgradeCommand());
|
|
16870
16975
|
program.addCommand(createCleanCommand());
|
|
16871
|
-
var redmineCommand = new
|
|
16976
|
+
var redmineCommand = new Command103("redmine").description("Redmine context sync commands");
|
|
16872
16977
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
16873
|
-
var syncCommand = new
|
|
16978
|
+
var syncCommand = new Command103("sync").description("Sync Redmine issues to markdown files");
|
|
16874
16979
|
syncCommand.addCommand(createSyncIssueCommand());
|
|
16875
16980
|
syncCommand.addCommand(createSyncProjectCommand());
|
|
16876
16981
|
redmineCommand.addCommand(syncCommand);
|