@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.
- package/README.md +126 -0
- package/dist/bin/roy.js +127297 -0
- package/dist/roy-agent-darwin-arm64/bin/roy.js +127297 -0
- package/dist/roy-agent-darwin-x64/bin/roy.js +127297 -0
- package/dist/roy-agent-linux-arm64/bin/roy.js +127297 -0
- package/dist/roy-agent-linux-x64/bin/roy.js +127297 -0
- package/dist/roy-agent-windows-x64/bin/roy.js +127297 -0
- package/package.json +91 -0
- package/src/bin/roy.ts +12 -0
- package/src/cli.ts +101 -0
- package/src/commands/act.ts +480 -0
- package/src/commands/commands-add.ts +110 -0
- package/src/commands/commands-dirs.ts +70 -0
- package/src/commands/commands-info.ts +90 -0
- package/src/commands/commands-list.ts +161 -0
- package/src/commands/commands-remove.ts +147 -0
- package/src/commands/commands.ts +55 -0
- package/src/commands/config/config-service.test.ts +449 -0
- package/src/commands/config/config-service.ts +312 -0
- package/src/commands/config/deep-merge.test.ts +168 -0
- package/src/commands/config/deep-merge.ts +63 -0
- package/src/commands/config/export.ts +97 -0
- package/src/commands/config/filter-history-e2e.test.ts +141 -0
- package/src/commands/config/import-preserve-refs.test.ts +212 -0
- package/src/commands/config/import.ts +119 -0
- package/src/commands/config/index.ts +35 -0
- package/src/commands/config/list.ts +281 -0
- package/src/commands/config/roy-config-e2e.test.ts +297 -0
- package/src/commands/config/types.ts +54 -0
- package/src/commands/debug/index.ts +38 -0
- package/src/commands/debug/log.test.ts +233 -0
- package/src/commands/debug/log.ts +123 -0
- package/src/commands/debug/span.test.ts +297 -0
- package/src/commands/debug/span.ts +211 -0
- package/src/commands/debug/trace.test.ts +254 -0
- package/src/commands/debug/trace.ts +140 -0
- package/src/commands/eventsource/add.ts +133 -0
- package/src/commands/eventsource/index.ts +48 -0
- package/src/commands/eventsource/list.ts +194 -0
- package/src/commands/eventsource/remove.ts +95 -0
- package/src/commands/eventsource/start.ts +103 -0
- package/src/commands/eventsource/status.ts +185 -0
- package/src/commands/eventsource/stop.ts +89 -0
- package/src/commands/index.ts +22 -0
- package/src/commands/input-handler.test.ts +76 -0
- package/src/commands/input-handler.ts +43 -0
- package/src/commands/interactive-esc.test.ts +254 -0
- package/src/commands/interactive.shutdown.test.ts +122 -0
- package/src/commands/interactive.test.ts +221 -0
- package/src/commands/interactive.ts +1015 -0
- package/src/commands/lsp/check.ts +92 -0
- package/src/commands/lsp/index.ts +32 -0
- package/src/commands/lsp/install.ts +126 -0
- package/src/commands/lsp/list.ts +64 -0
- package/src/commands/mcp/index.ts +27 -0
- package/src/commands/mcp/list.ts +116 -0
- package/src/commands/mcp/reload.ts +70 -0
- package/src/commands/mcp/tools.ts +121 -0
- package/src/commands/memory/extract-e2e.test.ts +388 -0
- package/src/commands/memory/index.ts +11 -0
- package/src/commands/memory/memory-simplified.test.ts +58 -0
- package/src/commands/memory/memory.ts +25 -0
- package/src/commands/memory/organize.ts +300 -0
- package/src/commands/memory/recall.test.ts +120 -0
- package/src/commands/memory/recall.ts +88 -0
- package/src/commands/memory/record-extract-handle-query.test.ts +385 -0
- package/src/commands/memory/record-prompt-component.test.ts +343 -0
- package/src/commands/memory/record.test.ts +92 -0
- package/src/commands/memory/record.ts +332 -0
- package/src/commands/plugin.test.ts +292 -0
- package/src/commands/plugin.ts +267 -0
- package/src/commands/sessions/active.ts +96 -0
- package/src/commands/sessions/add-message.ts +96 -0
- package/src/commands/sessions/checkpoints.ts +154 -0
- package/src/commands/sessions/compact.test.ts +215 -0
- package/src/commands/sessions/compact.ts +269 -0
- package/src/commands/sessions/delete.ts +236 -0
- package/src/commands/sessions/get.ts +165 -0
- package/src/commands/sessions/grep.ts +233 -0
- package/src/commands/sessions/index.ts +95 -0
- package/src/commands/sessions/list.ts +210 -0
- package/src/commands/sessions/messages.test.ts +333 -0
- package/src/commands/sessions/messages.ts +248 -0
- package/src/commands/sessions/mock.ts +194 -0
- package/src/commands/sessions/new.ts +82 -0
- package/src/commands/sessions/rename.ts +98 -0
- package/src/commands/shared/event-handler.ts +213 -0
- package/src/commands/shared/event-message-formatter.ts +295 -0
- package/src/commands/shared/index.ts +11 -0
- package/src/commands/shared/query-executor.test.ts +434 -0
- package/src/commands/shared/query-executor.ts +324 -0
- package/src/commands/shared/repl-engine.test.ts +354 -0
- package/src/commands/shared/session-manager.test.ts +212 -0
- package/src/commands/shared/session-manager.ts +114 -0
- package/src/commands/skills/get.ts +90 -0
- package/src/commands/skills/index.ts +39 -0
- package/src/commands/skills/list.ts +129 -0
- package/src/commands/skills/reload.ts +59 -0
- package/src/commands/skills/search.ts +132 -0
- package/src/commands/skills/show-config.ts +93 -0
- package/src/commands/tasks/complete.ts +92 -0
- package/src/commands/tasks/create.ts +118 -0
- package/src/commands/tasks/delete.ts +86 -0
- package/src/commands/tasks/get.ts +116 -0
- package/src/commands/tasks/index.ts +53 -0
- package/src/commands/tasks/list.ts +140 -0
- package/src/commands/tasks/operations.ts +120 -0
- package/src/commands/tasks/update.ts +122 -0
- package/src/commands/tools/exec-tool.ts +128 -0
- package/src/commands/tools/get.ts +114 -0
- package/src/commands/tools/index.ts +35 -0
- package/src/commands/tools/list.ts +107 -0
- package/src/commands/tools/shared/index.ts +7 -0
- package/src/commands/tools/shared/schema-helper.ts +111 -0
- package/src/commands/workflow/commands/add.ts +315 -0
- package/src/commands/workflow/commands/get.ts +193 -0
- package/src/commands/workflow/commands/list.ts +137 -0
- package/src/commands/workflow/commands/nodes.ts +528 -0
- package/src/commands/workflow/commands/remove.ts +94 -0
- package/src/commands/workflow/commands/run.ts +398 -0
- package/src/commands/workflow/commands/status.ts +147 -0
- package/src/commands/workflow/commands/stop.ts +91 -0
- package/src/commands/workflow/commands/update.ts +130 -0
- package/src/commands/workflow/commands/validate.ts +139 -0
- package/src/commands/workflow/commands/workflow-cli.test.ts +196 -0
- package/src/commands/workflow/index.ts +65 -0
- package/src/commands/workflow/renderers.ts +358 -0
- package/src/commands/workflow/validators/index.ts +8 -0
- package/src/commands/workflow/validators/node-validator-factory.ts +40 -0
- package/src/commands/workflow/validators/node-validator.ts +125 -0
- package/src/commands/workflow/validators/nodes/agent-node-validator.ts +58 -0
- package/src/commands/workflow/validators/nodes/condition-node-validator.ts +34 -0
- package/src/commands/workflow/validators/nodes/decorator-node-validator.ts +45 -0
- package/src/commands/workflow/validators/nodes/merge-node-validator.ts +46 -0
- package/src/commands/workflow/validators/nodes/skill-node-validator.ts +33 -0
- package/src/commands/workflow/validators/nodes/tool-node-validator.ts +54 -0
- package/src/commands/workflow/validators/nodes/workflow-node-validator.ts +33 -0
- package/src/commands/workflow/validators/types.ts +78 -0
- package/src/commands/workflow/validators/workflow-validator.test.ts +273 -0
- package/src/commands/workflow/validators/workflow-validator.ts +320 -0
- package/src/index.ts +19 -0
- package/src/plugin/apply.ts +103 -0
- package/src/plugin/discover.ts +219 -0
- package/src/plugin/index.ts +45 -0
- package/src/plugin/registry.ts +272 -0
- package/src/plugin/types.ts +165 -0
- package/src/services/context-handler.service.test.ts +501 -0
- package/src/services/context-handler.service.ts +372 -0
- package/src/services/environment.service.commands-prompt.test.ts +167 -0
- package/src/services/environment.service.ts +656 -0
- package/src/services/output.service.test.ts +92 -0
- package/src/services/output.service.ts +122 -0
- package/src/services/quiet-mode.service.test.ts +114 -0
- package/src/services/quiet-mode.service.ts +81 -0
- package/src/services/stream-output.service.test.ts +214 -0
- package/src/services/stream-output.service.ts +323 -0
- package/src/util/which.test.ts +101 -0
- package/src/util/which.ts +55 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Memory Organize Command
|
|
3
|
+
*
|
|
4
|
+
* Manual trigger for memory organization
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* roy memory organize
|
|
8
|
+
* roy memory organize --scope project
|
|
9
|
+
* roy memory organize --scope global
|
|
10
|
+
* roy memory organize --scope all
|
|
11
|
+
* roy memory organize --since 2026-04-01
|
|
12
|
+
* roy memory organize --session <session-id>
|
|
13
|
+
* roy memory organize --list-sessions
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { CommandModule } from "yargs";
|
|
17
|
+
import { EnvironmentService } from "../../services/environment.service";
|
|
18
|
+
import { OutputService } from "../../services/output.service";
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
import type { SessionComponent, AgentComponent, MemoryPlugin as IMemoryPlugin } from "@ai-setting/roy-agent-core";
|
|
21
|
+
|
|
22
|
+
interface OrganizeOptions {
|
|
23
|
+
scope?: "project" | "global" | "all";
|
|
24
|
+
since?: string;
|
|
25
|
+
session?: string;
|
|
26
|
+
listSessions?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* OrganizeContext - 用于依赖注入的上下文
|
|
31
|
+
*/
|
|
32
|
+
export interface OrganizeContext {
|
|
33
|
+
envService: EnvironmentService;
|
|
34
|
+
sessionComponent: SessionComponent | null;
|
|
35
|
+
agentComponent: AgentComponent | null;
|
|
36
|
+
memoryPlugin?: IMemoryPlugin;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 创建 OrganizeContext
|
|
41
|
+
*/
|
|
42
|
+
export function createOrganizeContext(
|
|
43
|
+
envService: EnvironmentService
|
|
44
|
+
): OrganizeContext {
|
|
45
|
+
return {
|
|
46
|
+
envService,
|
|
47
|
+
sessionComponent: envService.getSession(),
|
|
48
|
+
agentComponent: envService.getAgent(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 准备 MemoryPlugin
|
|
54
|
+
*
|
|
55
|
+
* @param workDir 工作目录
|
|
56
|
+
* @param agentComponent AgentComponent(可选,用于 AI 记忆整理)
|
|
57
|
+
*/
|
|
58
|
+
export async function prepareMemoryPlugin(
|
|
59
|
+
workDir: string,
|
|
60
|
+
agentComponent?: AgentComponent
|
|
61
|
+
): Promise<IMemoryPlugin> {
|
|
62
|
+
// 动态导入 MemoryPlugin
|
|
63
|
+
const { MemoryPlugin } = await import("@ai-setting/roy-agent-core");
|
|
64
|
+
|
|
65
|
+
// 设置全局注册表(供 MemoryPlugin 初始化时获取组件)
|
|
66
|
+
const globalRegistry = (globalThis as any).__royAgentRegistry || {};
|
|
67
|
+
// 保留现有的 sessionComponent(如果有)
|
|
68
|
+
if (!globalRegistry.sessionComponent) {
|
|
69
|
+
// 从 EnvironmentService 获取(如果可用)
|
|
70
|
+
// 注意:在 CLI 模式下,sessionComponent 由调用方通过 ctx 传递
|
|
71
|
+
}
|
|
72
|
+
globalRegistry.agentComponent = agentComponent;
|
|
73
|
+
(globalThis as any).__royAgentRegistry = globalRegistry;
|
|
74
|
+
|
|
75
|
+
// 创建 MemoryPlugin
|
|
76
|
+
const memoryPlugin = new MemoryPlugin(workDir, {
|
|
77
|
+
enabled: true,
|
|
78
|
+
} as any);
|
|
79
|
+
|
|
80
|
+
await memoryPlugin.initialize();
|
|
81
|
+
|
|
82
|
+
// 额外注册 AgentComponent(双保险,确保 MemoryPlugin 能使用 AI)
|
|
83
|
+
if (agentComponent) {
|
|
84
|
+
(memoryPlugin as any).registerAgentComponent(agentComponent);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return memoryPlugin;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 清理资源
|
|
92
|
+
*/
|
|
93
|
+
export function cleanupOrganizeResources(
|
|
94
|
+
ctx: OrganizeContext,
|
|
95
|
+
memoryPlugin?: IMemoryPlugin
|
|
96
|
+
): void {
|
|
97
|
+
// 清理全局注册表
|
|
98
|
+
delete (globalThis as any).__royAgentRegistry;
|
|
99
|
+
|
|
100
|
+
// MemoryPlugin 清理由调用方负责
|
|
101
|
+
ctx.memoryPlugin = memoryPlugin;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const OrganizeCommand: CommandModule = {
|
|
105
|
+
command: "organize [options]",
|
|
106
|
+
describe: "手动触发记忆整理",
|
|
107
|
+
builder: (yargs) =>
|
|
108
|
+
yargs
|
|
109
|
+
.option("scope", {
|
|
110
|
+
alias: "s",
|
|
111
|
+
description: "整理范围: project, global, all (默认: all)",
|
|
112
|
+
type: "string",
|
|
113
|
+
choices: ["project", "global", "all"],
|
|
114
|
+
default: "all",
|
|
115
|
+
})
|
|
116
|
+
.option("since", {
|
|
117
|
+
alias: "t",
|
|
118
|
+
description: "整理指定时间以来的记忆 (ISO 日期或 Unix 时间戳)",
|
|
119
|
+
type: "string",
|
|
120
|
+
})
|
|
121
|
+
.option("session", {
|
|
122
|
+
alias: "S",
|
|
123
|
+
description: "整理指定会话的记忆",
|
|
124
|
+
type: "string",
|
|
125
|
+
})
|
|
126
|
+
.option("list-sessions", {
|
|
127
|
+
alias: "l",
|
|
128
|
+
description: "列出可用的会话",
|
|
129
|
+
type: "boolean",
|
|
130
|
+
default: false,
|
|
131
|
+
})
|
|
132
|
+
.example("roy memory organize", "整理所有记忆")
|
|
133
|
+
.example("roy memory organize --scope project", "只整理项目记忆")
|
|
134
|
+
.example("roy memory organize --scope global", "只整理全局记忆")
|
|
135
|
+
.example("roy memory organize --since 2026-04-01", "整理指定时间以来的记忆")
|
|
136
|
+
.example("roy memory organize --list-sessions", "列出所有会话")
|
|
137
|
+
.example("roy memory organize --session abc123", "整理指定会话的记忆"),
|
|
138
|
+
|
|
139
|
+
async handler(argv) {
|
|
140
|
+
const output = new OutputService();
|
|
141
|
+
const envService = new EnvironmentService(output);
|
|
142
|
+
let memoryPlugin: IMemoryPlugin | undefined;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await envService.create();
|
|
146
|
+
|
|
147
|
+
const ctx = createOrganizeContext(envService);
|
|
148
|
+
const sessionComponent = ctx.sessionComponent;
|
|
149
|
+
|
|
150
|
+
// 处理 --list-sessions 选项
|
|
151
|
+
if ((argv as OrganizeOptions).listSessions) {
|
|
152
|
+
await listSessions(sessionComponent, output);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const options = argv as OrganizeOptions;
|
|
157
|
+
|
|
158
|
+
output.log(chalk.bold("\n📋 记忆整理\n"));
|
|
159
|
+
|
|
160
|
+
// 如果指定了 session,获取该 session 的消息并显示预览
|
|
161
|
+
if (options.session) {
|
|
162
|
+
output.log(chalk.gray(`📂 会话: ${options.session}`));
|
|
163
|
+
|
|
164
|
+
const messages = await getSessionMessages(sessionComponent, options.session);
|
|
165
|
+
if (messages.length === 0) {
|
|
166
|
+
output.error(`会话不存在或没有消息: ${options.session}`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
output.log(chalk.gray(`📝 共 ${messages.length} 条消息\n`));
|
|
171
|
+
|
|
172
|
+
// 显示消息预览
|
|
173
|
+
output.log(chalk.gray("📄 消息摘要预览:"));
|
|
174
|
+
for (let i = 0; i < Math.min(3, messages.length); i++) {
|
|
175
|
+
const msg = messages[i];
|
|
176
|
+
const content = typeof msg.content === "string"
|
|
177
|
+
? msg.content.substring(0, 100) + "..."
|
|
178
|
+
: JSON.stringify(msg.content).substring(0, 100) + "...";
|
|
179
|
+
output.log(chalk.gray(` [${msg.role}] ${content}`));
|
|
180
|
+
}
|
|
181
|
+
output.log("");
|
|
182
|
+
} else {
|
|
183
|
+
output.log(chalk.gray("💡 提示: 使用 --session 指定会话来整理对话记忆\n"));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
output.log(chalk.gray(`🎯 整理范围: ${options.scope || "all"}\n`));
|
|
187
|
+
|
|
188
|
+
// 获取 AgentComponent
|
|
189
|
+
if (!ctx.agentComponent) {
|
|
190
|
+
output.error("AgentComponent not available, cannot organize memory with AI");
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 准备 MemoryPlugin(包含注册 AgentComponent)
|
|
195
|
+
memoryPlugin = await prepareMemoryPlugin(process.cwd(), ctx.agentComponent);
|
|
196
|
+
|
|
197
|
+
// 解析 since 时间戳
|
|
198
|
+
let sinceTimestamp: number | undefined;
|
|
199
|
+
if (options.since) {
|
|
200
|
+
const date = new Date(options.since);
|
|
201
|
+
if (!isNaN(date.getTime())) {
|
|
202
|
+
sinceTimestamp = date.getTime();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 调用 organize
|
|
207
|
+
output.log(chalk.gray("⏳ 正在整理记忆...\n"));
|
|
208
|
+
|
|
209
|
+
await (memoryPlugin as any).organize({
|
|
210
|
+
scope: options.scope as "project" | "global" | "all" || "all",
|
|
211
|
+
sessionId: options.session,
|
|
212
|
+
since: sinceTimestamp,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
output.success("\n✅ 记忆整理完成");
|
|
216
|
+
output.log(chalk.gray("\n提示: 使用 `roy memory recall` 查看整理后的记忆"));
|
|
217
|
+
|
|
218
|
+
} catch (error) {
|
|
219
|
+
output.error(`整理记忆失败: ${error}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
} finally {
|
|
222
|
+
// 清理 MemoryPlugin
|
|
223
|
+
if (memoryPlugin) {
|
|
224
|
+
await memoryPlugin.dispose();
|
|
225
|
+
}
|
|
226
|
+
// 清理全局注册表
|
|
227
|
+
cleanupOrganizeResources(createOrganizeContext(envService), memoryPlugin);
|
|
228
|
+
// 清理 EnvironmentService
|
|
229
|
+
await envService.dispose();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 获取指定 session 的消息
|
|
236
|
+
*/
|
|
237
|
+
async function getSessionMessages(
|
|
238
|
+
sessionComponent: SessionComponent | null,
|
|
239
|
+
sessionId: string
|
|
240
|
+
): Promise<Array<{ role: string; content: string }>> {
|
|
241
|
+
if (!sessionComponent) {
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const session = await sessionComponent.get(sessionId);
|
|
247
|
+
if (!session) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 获取消息,默认获取最近 100 条
|
|
252
|
+
const messages = await sessionComponent.getMessages(sessionId, { limit: 100 });
|
|
253
|
+
|
|
254
|
+
// 过滤掉 tool 相关消息,只保留 user 和 assistant
|
|
255
|
+
return messages
|
|
256
|
+
.filter((msg) => msg.role === "user" || msg.role === "assistant")
|
|
257
|
+
.map((msg) => ({
|
|
258
|
+
role: msg.role,
|
|
259
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
260
|
+
}));
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 列出所有会话
|
|
268
|
+
*/
|
|
269
|
+
export async function listSessions(
|
|
270
|
+
sessionComponent: SessionComponent | null,
|
|
271
|
+
output: OutputService
|
|
272
|
+
): Promise<void> {
|
|
273
|
+
if (!sessionComponent) {
|
|
274
|
+
output.error("SessionComponent not available");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const sessions = await sessionComponent.list({ limit: 20 });
|
|
280
|
+
|
|
281
|
+
if (sessions.length === 0) {
|
|
282
|
+
output.log(chalk.gray("暂无会话"));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
output.log(chalk.bold(`\n📂 会话列表 (最近 20 个)\n\n`));
|
|
287
|
+
|
|
288
|
+
for (const session of sessions) {
|
|
289
|
+
const date = new Date(session.createdAt).toLocaleDateString("zh-CN");
|
|
290
|
+
const time = new Date(session.createdAt).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
|
|
291
|
+
|
|
292
|
+
output.log(chalk.cyan(`ID: ${session.id}`));
|
|
293
|
+
output.log(` 标题: ${session.title || "无标题"}`);
|
|
294
|
+
output.log(` 创建: ${date} ${time}`);
|
|
295
|
+
output.log("");
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
output.error(`列出会话失败: ${error}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Memory Recall Command Tests
|
|
3
|
+
*
|
|
4
|
+
* 测试:recall 命令(记忆读取)
|
|
5
|
+
* 注意:--extract 功能已迁移到 record 命令
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test, vi } from "vitest";
|
|
9
|
+
import { RecallCommand } from "./recall";
|
|
10
|
+
|
|
11
|
+
describe("RecallCommand", () => {
|
|
12
|
+
describe("command configuration", () => {
|
|
13
|
+
test("should have correct command name", () => {
|
|
14
|
+
expect(RecallCommand.command).toBe("recall");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should have aliases", () => {
|
|
18
|
+
expect(RecallCommand.aliases).toContain("load");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("should have description", () => {
|
|
22
|
+
expect(RecallCommand.describe).toBe("读取所有 memory file 的内容");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("builder - options", () => {
|
|
27
|
+
test("should have config option", () => {
|
|
28
|
+
const builder = RecallCommand.builder as Function;
|
|
29
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
30
|
+
builder({ option: mockOption });
|
|
31
|
+
|
|
32
|
+
const calls = mockOption.mock.calls;
|
|
33
|
+
const configCall = calls.find((call: any[]) => call[0] === "config");
|
|
34
|
+
expect(configCall).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("should have json option", () => {
|
|
38
|
+
const builder = RecallCommand.builder as Function;
|
|
39
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
40
|
+
builder({ option: mockOption });
|
|
41
|
+
|
|
42
|
+
const calls = mockOption.mock.calls;
|
|
43
|
+
const jsonCall = calls.find((call: any[]) => call[0] === "json");
|
|
44
|
+
expect(jsonCall).toBeDefined();
|
|
45
|
+
expect(jsonCall[1]).toMatchObject({
|
|
46
|
+
type: "boolean",
|
|
47
|
+
default: false,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("should have scope option without default (optional parameter)", () => {
|
|
52
|
+
const builder = RecallCommand.builder as Function;
|
|
53
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
54
|
+
builder({ option: mockOption });
|
|
55
|
+
|
|
56
|
+
const calls = mockOption.mock.calls;
|
|
57
|
+
const scopeCall = calls.find((call: any[]) => call[0] === "scope");
|
|
58
|
+
expect(scopeCall).toBeDefined();
|
|
59
|
+
expect(scopeCall[1]).toMatchObject({
|
|
60
|
+
type: "string",
|
|
61
|
+
choices: ["project", "global"],
|
|
62
|
+
describe: "记忆作用域 (可选,不传则读取所有)",
|
|
63
|
+
});
|
|
64
|
+
// scope 是可选参数,不应该有默认值
|
|
65
|
+
expect(scopeCall[1].default).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("should NOT have extract option (migrated to record)", () => {
|
|
69
|
+
const builder = RecallCommand.builder as Function;
|
|
70
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
71
|
+
builder({ option: mockOption });
|
|
72
|
+
|
|
73
|
+
const calls = mockOption.mock.calls;
|
|
74
|
+
const extractCall = calls.find((call: any[]) => call[0] === "extract");
|
|
75
|
+
expect(extractCall).toBeUndefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("should NOT have session-id option (migrated to record)", () => {
|
|
79
|
+
const builder = RecallCommand.builder as Function;
|
|
80
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
81
|
+
builder({ option: mockOption });
|
|
82
|
+
|
|
83
|
+
const calls = mockOption.mock.calls;
|
|
84
|
+
const sessionIdCall = calls.find((call: any[]) => call[0] === "session-id");
|
|
85
|
+
expect(sessionIdCall).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("should NOT have require option (migrated to record)", () => {
|
|
89
|
+
const builder = RecallCommand.builder as Function;
|
|
90
|
+
const mockOption = vi.fn().mockReturnThis();
|
|
91
|
+
builder({ option: mockOption });
|
|
92
|
+
|
|
93
|
+
const calls = mockOption.mock.calls;
|
|
94
|
+
const requireCall = calls.find((call: any[]) => call[0] === "require");
|
|
95
|
+
expect(requireCall).toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("RecallOptions interface", () => {
|
|
100
|
+
test("should include basic options", () => {
|
|
101
|
+
const options = {
|
|
102
|
+
config: "/path/to/config",
|
|
103
|
+
json: true,
|
|
104
|
+
scope: "global" as const,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
expect(options.config).toBe("/path/to/config");
|
|
108
|
+
expect(options.json).toBe(true);
|
|
109
|
+
expect(options.scope).toBe("global");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("scope should accept both project and global", () => {
|
|
113
|
+
const projectOptions = { scope: "project" as const };
|
|
114
|
+
const globalOptions = { scope: "global" as const };
|
|
115
|
+
|
|
116
|
+
expect(projectOptions.scope).toBe("project");
|
|
117
|
+
expect(globalOptions.scope).toBe("global");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Memory Recall Command
|
|
3
|
+
*
|
|
4
|
+
* 命令:roy memory recall
|
|
5
|
+
*
|
|
6
|
+
* 读取所有 memory file 的内容
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { CommandModule } from "yargs";
|
|
10
|
+
import { EnvironmentService } from "../../services/environment.service";
|
|
11
|
+
import { OutputService } from "../../services/output.service";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import type {
|
|
14
|
+
MemoryComponent,
|
|
15
|
+
} from "@ai-setting/roy-agent-core";
|
|
16
|
+
|
|
17
|
+
interface RecallOptions {
|
|
18
|
+
config?: string;
|
|
19
|
+
json?: boolean;
|
|
20
|
+
/** 作用域 (可选,不传则读取所有) */
|
|
21
|
+
scope?: "project" | "global";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const RecallCommand: CommandModule<object, object> = {
|
|
25
|
+
command: "recall",
|
|
26
|
+
aliases: ["load"],
|
|
27
|
+
describe: "读取所有 memory file 的内容",
|
|
28
|
+
|
|
29
|
+
builder: (yargs) =>
|
|
30
|
+
yargs
|
|
31
|
+
.option("config", {
|
|
32
|
+
type: "string",
|
|
33
|
+
describe: "Config file path",
|
|
34
|
+
})
|
|
35
|
+
.option("json", {
|
|
36
|
+
alias: "j",
|
|
37
|
+
type: "boolean",
|
|
38
|
+
default: false,
|
|
39
|
+
description: "JSON 格式输出",
|
|
40
|
+
})
|
|
41
|
+
.option("scope", {
|
|
42
|
+
type: "string",
|
|
43
|
+
choices: ["project", "global"],
|
|
44
|
+
describe: "记忆作用域 (可选,不传则读取所有)",
|
|
45
|
+
}),
|
|
46
|
+
|
|
47
|
+
async handler(args) {
|
|
48
|
+
const a = args as any;
|
|
49
|
+
const output = new OutputService();
|
|
50
|
+
const envService = new EnvironmentService(output);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await envService.create({ configPath: a.config });
|
|
54
|
+
const env = envService.getEnvironment();
|
|
55
|
+
if (!env) {
|
|
56
|
+
output.error("Failed to create environment");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const memoryComponent = env.getComponent("memory") as MemoryComponent | undefined;
|
|
60
|
+
|
|
61
|
+
if (!memoryComponent) {
|
|
62
|
+
output.error("MemoryComponent not available");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 读取记忆
|
|
67
|
+
const content = await memoryComponent.recallMemory(a.scope);
|
|
68
|
+
|
|
69
|
+
if (!content) {
|
|
70
|
+
output.log(chalk.gray("(No memory files found)"));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (a.json) {
|
|
75
|
+
output.json({ content });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 直接输出 markdown 内容(处理转义字符)
|
|
80
|
+
output.log(content.replace(/\\n/g, "\n"));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
output.error(`Failed to recall memory: ${error}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
} finally {
|
|
85
|
+
await envService.dispose();
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|