@ai-setting/roy-agent-cli 1.0.0

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.
Files changed (158) hide show
  1. package/README.md +126 -0
  2. package/dist/bin/roy.js +127297 -0
  3. package/dist/roy-agent-darwin-arm64/bin/roy.js +127297 -0
  4. package/dist/roy-agent-darwin-x64/bin/roy.js +127297 -0
  5. package/dist/roy-agent-linux-arm64/bin/roy.js +127297 -0
  6. package/dist/roy-agent-linux-x64/bin/roy.js +127297 -0
  7. package/dist/roy-agent-windows-x64/bin/roy.js +127297 -0
  8. package/package.json +91 -0
  9. package/src/bin/roy.ts +12 -0
  10. package/src/cli.ts +101 -0
  11. package/src/commands/act.ts +480 -0
  12. package/src/commands/commands-add.ts +110 -0
  13. package/src/commands/commands-dirs.ts +70 -0
  14. package/src/commands/commands-info.ts +90 -0
  15. package/src/commands/commands-list.ts +161 -0
  16. package/src/commands/commands-remove.ts +147 -0
  17. package/src/commands/commands.ts +55 -0
  18. package/src/commands/config/config-service.test.ts +449 -0
  19. package/src/commands/config/config-service.ts +312 -0
  20. package/src/commands/config/deep-merge.test.ts +168 -0
  21. package/src/commands/config/deep-merge.ts +63 -0
  22. package/src/commands/config/export.ts +97 -0
  23. package/src/commands/config/filter-history-e2e.test.ts +141 -0
  24. package/src/commands/config/import-preserve-refs.test.ts +212 -0
  25. package/src/commands/config/import.ts +119 -0
  26. package/src/commands/config/index.ts +35 -0
  27. package/src/commands/config/list.ts +281 -0
  28. package/src/commands/config/roy-config-e2e.test.ts +297 -0
  29. package/src/commands/config/types.ts +54 -0
  30. package/src/commands/debug/index.ts +38 -0
  31. package/src/commands/debug/log.test.ts +233 -0
  32. package/src/commands/debug/log.ts +123 -0
  33. package/src/commands/debug/span.test.ts +297 -0
  34. package/src/commands/debug/span.ts +211 -0
  35. package/src/commands/debug/trace.test.ts +254 -0
  36. package/src/commands/debug/trace.ts +140 -0
  37. package/src/commands/eventsource/add.ts +133 -0
  38. package/src/commands/eventsource/index.ts +48 -0
  39. package/src/commands/eventsource/list.ts +194 -0
  40. package/src/commands/eventsource/remove.ts +95 -0
  41. package/src/commands/eventsource/start.ts +103 -0
  42. package/src/commands/eventsource/status.ts +185 -0
  43. package/src/commands/eventsource/stop.ts +89 -0
  44. package/src/commands/index.ts +22 -0
  45. package/src/commands/input-handler.test.ts +76 -0
  46. package/src/commands/input-handler.ts +43 -0
  47. package/src/commands/interactive-esc.test.ts +254 -0
  48. package/src/commands/interactive.shutdown.test.ts +122 -0
  49. package/src/commands/interactive.test.ts +221 -0
  50. package/src/commands/interactive.ts +1015 -0
  51. package/src/commands/lsp/check.ts +92 -0
  52. package/src/commands/lsp/index.ts +32 -0
  53. package/src/commands/lsp/install.ts +126 -0
  54. package/src/commands/lsp/list.ts +64 -0
  55. package/src/commands/mcp/index.ts +27 -0
  56. package/src/commands/mcp/list.ts +116 -0
  57. package/src/commands/mcp/reload.ts +70 -0
  58. package/src/commands/mcp/tools.ts +121 -0
  59. package/src/commands/memory/extract-e2e.test.ts +388 -0
  60. package/src/commands/memory/index.ts +11 -0
  61. package/src/commands/memory/memory-simplified.test.ts +58 -0
  62. package/src/commands/memory/memory.ts +25 -0
  63. package/src/commands/memory/organize.ts +300 -0
  64. package/src/commands/memory/recall.test.ts +120 -0
  65. package/src/commands/memory/recall.ts +88 -0
  66. package/src/commands/memory/record-extract-handle-query.test.ts +385 -0
  67. package/src/commands/memory/record-prompt-component.test.ts +343 -0
  68. package/src/commands/memory/record.test.ts +92 -0
  69. package/src/commands/memory/record.ts +332 -0
  70. package/src/commands/plugin.test.ts +292 -0
  71. package/src/commands/plugin.ts +267 -0
  72. package/src/commands/sessions/active.ts +96 -0
  73. package/src/commands/sessions/add-message.ts +96 -0
  74. package/src/commands/sessions/checkpoints.ts +154 -0
  75. package/src/commands/sessions/compact.test.ts +215 -0
  76. package/src/commands/sessions/compact.ts +269 -0
  77. package/src/commands/sessions/delete.ts +236 -0
  78. package/src/commands/sessions/get.ts +165 -0
  79. package/src/commands/sessions/grep.ts +233 -0
  80. package/src/commands/sessions/index.ts +95 -0
  81. package/src/commands/sessions/list.ts +210 -0
  82. package/src/commands/sessions/messages.test.ts +333 -0
  83. package/src/commands/sessions/messages.ts +248 -0
  84. package/src/commands/sessions/mock.ts +194 -0
  85. package/src/commands/sessions/new.ts +82 -0
  86. package/src/commands/sessions/rename.ts +98 -0
  87. package/src/commands/shared/event-handler.ts +213 -0
  88. package/src/commands/shared/event-message-formatter.ts +295 -0
  89. package/src/commands/shared/index.ts +11 -0
  90. package/src/commands/shared/query-executor.test.ts +434 -0
  91. package/src/commands/shared/query-executor.ts +324 -0
  92. package/src/commands/shared/repl-engine.test.ts +354 -0
  93. package/src/commands/shared/session-manager.test.ts +212 -0
  94. package/src/commands/shared/session-manager.ts +114 -0
  95. package/src/commands/skills/get.ts +90 -0
  96. package/src/commands/skills/index.ts +39 -0
  97. package/src/commands/skills/list.ts +129 -0
  98. package/src/commands/skills/reload.ts +59 -0
  99. package/src/commands/skills/search.ts +132 -0
  100. package/src/commands/skills/show-config.ts +93 -0
  101. package/src/commands/tasks/complete.ts +92 -0
  102. package/src/commands/tasks/create.ts +118 -0
  103. package/src/commands/tasks/delete.ts +86 -0
  104. package/src/commands/tasks/get.ts +116 -0
  105. package/src/commands/tasks/index.ts +53 -0
  106. package/src/commands/tasks/list.ts +140 -0
  107. package/src/commands/tasks/operations.ts +120 -0
  108. package/src/commands/tasks/update.ts +122 -0
  109. package/src/commands/tools/exec-tool.ts +128 -0
  110. package/src/commands/tools/get.ts +114 -0
  111. package/src/commands/tools/index.ts +35 -0
  112. package/src/commands/tools/list.ts +107 -0
  113. package/src/commands/tools/shared/index.ts +7 -0
  114. package/src/commands/tools/shared/schema-helper.ts +111 -0
  115. package/src/commands/workflow/commands/add.ts +315 -0
  116. package/src/commands/workflow/commands/get.ts +193 -0
  117. package/src/commands/workflow/commands/list.ts +137 -0
  118. package/src/commands/workflow/commands/nodes.ts +528 -0
  119. package/src/commands/workflow/commands/remove.ts +94 -0
  120. package/src/commands/workflow/commands/run.ts +398 -0
  121. package/src/commands/workflow/commands/status.ts +147 -0
  122. package/src/commands/workflow/commands/stop.ts +91 -0
  123. package/src/commands/workflow/commands/update.ts +130 -0
  124. package/src/commands/workflow/commands/validate.ts +139 -0
  125. package/src/commands/workflow/commands/workflow-cli.test.ts +196 -0
  126. package/src/commands/workflow/index.ts +65 -0
  127. package/src/commands/workflow/renderers.ts +358 -0
  128. package/src/commands/workflow/validators/index.ts +8 -0
  129. package/src/commands/workflow/validators/node-validator-factory.ts +40 -0
  130. package/src/commands/workflow/validators/node-validator.ts +125 -0
  131. package/src/commands/workflow/validators/nodes/agent-node-validator.ts +58 -0
  132. package/src/commands/workflow/validators/nodes/condition-node-validator.ts +34 -0
  133. package/src/commands/workflow/validators/nodes/decorator-node-validator.ts +45 -0
  134. package/src/commands/workflow/validators/nodes/merge-node-validator.ts +46 -0
  135. package/src/commands/workflow/validators/nodes/skill-node-validator.ts +33 -0
  136. package/src/commands/workflow/validators/nodes/tool-node-validator.ts +54 -0
  137. package/src/commands/workflow/validators/nodes/workflow-node-validator.ts +33 -0
  138. package/src/commands/workflow/validators/types.ts +78 -0
  139. package/src/commands/workflow/validators/workflow-validator.test.ts +273 -0
  140. package/src/commands/workflow/validators/workflow-validator.ts +320 -0
  141. package/src/index.ts +19 -0
  142. package/src/plugin/apply.ts +103 -0
  143. package/src/plugin/discover.ts +219 -0
  144. package/src/plugin/index.ts +45 -0
  145. package/src/plugin/registry.ts +272 -0
  146. package/src/plugin/types.ts +165 -0
  147. package/src/services/context-handler.service.test.ts +501 -0
  148. package/src/services/context-handler.service.ts +372 -0
  149. package/src/services/environment.service.commands-prompt.test.ts +167 -0
  150. package/src/services/environment.service.ts +656 -0
  151. package/src/services/output.service.test.ts +92 -0
  152. package/src/services/output.service.ts +122 -0
  153. package/src/services/quiet-mode.service.test.ts +114 -0
  154. package/src/services/quiet-mode.service.ts +81 -0
  155. package/src/services/stream-output.service.test.ts +214 -0
  156. package/src/services/stream-output.service.ts +323 -0
  157. package/src/util/which.test.ts +101 -0
  158. package/src/util/which.ts +55 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @fileoverview Commands Add Command
3
+ *
4
+ * 添加收藏命令
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../services/environment.service";
9
+ import { OutputService } from "../services/output.service";
10
+ import chalk from "chalk";
11
+ import type { CommandsComponent } from "@ai-setting/roy-agent-core";
12
+
13
+ export interface CommandsAddOptions {
14
+ name: string;
15
+ target: string;
16
+ global: boolean;
17
+ local: boolean;
18
+ description: string;
19
+ tips: string;
20
+ config?: string;
21
+ }
22
+
23
+ export const CommandsAddCommand: CommandModule<object, CommandsAddOptions> = {
24
+ command: "add <name> <target>",
25
+ describe: "添加收藏命令(自动创建 symlink)",
26
+
27
+ builder: (yargs) =>
28
+ yargs
29
+ .positional("name", {
30
+ describe: "命令名称",
31
+ type: "string",
32
+ demandOption: true,
33
+ })
34
+ .positional("target", {
35
+ describe: "目标命令路径或名称",
36
+ type: "string",
37
+ demandOption: true,
38
+ })
39
+ .option("global", {
40
+ alias: "g",
41
+ describe: "添加到用户级别目录(默认)",
42
+ type: "boolean",
43
+ default: true,
44
+ })
45
+ .option("local", {
46
+ alias: "l",
47
+ describe: "添加到项目级别目录",
48
+ type: "boolean",
49
+ default: false,
50
+ })
51
+ .option("description", {
52
+ alias: "d",
53
+ describe: "命令功能描述(用于 AI Prompt 注入)",
54
+ type: "string",
55
+ demandOption: true,
56
+ })
57
+ .option("tips", {
58
+ alias: "t",
59
+ describe: "探索提示(告诉用户如何使用 --help 探索)",
60
+ type: "string",
61
+ demandOption: true,
62
+ })
63
+ .option("config", {
64
+ describe: "配置文件路径",
65
+ type: "string",
66
+ }),
67
+
68
+ async handler(args) {
69
+ const output = new OutputService();
70
+ const envService = new EnvironmentService(output);
71
+
72
+ try {
73
+ await envService.create({ configPath: args.config });
74
+ const env = envService.getEnvironment();
75
+ if (!env) {
76
+ output.error("Failed to create environment");
77
+ process.exit(1);
78
+ }
79
+ const commandsComponent = env.getComponent("commands") as CommandsComponent | undefined;
80
+
81
+ if (!commandsComponent) {
82
+ output.error("CommandsComponent not available");
83
+ process.exit(1);
84
+ }
85
+
86
+ const source = args.local ? "project" : "user";
87
+
88
+ await commandsComponent.add({
89
+ name: args.name,
90
+ target: args.target,
91
+ source,
92
+ description: args.description,
93
+ tips: args.tips,
94
+ });
95
+
96
+ const dirs = await commandsComponent.getCommandDirs();
97
+ const targetDir = source === "user" ? dirs.user : dirs.project;
98
+
99
+ output.log(chalk.green(`✅ 已添加命令 '${args.name}'`));
100
+ output.log(chalk.gray(` 目标: ${args.target}`));
101
+ output.log(chalk.gray(` 位置: ${targetDir}/${args.name}`));
102
+ output.log(chalk.gray(` 级别: ${source === "user" ? "USER" : "PROJECT"}`));
103
+ } catch (error: any) {
104
+ output.error(`添加失败: ${error.message}`);
105
+ process.exit(1);
106
+ } finally {
107
+ await envService.dispose();
108
+ }
109
+ },
110
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview Commands Dirs Command
3
+ *
4
+ * 显示命令目录
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../services/environment.service";
9
+ import { OutputService } from "../services/output.service";
10
+ import chalk from "chalk";
11
+ import type { CommandsComponent } from "@ai-setting/roy-agent-core";
12
+
13
+ export interface CommandsDirsOptions {
14
+ json?: boolean;
15
+ config?: string;
16
+ }
17
+
18
+ export const CommandsDirsCommand: CommandModule<object, CommandsDirsOptions> = {
19
+ command: "dirs",
20
+ describe: "显示命令目录",
21
+
22
+ builder: (yargs) =>
23
+ yargs
24
+ .option("json", {
25
+ alias: "j",
26
+ describe: "JSON 格式输出",
27
+ type: "boolean",
28
+ default: false,
29
+ })
30
+ .option("config", {
31
+ describe: "配置文件路径",
32
+ type: "string",
33
+ }),
34
+
35
+ async handler(args) {
36
+ const output = new OutputService();
37
+ const envService = new EnvironmentService(output);
38
+
39
+ try {
40
+ await envService.create({ configPath: args.config });
41
+ const env = envService.getEnvironment();
42
+ if (!env) {
43
+ output.error("Failed to create environment");
44
+ process.exit(1);
45
+ }
46
+ const commandsComponent = env.getComponent("commands") as CommandsComponent | undefined;
47
+
48
+ if (!commandsComponent) {
49
+ output.error("CommandsComponent not available");
50
+ process.exit(1);
51
+ }
52
+
53
+ const dirs = await commandsComponent.getCommandDirs();
54
+
55
+ if (args.json) {
56
+ output.json(dirs);
57
+ } else {
58
+ output.log(chalk.bold("命令目录:"));
59
+ output.log();
60
+ output.log(chalk.cyan("USER:") + ` ${dirs.user}`);
61
+ output.log(chalk.cyan("PROJECT:") + ` ${dirs.project}`);
62
+ }
63
+ } catch (error: any) {
64
+ output.error(`错误: ${error.message}`);
65
+ process.exit(1);
66
+ } finally {
67
+ await envService.dispose();
68
+ }
69
+ },
70
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @fileoverview Commands Info Command
3
+ *
4
+ * 显示命令详细信息
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../services/environment.service";
9
+ import { OutputService } from "../services/output.service";
10
+ import chalk from "chalk";
11
+ import { exec } from "child_process";
12
+ import { promisify } from "util";
13
+ import type { CommandsComponent } from "@ai-setting/roy-agent-core";
14
+
15
+ const execAsync = promisify(exec);
16
+
17
+ export interface CommandsInfoOptions {
18
+ name: string;
19
+ config?: string;
20
+ }
21
+
22
+ export const CommandsInfoCommand: CommandModule<object, CommandsInfoOptions> = {
23
+ command: "info <name>",
24
+ describe: "显示命令详细信息",
25
+
26
+ builder: (yargs) =>
27
+ yargs
28
+ .positional("name", {
29
+ describe: "命令名称",
30
+ type: "string",
31
+ demandOption: true,
32
+ })
33
+ .option("config", {
34
+ describe: "配置文件路径",
35
+ type: "string",
36
+ }),
37
+
38
+ async handler(args) {
39
+ const output = new OutputService();
40
+ const envService = new EnvironmentService(output);
41
+
42
+ try {
43
+ await envService.create({ configPath: args.config });
44
+ const env = envService.getEnvironment();
45
+ if (!env) {
46
+ output.error("Failed to create environment");
47
+ process.exit(1);
48
+ }
49
+ const commandsComponent = env.getComponent("commands") as CommandsComponent | undefined;
50
+
51
+ if (!commandsComponent) {
52
+ output.error("CommandsComponent not available");
53
+ process.exit(1);
54
+ }
55
+
56
+ const info = await commandsComponent.getInfo(args.name);
57
+
58
+ if (!info) {
59
+ output.error(`命令 '${args.name}' 不存在`);
60
+ process.exit(1);
61
+ }
62
+
63
+ output.log(chalk.bold("Name:") + ` ${info.name}`);
64
+ output.log(chalk.bold("Path:") + ` ${info.path}`);
65
+ output.log(chalk.bold("Source:") + ` ${info.source.toUpperCase()}`);
66
+ output.log(chalk.bold("Description:") + ` ${info.shortDescription || "-"}`);
67
+ output.log();
68
+ output.log(chalk.gray("─".repeat(50)));
69
+ output.log();
70
+
71
+ // 显示完整 --help
72
+ try {
73
+ const { stdout } = await execAsync(`${info.path} --help`, { timeout: 5000 });
74
+ output.log(stdout);
75
+ } catch {
76
+ try {
77
+ const { stdout } = await execAsync(`${info.path} -h`, { timeout: 5000 });
78
+ output.log(stdout);
79
+ } catch {
80
+ output.log(chalk.gray("(无法获取帮助信息)"));
81
+ }
82
+ }
83
+ } catch (error: any) {
84
+ output.error(`错误: ${error.message}`);
85
+ process.exit(1);
86
+ } finally {
87
+ await envService.dispose();
88
+ }
89
+ },
90
+ };
@@ -0,0 +1,161 @@
1
+ /**
2
+ * @fileoverview Commands List Command
3
+ *
4
+ * 列出收藏的命令
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../services/environment.service";
9
+ import { OutputService } from "../services/output.service";
10
+ import chalk from "chalk";
11
+ import type { CommandsComponent } from "@ai-setting/roy-agent-core";
12
+
13
+ export interface CommandsListOptions {
14
+ pattern?: string;
15
+ json?: boolean;
16
+ config?: string;
17
+ }
18
+
19
+ interface CommandRow {
20
+ name: string;
21
+ path: string;
22
+ source: string;
23
+ description: string;
24
+ }
25
+
26
+ /**
27
+ * 计算字符串的视觉宽度(中文字符占2格)
28
+ */
29
+ function visualWidth(str: string): number {
30
+ let width = 0;
31
+ for (const char of str) {
32
+ if (char.charCodeAt(0) > 255) {
33
+ width += 2;
34
+ } else {
35
+ width += 1;
36
+ }
37
+ }
38
+ return width;
39
+ }
40
+
41
+ /**
42
+ * 按视觉宽度截断字符串
43
+ */
44
+ function truncateVisual(str: string, maxWidth: number): string {
45
+ let result = "";
46
+ let width = 0;
47
+ for (const char of str) {
48
+ const charWidth = char.charCodeAt(0) > 255 ? 2 : 1;
49
+ if (width + charWidth > maxWidth) break;
50
+ result += char;
51
+ width += charWidth;
52
+ }
53
+ return result;
54
+ }
55
+
56
+ /**
57
+ * 格式化命令表格
58
+ */
59
+ function formatCommandsTable(commands: CommandRow[]): string {
60
+ if (commands.length === 0) {
61
+ return chalk.yellow("命令目录为空,使用 'roy commands add' 添加命令");
62
+ }
63
+
64
+ const NAME_WIDTH = 20;
65
+ const SOURCE_WIDTH = 10;
66
+ const DESC_WIDTH = 50;
67
+ const GAP = " ";
68
+
69
+ const headerLine = [
70
+ chalk.bold("NAME".padEnd(NAME_WIDTH)),
71
+ chalk.bold("SOURCE".padEnd(SOURCE_WIDTH)),
72
+ chalk.bold("DESCRIPTION"),
73
+ ].join(GAP);
74
+
75
+ const sepLine = "─".repeat(NAME_WIDTH + SOURCE_WIDTH + DESC_WIDTH + GAP.length * 2);
76
+
77
+ const formatRow = (cmd: CommandRow): string => {
78
+ const name = truncateVisual(cmd.name, NAME_WIDTH).padEnd(NAME_WIDTH);
79
+ const source = cmd.source.padEnd(SOURCE_WIDTH);
80
+ const desc = truncateVisual(cmd.description || "-", DESC_WIDTH);
81
+ return `${name}${GAP}${source}${GAP}${desc}`;
82
+ };
83
+
84
+ const rows = commands.map(formatRow);
85
+ return [headerLine, sepLine, ...rows].join("\n");
86
+ }
87
+
88
+ export const CommandsListCommand: CommandModule<object, CommandsListOptions> = {
89
+ command: "list [pattern]",
90
+ describe: "列出收藏的命令(支持 glob 过滤)",
91
+
92
+ builder: (yargs) =>
93
+ yargs
94
+ .positional("pattern", {
95
+ describe: "glob 过滤模式",
96
+ type: "string",
97
+ })
98
+ .option("json", {
99
+ alias: "j",
100
+ describe: "JSON 格式输出",
101
+ type: "boolean",
102
+ default: false,
103
+ })
104
+ .option("config", {
105
+ describe: "配置文件路径",
106
+ type: "string",
107
+ }),
108
+
109
+ async handler(args) {
110
+ const output = new OutputService();
111
+ const envService = new EnvironmentService(output);
112
+
113
+ try {
114
+ await envService.create({ configPath: args.config });
115
+ const env = envService.getEnvironment();
116
+ if (!env) {
117
+ output.error("Failed to create environment");
118
+ process.exit(1);
119
+ }
120
+ const commandsComponent = env.getComponent("commands") as CommandsComponent | undefined;
121
+
122
+ if (!commandsComponent) {
123
+ output.error("CommandsComponent not available");
124
+ process.exit(1);
125
+ }
126
+
127
+ const result = await commandsComponent.discover({ pattern: args.pattern });
128
+
129
+ if (args.json) {
130
+ output.json({
131
+ commands: result.commands.map((cmd) => ({
132
+ name: cmd.name,
133
+ path: cmd.path,
134
+ source: cmd.source,
135
+ description: cmd.shortDescription,
136
+ })),
137
+ stats: result.stats,
138
+ });
139
+ } else {
140
+ const rows: CommandRow[] = result.commands.map((cmd) => ({
141
+ name: cmd.name,
142
+ path: cmd.path,
143
+ source: cmd.source === "user" ? "USER" : "PROJECT",
144
+ description: cmd.shortDescription || "-",
145
+ }));
146
+
147
+ output.log(formatCommandsTable(rows));
148
+ output.info("");
149
+ output.log(
150
+ chalk.green(`✅ 共 ${result.commands.length} 个命令`) +
151
+ chalk.gray(` (user: ${result.stats.userCount}, project: ${result.stats.projectCount})`)
152
+ );
153
+ }
154
+ } catch (error) {
155
+ output.error(`Failed to list commands: ${error}`);
156
+ process.exit(1);
157
+ } finally {
158
+ await envService.dispose();
159
+ }
160
+ },
161
+ };
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @fileoverview Commands Remove Command
3
+ *
4
+ * 移除收藏命令
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../services/environment.service";
9
+ import { OutputService } from "../services/output.service";
10
+ import chalk from "chalk";
11
+ import type { CommandsComponent } from "@ai-setting/roy-agent-core";
12
+
13
+ export interface CommandsRemoveOptions {
14
+ name: string;
15
+ global: boolean;
16
+ local: boolean;
17
+ config?: string;
18
+ }
19
+
20
+ export const CommandsRemoveCommand: CommandModule<object, CommandsRemoveOptions> = {
21
+ command: "remove <name>",
22
+ aliases: ["rm"],
23
+ describe: "移除收藏命令",
24
+
25
+ builder: (yargs) =>
26
+ yargs
27
+ .positional("name", {
28
+ describe: "命令名称",
29
+ type: "string",
30
+ demandOption: true,
31
+ })
32
+ .option("global", {
33
+ alias: "g",
34
+ describe: "从用户级别目录移除",
35
+ type: "boolean",
36
+ default: false,
37
+ })
38
+ .option("local", {
39
+ alias: "l",
40
+ describe: "从项目级别目录移除",
41
+ type: "boolean",
42
+ default: false,
43
+ })
44
+ .option("config", {
45
+ describe: "配置文件路径",
46
+ type: "string",
47
+ }),
48
+
49
+ async handler(args) {
50
+ const output = new OutputService();
51
+ const envService = new EnvironmentService(output);
52
+
53
+ try {
54
+ await envService.create({ configPath: args.config });
55
+ const env = envService.getEnvironment();
56
+ if (!env) {
57
+ output.error("Failed to create environment");
58
+ process.exit(1);
59
+ }
60
+ const commandsComponent = env.getComponent("commands") as CommandsComponent | undefined;
61
+
62
+ if (!commandsComponent) {
63
+ output.error("CommandsComponent not available");
64
+ process.exit(1);
65
+ }
66
+
67
+ // 统一移除逻辑:
68
+ // - 如果指定了 --global,从 user 目录移除
69
+ // - 如果指定了 --local,从 project 目录移除
70
+ // - 如果都没指定,同时尝试从两个目录移除
71
+ if (args.global) {
72
+ // 从 user 目录移除
73
+ try {
74
+ await commandsComponent.remove({
75
+ name: args.name,
76
+ source: "user",
77
+ });
78
+ output.log(chalk.green(`✅ 已从用户目录移除命令 '${args.name}'`));
79
+ } catch (error: any) {
80
+ // 忽略"命令不存在"的错误
81
+ if (!error.message?.includes("不存在")) {
82
+ throw error;
83
+ }
84
+ output.warn(`命令 '${args.name}' 不存在于用户目录`);
85
+ }
86
+ } else if (args.local) {
87
+ // 从 project 目录移除
88
+ try {
89
+ await commandsComponent.remove({
90
+ name: args.name,
91
+ source: "project",
92
+ });
93
+ output.log(chalk.green(`✅ 已从项目目录移除命令 '${args.name}'`));
94
+ } catch (error: any) {
95
+ // 忽略"命令不存在"的错误
96
+ if (!error.message?.includes("不存在")) {
97
+ throw error;
98
+ }
99
+ output.warn(`命令 '${args.name}' 不存在于项目目录`);
100
+ }
101
+ } else {
102
+ // 同时尝试从两个目录移除
103
+ let removed = false;
104
+
105
+ // 先尝试从 user 目录移除
106
+ try {
107
+ await commandsComponent.remove({
108
+ name: args.name,
109
+ source: "user",
110
+ });
111
+ output.log(chalk.green(`✅ 已从用户目录移除命令 '${args.name}'`));
112
+ removed = true;
113
+ } catch (error: any) {
114
+ // 忽略"命令不存在"的错误
115
+ if (!error.message?.includes("不存在")) {
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ // 再尝试从 project 目录移除
121
+ try {
122
+ await commandsComponent.remove({
123
+ name: args.name,
124
+ source: "project",
125
+ });
126
+ output.log(chalk.green(`✅ 已从项目目录移除命令 '${args.name}'`));
127
+ removed = true;
128
+ } catch (error: any) {
129
+ // 忽略"命令不存在"的错误
130
+ if (!error.message?.includes("不存在")) {
131
+ throw error;
132
+ }
133
+ }
134
+
135
+ if (!removed) {
136
+ output.error(`命令 '${args.name}' 不存在`);
137
+ process.exit(1);
138
+ }
139
+ }
140
+ } catch (error: any) {
141
+ output.error(`移除失败: ${error.message}`);
142
+ process.exit(1);
143
+ } finally {
144
+ await envService.dispose();
145
+ }
146
+ },
147
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Commands Command
3
+ *
4
+ * 命令库管理 - CLI 主命令
5
+ *
6
+ * 功能:
7
+ * - 列出收藏命令(roy commands list)
8
+ * - 添加命令(roy commands add)
9
+ * - 移除命令(roy commands remove)
10
+ * - 显示命令详情(roy commands info)
11
+ * - 显示命令目录(roy commands dirs)
12
+ */
13
+
14
+ import { CommandModule } from "yargs";
15
+ import { CommandsListCommand } from "./commands-list";
16
+ import { CommandsAddCommand } from "./commands-add";
17
+ import { CommandsRemoveCommand } from "./commands-remove";
18
+ import { CommandsInfoCommand } from "./commands-info";
19
+ import { CommandsDirsCommand } from "./commands-dirs";
20
+
21
+ export interface CommandsOptions {
22
+ command?: string;
23
+ }
24
+
25
+ /**
26
+ * CommandsCommand - 命令库管理
27
+ */
28
+ export const CommandsCommand: CommandModule<object, CommandsOptions> = {
29
+ command: "commands",
30
+ describe: "命令库管理 - 收藏、查询常用命令",
31
+
32
+ builder: (yargs) =>
33
+ yargs
34
+ .command(CommandsListCommand)
35
+ .command(CommandsAddCommand)
36
+ .command(CommandsRemoveCommand)
37
+ .command(CommandsInfoCommand)
38
+ .command(CommandsDirsCommand)
39
+ .demandCommand(1, "请指定子命令:list、add、remove、info、dirs")
40
+ .help()
41
+ .strict(),
42
+
43
+ handler() {
44
+ // 默认显示帮助(由 yargs demandCommand 处理)
45
+ },
46
+ };
47
+
48
+ // 导出子命令
49
+ export {
50
+ CommandsListCommand,
51
+ CommandsAddCommand,
52
+ CommandsRemoveCommand,
53
+ CommandsInfoCommand,
54
+ CommandsDirsCommand,
55
+ };