@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,140 @@
1
+ /**
2
+ * @fileoverview Tasks List Command
3
+ *
4
+ * 命令:roy tasks list
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 {
12
+ TaskComponent,
13
+ Task,
14
+ ListTasksOptions,
15
+ } from "@ai-setting/roy-agent-core";
16
+
17
+ interface ListOptions {
18
+ status?: string;
19
+ priority?: string;
20
+ limit?: number;
21
+ offset?: number;
22
+ json?: boolean;
23
+ quiet?: boolean;
24
+ config?: string;
25
+ }
26
+
27
+ export const ListCommand: CommandModule<object, ListOptions> = {
28
+ command: "list",
29
+ aliases: ["ls"],
30
+ describe: "列出所有任务",
31
+
32
+ builder: (yargs) =>
33
+ yargs
34
+ .option("status", {
35
+ alias: "s",
36
+ type: "string",
37
+ choices: ["todo", "active", "completed", "paused", "cancelled"],
38
+ description: "按状态筛选",
39
+ })
40
+ .option("priority", {
41
+ alias: "p",
42
+ type: "string",
43
+ choices: ["low", "medium", "high"],
44
+ description: "按优先级筛选",
45
+ })
46
+ .option("limit", { alias: "n", type: "number", default: 20, description: "返回数量" })
47
+ .option("offset", { type: "number", default: 0, description: "偏移量" })
48
+ .option("json", { alias: "j", type: "boolean", default: false, description: "JSON 输出" })
49
+ .option("quiet", { alias: "q", type: "boolean", default: false, description: "简洁输出" }),
50
+
51
+ async handler(args) {
52
+ const output = new OutputService();
53
+ const envService = new EnvironmentService(output);
54
+
55
+ try {
56
+ await envService.create({ configPath: args.config });
57
+ const env = envService.getEnvironment();
58
+ if (!env) {
59
+ output.error("Failed to create environment");
60
+ process.exit(1);
61
+ }
62
+ const taskComponent = env.getComponent("task") as TaskComponent | undefined;
63
+
64
+ if (!taskComponent) {
65
+ output.error("TaskComponent not available");
66
+ process.exit(1);
67
+ }
68
+
69
+ const listOptions: ListTasksOptions = {
70
+ status: args.status as any,
71
+ priority: args.priority as any,
72
+ limit: args.limit,
73
+ offset: args.offset,
74
+ };
75
+ const tasks: Task[] = await taskComponent.listTasks(listOptions);
76
+
77
+ if (args.json) {
78
+ output.json({
79
+ tasks: tasks.map((t: Task) => ({
80
+ id: t.id,
81
+ title: t.title,
82
+ status: t.status,
83
+ priority: t.priority,
84
+ progress: t.progress,
85
+ current_status: t.current_status,
86
+ createdAt: t.createdAt,
87
+ updatedAt: t.updatedAt,
88
+ })),
89
+ count: tasks.length,
90
+ });
91
+ } else if (args.quiet) {
92
+ tasks.forEach((t: Task) => output.log(t.id.toString()));
93
+ } else {
94
+ // 表格输出
95
+ const header = [
96
+ chalk.bold("#"),
97
+ chalk.bold("Title"),
98
+ chalk.bold("Status"),
99
+ chalk.bold("Priority"),
100
+ chalk.bold("Progress"),
101
+ ].join(" │ ");
102
+
103
+ const rows = tasks.map((t: Task, i: number) => {
104
+ const statusColor = t.status === "completed" ? chalk.green :
105
+ t.status === "active" ? chalk.blue :
106
+ t.status === "paused" ? chalk.yellow :
107
+ t.status === "cancelled" ? chalk.strikethrough :
108
+ chalk.gray;
109
+
110
+ const priorityColor = t.priority === "high" ? chalk.red :
111
+ t.priority === "medium" ? chalk.yellow :
112
+ chalk.gray;
113
+
114
+ return [
115
+ (args.offset! + i + 1).toString(),
116
+ t.title.length > 30 ? t.title.slice(0, 27) + "..." : t.title,
117
+ statusColor(t.status),
118
+ priorityColor(t.priority),
119
+ `${t.progress}%`,
120
+ ].join(" │ ");
121
+ });
122
+
123
+ output.log([
124
+ `┌─ Tasks ${"─".repeat(55)}┐`,
125
+ `│${header}│`,
126
+ "├" + "─".repeat(header.length + 2) + "┤",
127
+ ...rows.map(r => `│${r}│`),
128
+ "└" + "─".repeat(header.length + 2) + "┘",
129
+ "",
130
+ chalk.gray(`Total: ${tasks.length} tasks`),
131
+ ].join("\n"));
132
+ }
133
+ } catch (error) {
134
+ output.error(`Failed to list tasks: ${error}`);
135
+ process.exit(1);
136
+ } finally {
137
+ await envService.dispose();
138
+ }
139
+ },
140
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @fileoverview Tasks Operations Command
3
+ *
4
+ * 命令:roy tasks operations <task-id>
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 {
12
+ TaskComponent,
13
+ Task,
14
+ TaskOperation,
15
+ ActionType,
16
+ ListOperationsOptions,
17
+ } from "@ai-setting/roy-agent-core";
18
+
19
+ interface OperationsOptions {
20
+ taskId: number;
21
+ type?: string;
22
+ limit?: number;
23
+ offset?: number;
24
+ json?: boolean;
25
+ config?: string;
26
+ }
27
+
28
+ export const OperationsCommand: CommandModule<object, object> = {
29
+ command: "operations <task-id>",
30
+ aliases: ["ops", "log"],
31
+ describe: "查看任务操作记录",
32
+
33
+ builder: (yargs) =>
34
+ yargs
35
+ .positional("task-id", {
36
+ type: "number",
37
+ describe: "任务 ID",
38
+ demandOption: true,
39
+ })
40
+ .option("type", {
41
+ alias: "t",
42
+ type: "string",
43
+ choices: ["create", "progress", "milestone", "problem", "solution", "decision", "review", "completed"],
44
+ description: "按类型筛选",
45
+ })
46
+ .option("limit", { alias: "n", type: "number", default: 20, description: "返回数量" })
47
+ .option("offset", { type: "number", default: 0, description: "偏移量" })
48
+ .option("json", { alias: "j", type: "boolean", default: false, description: "JSON 输出" }),
49
+
50
+ async handler(args) {
51
+ const a = args as any;
52
+ const output = new OutputService();
53
+ const envService = new EnvironmentService(output);
54
+
55
+ try {
56
+ await envService.create({ configPath: a.config });
57
+ const env = envService.getEnvironment();
58
+ if (!env) {
59
+ output.error("Failed to create environment");
60
+ process.exit(1);
61
+ }
62
+ const taskComponent = env.getComponent("task") as TaskComponent | undefined;
63
+
64
+ if (!taskComponent) {
65
+ output.error("TaskComponent not available");
66
+ process.exit(1);
67
+ }
68
+
69
+ // Check if task exists
70
+ const task: Task | undefined = await taskComponent.getTask(a.taskId);
71
+ if (!task) {
72
+ output.error(`Task not found: ${a.taskId}`);
73
+ process.exit(1);
74
+ }
75
+
76
+ const listOptions: ListOperationsOptions = {
77
+ taskId: a.taskId,
78
+ actionType: a.type as ActionType,
79
+ limit: a.limit,
80
+ offset: a.offset,
81
+ };
82
+ const operations: TaskOperation[] = await taskComponent.listOperations(listOptions);
83
+
84
+ if (a.json) {
85
+ output.json({
86
+ task: { id: task.id, title: task.title },
87
+ operations,
88
+ count: operations.length,
89
+ });
90
+ } else {
91
+ output.log(chalk.bold(`\nOperations for Task #${task.id}: ${task.title}\n`));
92
+
93
+ if (operations.length === 0) {
94
+ output.log(chalk.gray("No operations found."));
95
+ } else {
96
+ operations.forEach((op: TaskOperation, i: number) => {
97
+ const actionColor = op.actionType === "completed" ? chalk.green :
98
+ op.actionType === "milestone" ? chalk.blue :
99
+ op.actionType === "problem" ? chalk.red :
100
+ chalk.gray;
101
+
102
+ output.log(`${chalk.bold(a.offset! + i + 1)}. ` + actionColor(`[${op.actionType}]`) + ` ${op.actionTitle}`);
103
+ output.log(` Session: ${chalk.cyan(op.sessionId)} | Time: ${op.timestamp}`);
104
+ if (op.actionDescription) {
105
+ output.log(` ${op.actionDescription}`);
106
+ }
107
+ output.log("");
108
+ });
109
+ }
110
+
111
+ output.log(chalk.gray(`Total: ${operations.length} operations`));
112
+ }
113
+ } catch (error) {
114
+ output.error(`Failed to list operations: ${error}`);
115
+ process.exit(1);
116
+ } finally {
117
+ await envService.dispose();
118
+ }
119
+ },
120
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @fileoverview Tasks Update Command
3
+ *
4
+ * 命令:roy tasks update <task-id>
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 {
12
+ SessionComponent,
13
+ TaskComponent,
14
+ Task,
15
+ TaskStatus,
16
+ TaskPriority,
17
+ UpdateTaskOptions,
18
+ } from "@ai-setting/roy-agent-core";
19
+
20
+ interface UpdateOptions {
21
+ id: number;
22
+ title?: string;
23
+ description?: string;
24
+ status?: string;
25
+ priority?: string;
26
+ progress?: number;
27
+ current_status?: string;
28
+ json?: boolean;
29
+ config?: string;
30
+ }
31
+
32
+ export const UpdateCommand: CommandModule<object, UpdateOptions> = {
33
+ command: "update <id>",
34
+ aliases: ["set"],
35
+ describe: "更新任务",
36
+
37
+ builder: (yargs) =>
38
+ yargs
39
+ .positional("id", {
40
+ type: "number",
41
+ describe: "任务 ID",
42
+ demandOption: true,
43
+ })
44
+ .option("title", { alias: "t", type: "string", description: "标题" })
45
+ .option("description", { alias: "d", type: "string", description: "描述" })
46
+ .option("status", { alias: "s", type: "string", choices: ["todo", "active", "completed", "paused", "cancelled"], description: "状态" })
47
+ .option("priority", { alias: "p", type: "string", choices: ["low", "medium", "high"], description: "优先级" })
48
+ .option("progress", { type: "number", min: 0, max: 100, description: "进度 (0-100)" })
49
+ .option("current_status", { alias: "c", type: "string", description: "当前状态描述" })
50
+ .option("json", { alias: "j", type: "boolean", default: false, description: "JSON 输出" }),
51
+
52
+ async handler(args) {
53
+ const output = new OutputService();
54
+ const envService = new EnvironmentService(output);
55
+
56
+ try {
57
+ await envService.create({ configPath: args.config });
58
+ const env = envService.getEnvironment();
59
+ if (!env) {
60
+ output.error("Failed to create environment");
61
+ process.exit(1);
62
+ }
63
+ const sessionComponent = env.getComponent("session") as SessionComponent | undefined;
64
+ const taskComponent = env.getComponent("task") as TaskComponent | undefined;
65
+
66
+ if (!taskComponent) {
67
+ output.error("TaskComponent not available");
68
+ process.exit(1);
69
+ }
70
+
71
+ // Get current session ID
72
+ const currentSessionId = sessionComponent?.getActiveSessionId() || "cli";
73
+
74
+ const updateOptions: UpdateTaskOptions = {
75
+ title: args.title,
76
+ description: args.description,
77
+ status: args.status as TaskStatus,
78
+ priority: args.priority as TaskPriority,
79
+ progress: args.progress,
80
+ current_status: args.current_status,
81
+ };
82
+ const task: Task | undefined = await taskComponent.updateTask(args.id, updateOptions);
83
+
84
+ if (!task) {
85
+ output.error(`Task not found: ${args.id}`);
86
+ process.exit(1);
87
+ }
88
+
89
+ // Create operation record for status/progress changes
90
+ if (args.status || args.progress !== undefined || args.current_status) {
91
+ await taskComponent.createOperation({
92
+ taskId: args.id,
93
+ sessionId: currentSessionId,
94
+ actionType: "progress",
95
+ actionTitle: args.current_status || `Updated: ${args.status || `${args.progress}%`}`,
96
+ actionDescription: JSON.stringify({
97
+ status: args.status,
98
+ progress: args.progress,
99
+ }),
100
+ });
101
+ }
102
+
103
+ if (args.json) {
104
+ output.json(task);
105
+ } else {
106
+ output.log(chalk.green(`\n✓ Task updated: #${task.id}`));
107
+ output.log(` Title: ${task.title}`);
108
+ output.log(` Status: ${task.status}`);
109
+ output.log(` Priority: ${task.priority}`);
110
+ output.log(` Progress: ${task.progress}%`);
111
+ if (task.current_status) {
112
+ output.log(` Current Status: ${task.current_status}`);
113
+ }
114
+ }
115
+ } catch (error) {
116
+ output.error(`Failed to update task: ${error}`);
117
+ process.exit(1);
118
+ } finally {
119
+ await envService.dispose();
120
+ }
121
+ },
122
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @fileoverview Tools Exec-Tool Command
3
+ *
4
+ * 命令:roy tools exec-tool <name>
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { EnvironmentService } from "../../services/environment.service";
9
+ import { OutputService } from "../../services/output.service";
10
+ import type { ToolComponent, Tool } from "@ai-setting/roy-agent-core";
11
+ import {
12
+ extractParameters,
13
+ validateRequiredParams,
14
+ } from "./shared/schema-helper";
15
+
16
+ interface ExecToolOptions {
17
+ name: string;
18
+ config?: string;
19
+ // 动态参数将通过 yargs builder 动态添加
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ export const ExecToolCommand: CommandModule<object, ExecToolOptions> = {
24
+ command: "exec-tool <name>",
25
+ describe: "执行指定工具",
26
+
27
+ builder: (yargs) => {
28
+ return yargs
29
+ .positional("name", {
30
+ type: "string",
31
+ demandOption: true,
32
+ description: "工具名称",
33
+ })
34
+ .strict(false); // 允许动态参数
35
+ },
36
+
37
+ async handler(args) {
38
+ const output = new OutputService();
39
+ const envService = new EnvironmentService(output);
40
+ const toolName = args.name;
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 toolComponent = env.getComponent("tool") as ToolComponent | undefined;
50
+
51
+ if (!toolComponent) {
52
+ output.error("ToolComponent not available");
53
+ process.exit(1);
54
+ }
55
+
56
+ // 获取工具
57
+ const tool = toolComponent.getTool(toolName);
58
+
59
+ if (!tool) {
60
+ output.error(`Tool not found: ${toolName}`);
61
+ output.log("Use 'roy tools list' to see available tools.");
62
+ process.exit(1);
63
+ }
64
+
65
+ // 提取参数信息
66
+ const params = extractParameters(tool.parameters);
67
+
68
+ // 收集用户提供的参数(排除 name 和 config)
69
+ const userArgs: Record<string, unknown> = {};
70
+ for (const param of params) {
71
+ if (args[param.name] !== undefined) {
72
+ userArgs[param.name] = args[param.name];
73
+ }
74
+ }
75
+
76
+ // 验证必需参数
77
+ const validation = validateRequiredParams(params, userArgs);
78
+
79
+ if (!validation.valid) {
80
+ output.error(`Missing required arguments for '${toolName}':`);
81
+ for (const missing of validation.missing) {
82
+ const param = params.find((p) => p.name === missing);
83
+ output.log(` - ${missing} (${param?.description || "required"})`);
84
+ }
85
+ output.log("");
86
+ output.log(`Usage: roy tools exec-tool ${toolName} [options]`);
87
+ for (const param of params) {
88
+ const req = param.required ? "required" : "optional";
89
+ const def =
90
+ param.default !== undefined
91
+ ? ` [default: ${JSON.stringify(param.default)}]`
92
+ : "";
93
+ output.log(` --${param.name} <${param.type}> (${req})${def}`);
94
+ }
95
+ process.exit(1);
96
+ }
97
+
98
+ // 执行工具
99
+ const result = await toolComponent.execute({
100
+ name: toolName,
101
+ args: userArgs,
102
+ context: {
103
+ workdir: process.cwd(),
104
+ },
105
+ });
106
+
107
+ // 输出结果(JSON 格式)
108
+ output.json(result);
109
+
110
+ // 根据结果设置退出码
111
+ if (!result.success) {
112
+ process.exit(1);
113
+ }
114
+ } catch (error) {
115
+ output.json({
116
+ success: false,
117
+ output: "",
118
+ error: error instanceof Error ? error.message : String(error),
119
+ metadata: {
120
+ execution_time_ms: 0,
121
+ },
122
+ });
123
+ process.exit(1);
124
+ } finally {
125
+ await envService.dispose();
126
+ }
127
+ },
128
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview Tools Get Command
3
+ *
4
+ * 命令:roy tools get <name>
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 { ToolComponent, Tool } from "@ai-setting/roy-agent-core";
12
+ import { extractParameters } from "./shared/schema-helper";
13
+
14
+ interface GetOptions {
15
+ name: string;
16
+ json?: boolean;
17
+ config?: string;
18
+ }
19
+
20
+ export const GetCommand: CommandModule<object, GetOptions> = {
21
+ command: "get <name>",
22
+ describe: "获取指定工具的详细信息",
23
+
24
+ builder: (yargs) =>
25
+ yargs
26
+ .positional("name", {
27
+ type: "string",
28
+ demandOption: true,
29
+ description: "工具名称",
30
+ })
31
+ .option("json", {
32
+ alias: "j",
33
+ type: "boolean",
34
+ default: false,
35
+ description: "JSON 输出",
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 toolComponent = env.getComponent("tool") as ToolComponent | undefined;
50
+
51
+ if (!toolComponent) {
52
+ output.error("ToolComponent not available");
53
+ process.exit(1);
54
+ }
55
+
56
+ const tool = toolComponent.getTool(args.name);
57
+
58
+ if (!tool) {
59
+ output.error(`Tool not found: ${args.name}`);
60
+ output.log("Use 'roy tools list' to see available tools.");
61
+ process.exit(1);
62
+ }
63
+
64
+ // 提取参数信息
65
+ const params = extractParameters(tool.parameters);
66
+
67
+ if (args.json) {
68
+ output.json({
69
+ name: tool.name,
70
+ description: tool.description,
71
+ category: tool.metadata?.category,
72
+ tags: tool.metadata?.tags,
73
+ parameters: params.map((p) => ({
74
+ name: p.name,
75
+ type: p.type,
76
+ description: p.description,
77
+ required: p.required,
78
+ default: p.default,
79
+ })),
80
+ });
81
+ } else {
82
+ // 格式化输出
83
+ output.log(chalk.bold.cyan(`Tool: ${tool.name}`));
84
+ output.log(chalk.gray("─".repeat(60)));
85
+ output.log(`Description: ${tool.description}`);
86
+ if (tool.metadata?.category) {
87
+ output.log(`Category: ${tool.metadata.category}`);
88
+ }
89
+ if (tool.metadata?.tags?.length) {
90
+ output.log(`Tags: ${tool.metadata.tags.join(", ")}`);
91
+ }
92
+ output.log("");
93
+ output.log(chalk.bold("Parameters:"));
94
+
95
+ for (const param of params) {
96
+ const required = param.required
97
+ ? chalk.red("(required)")
98
+ : chalk.gray("(optional)");
99
+ const defaultVal =
100
+ param.default !== undefined
101
+ ? ` [default: ${JSON.stringify(param.default)}]`
102
+ : "";
103
+ output.log(` --${param.name} <${param.type}> ${required}`);
104
+ output.log(` ${param.description}${defaultVal}`);
105
+ }
106
+ }
107
+ } catch (error) {
108
+ output.error(`Failed to get tool: ${error}`);
109
+ process.exit(1);
110
+ } finally {
111
+ await envService.dispose();
112
+ }
113
+ },
114
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @fileoverview Tools Command Entry
3
+ *
4
+ * 命令入口:roy tools [action]
5
+ */
6
+
7
+ import { CommandModule } from "yargs";
8
+ import { ListCommand } from "./list";
9
+ import { GetCommand } from "./get";
10
+ import { ExecToolCommand } from "./exec-tool";
11
+
12
+ /**
13
+ * Tools Command
14
+ *
15
+ * 工具管理命令,提供以下子命令:
16
+ * - list: 列出所有工具
17
+ * - get: 获取工具详情
18
+ * - exec-tool: 执行工具
19
+ */
20
+ export const ToolsCommand: CommandModule = {
21
+ command: "tools",
22
+ describe: "工具管理 - 列出、获取、执行内置工具",
23
+
24
+ builder: (yargs) =>
25
+ yargs
26
+ .command(ListCommand)
27
+ .command(GetCommand)
28
+ .command(ExecToolCommand)
29
+ .demandCommand()
30
+ .help(),
31
+
32
+ handler: () => {
33
+ console.log("Use 'roy tools --help' for usage information");
34
+ },
35
+ };