@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,343 @@
1
+ /**
2
+ * @fileoverview TDD Test: record.ts should use PromptComponent for prompts
3
+ *
4
+ * RED Phase: Write failing test
5
+ *
6
+ * 验证 record.ts 应该:
7
+ * 1. 通过 PromptComponent 获取 prompt,而非直接调用 getBuiltInPrompt()
8
+ * 2. 确保 memory path 从配置读取
9
+ */
10
+
11
+ import { describe, expect, test, vi, beforeEach, afterEach } from "vitest";
12
+ import type { PromptComponent } from "@ai-setting/roy-agent-core";
13
+
14
+ // Mock chalk
15
+ vi.mock("chalk", () => ({
16
+ default: {
17
+ blue: (str: string) => str,
18
+ gray: (str: string) => str,
19
+ green: (str: string) => str,
20
+ red: (str: string) => str,
21
+ yellow: (str: string) => str,
22
+ cyan: (str: string) => str,
23
+ bold: (str: string) => str,
24
+ },
25
+ }));
26
+
27
+ describe("TDD: record.ts should use PromptComponent", () => {
28
+
29
+ // ========================================================================
30
+ // GREEN Phase: Minimal implementation to pass tests
31
+ // ========================================================================
32
+
33
+ /**
34
+ * Helper function to get prompt (simulates the desired implementation)
35
+ *
36
+ * This should be the actual implementation in record.ts after GREEN phase
37
+ */
38
+ async function getPromptFromComponent(
39
+ env: any,
40
+ promptName: string
41
+ ): Promise<string> {
42
+ const promptComponent = env?.getComponent?.("prompt") as PromptComponent | undefined;
43
+
44
+ if (promptComponent) {
45
+ return await promptComponent.getPrompt(promptName);
46
+ }
47
+
48
+ // Fallback to getBuiltInPrompt if PromptComponent not available
49
+ const { getBuiltInPrompt } = await import("@ai-setting/roy-agent-core");
50
+ return getBuiltInPrompt(promptName) || "";
51
+ }
52
+
53
+ describe("GREEN: getPromptFromComponent implementation", () => {
54
+
55
+ test("should get prompt from PromptComponent when available", async () => {
56
+ // Given: PromptComponent mock
57
+ const mockPromptComponent = {
58
+ getPrompt: vi.fn().mockResolvedValue("# Memory Agent Prompt\n\n{USER_REQUIREMENT}"),
59
+ has: vi.fn().mockReturnValue(true),
60
+ } as unknown as PromptComponent;
61
+
62
+ const mockEnv = {
63
+ getComponent: vi.fn().mockReturnValue(mockPromptComponent),
64
+ };
65
+
66
+ // When: Getting prompt
67
+ const prompt = await getPromptFromComponent(mockEnv, "project-memory");
68
+
69
+ // Then: Should be fetched via PromptComponent
70
+ expect(mockPromptComponent.getPrompt).toHaveBeenCalledWith("project-memory");
71
+ expect(prompt).toContain("{USER_REQUIREMENT}");
72
+ });
73
+
74
+ test("should fallback to getBuiltInPrompt when PromptComponent not available", async () => {
75
+ // Given: No PromptComponent
76
+ const mockEnv = {
77
+ getComponent: vi.fn().mockReturnValue(undefined),
78
+ };
79
+
80
+ // When: Getting prompt
81
+ const prompt = await getPromptFromComponent(mockEnv, "project-memory");
82
+
83
+ // Then: Should fallback to getBuiltInPrompt
84
+ expect(prompt).toBeDefined();
85
+ expect(prompt).toContain("项目记忆管理专家");
86
+ });
87
+
88
+ test("should support both project-memory and global-memory", async () => {
89
+ // Create a single mock PromptComponent with persistent mock
90
+ const mockGetPrompt = vi.fn()
91
+ .mockResolvedValueOnce("# Project Memory")
92
+ .mockResolvedValueOnce("# Global Memory");
93
+
94
+ const mockEnv = {
95
+ getComponent: vi.fn((name: string) => {
96
+ if (name === "prompt") {
97
+ return { getPrompt: mockGetPrompt };
98
+ }
99
+ return undefined;
100
+ }),
101
+ };
102
+
103
+ const projectPrompt = await getPromptFromComponent(mockEnv, "project-memory");
104
+ expect(projectPrompt).toBe("# Project Memory");
105
+
106
+ const globalPrompt = await getPromptFromComponent(mockEnv, "global-memory");
107
+ expect(globalPrompt).toBe("# Global Memory");
108
+ });
109
+
110
+ });
111
+
112
+ describe("Memory Path from Config (already working)", () => {
113
+
114
+ test("should get memory paths from MemoryComponent.config.memoryPaths", async () => {
115
+ // Given: MemoryComponent with configured paths
116
+ const mockMemoryComponent = {
117
+ recallMemory: vi.fn().mockResolvedValue("# Project Memory\n\n暂无内容"),
118
+ getMemoryPaths: vi.fn().mockReturnValue([
119
+ { type: "project", path: ".roy/memory" },
120
+ { type: "user", path: "~/.config/roy-agent/memory" },
121
+ ]),
122
+ getMemoryFile: vi.fn().mockReturnValue("memory.md"),
123
+ };
124
+
125
+ // When: Getting paths for scope
126
+ const paths = mockMemoryComponent.getMemoryPaths();
127
+ const projectPath = paths.find(p => p.type === "project");
128
+
129
+ // Then: Should return configured paths, not hardcoded
130
+ expect(projectPath?.path).toBe(".roy/memory");
131
+ expect(mockMemoryComponent.getMemoryPaths).toHaveBeenCalled();
132
+ });
133
+
134
+ test("should map scope 'global' to type 'user' in memory paths", async () => {
135
+ const mockMemoryComponent = {
136
+ recallMemory: vi.fn().mockImplementation((scope?: "project" | "global") => {
137
+ if (scope === "global") {
138
+ return Promise.resolve("# Global Memory\n\nUser preferences");
139
+ }
140
+ return Promise.resolve("# Project Memory\n\nProject settings");
141
+ }),
142
+ getMemoryPaths: vi.fn().mockReturnValue([
143
+ { type: "project", path: ".roy/memory" },
144
+ { type: "user", path: "~/.config/roy-agent/memory" },
145
+ ]),
146
+ };
147
+
148
+ const globalMemory = await mockMemoryComponent.recallMemory("global");
149
+ expect(globalMemory).toContain("User preferences");
150
+ expect(globalMemory).toContain("Global Memory");
151
+ });
152
+
153
+ });
154
+
155
+ describe("Integration: Full prompt flow with PromptComponent", () => {
156
+
157
+ test("should fetch prompt from PromptComponent and replace variables", async () => {
158
+ // Given: PromptComponent with prompt template
159
+ const promptTemplate = `# Memory Agent
160
+ Scope: {SCOPE}
161
+ User Requirement: {USER_REQUIREMENT}
162
+ Existing Memory: {CURRENT_MEMORY}`;
163
+
164
+ const mockPromptComponent = {
165
+ getPrompt: vi.fn().mockResolvedValue(promptTemplate),
166
+ } as unknown as PromptComponent;
167
+
168
+ const mockMemoryComponent = {
169
+ recallMemory: vi.fn().mockResolvedValue("# Previous memory content"),
170
+ };
171
+
172
+ // When: Building final prompt
173
+ const rawPrompt = await mockPromptComponent.getPrompt("project-memory");
174
+ const userRequirement = "Extract key project information";
175
+ const currentMemory = await mockMemoryComponent.recallMemory("project");
176
+
177
+ const finalPrompt = rawPrompt
178
+ .replace("{USER_REQUIREMENT}", userRequirement)
179
+ .replace("{SCOPE}", "project")
180
+ .replace("{CURRENT_MEMORY}", currentMemory || "(无现有记忆)");
181
+
182
+ // Then: Should have all variables replaced
183
+ expect(finalPrompt).toContain("Extract key project information");
184
+ expect(finalPrompt).toContain("project");
185
+ expect(finalPrompt).toContain("Previous memory content");
186
+ expect(finalPrompt).not.toContain("{USER_REQUIREMENT}");
187
+ expect(finalPrompt).not.toContain("{SCOPE}");
188
+ });
189
+
190
+ test("should use same pattern as default agent for consistency", async () => {
191
+ // The default agent uses: promptComponent.getPrompt("default")
192
+ // Memory agent should use: promptComponent.getPrompt("project-memory") or "global-memory"
193
+
194
+ const mockPromptComponent = {
195
+ getPrompt: vi.fn()
196
+ .mockResolvedValueOnce("Default Agent Prompt")
197
+ .mockResolvedValueOnce("Project Memory Prompt"),
198
+ } as unknown as PromptComponent;
199
+
200
+ // Default agent pattern
201
+ const defaultPrompt = await mockPromptComponent.getPrompt("default");
202
+ expect(mockPromptComponent.getPrompt).toHaveBeenNthCalledWith(1, "default");
203
+
204
+ // Memory agent pattern (should be consistent)
205
+ const memoryPrompt = await mockPromptComponent.getPrompt("project-memory");
206
+ expect(mockPromptComponent.getPrompt).toHaveBeenNthCalledWith(2, "project-memory");
207
+
208
+ expect(defaultPrompt).toBeDefined();
209
+ expect(memoryPrompt).toBeDefined();
210
+ });
211
+
212
+ });
213
+
214
+ describe("Environment integration", () => {
215
+
216
+ test("should get PromptComponent from env.getComponent('prompt')", async () => {
217
+ // Given: Mock environment with PromptComponent
218
+ const mockPromptComponent = {
219
+ getPrompt: vi.fn().mockResolvedValue("# Test Prompt"),
220
+ has: vi.fn().mockReturnValue(true),
221
+ } as unknown as PromptComponent;
222
+
223
+ const mockEnv = {
224
+ getComponent: vi.fn((name: string) => {
225
+ if (name === "prompt") return mockPromptComponent;
226
+ if (name === "memory") return {
227
+ recallMemory: vi.fn().mockResolvedValue(""),
228
+ getMemoryPaths: vi.fn().mockReturnValue([]),
229
+ };
230
+ return undefined;
231
+ }),
232
+ };
233
+
234
+ // When: Getting prompt component from env
235
+ const promptComponent = mockEnv.getComponent("prompt") as PromptComponent;
236
+ const prompt = await promptComponent.getPrompt("project-memory");
237
+
238
+ // Then: Should work via env
239
+ expect(mockEnv.getComponent).toHaveBeenCalledWith("prompt");
240
+ expect(promptComponent.getPrompt).toHaveBeenCalledWith("project-memory");
241
+ expect(prompt).toBeDefined();
242
+ });
243
+
244
+ test("should handle missing PromptComponent gracefully", async () => {
245
+ // Given: Environment without PromptComponent
246
+ const mockEnv = {
247
+ getComponent: vi.fn().mockReturnValue(undefined),
248
+ };
249
+
250
+ // When: Trying to get PromptComponent
251
+ const promptComponent = mockEnv.getComponent("prompt");
252
+
253
+ // Then: Should handle gracefully (fallback to getBuiltInPrompt)
254
+ expect(promptComponent).toBeUndefined();
255
+
256
+ // Fallback should work
257
+ const { getBuiltInPrompt } = await import("@ai-setting/roy-agent-core");
258
+ const fallbackPrompt = getBuiltInPrompt("project-memory");
259
+ expect(fallbackPrompt).toBeDefined();
260
+ });
261
+
262
+ });
263
+
264
+ // ========================================================================
265
+ // REFACTOR Phase: Verify implementation matches expected pattern
266
+ // ========================================================================
267
+
268
+ describe("REFACTOR: Verify record.ts implementation pattern", () => {
269
+
270
+ test("record.ts should follow getPromptFromComponent pattern", async () => {
271
+ // This test verifies that the helper function matches expected pattern
272
+ // After GREEN phase, record.ts should be refactored to use this pattern
273
+
274
+ // Mock environment with all required components
275
+ const mockMemoryComponent = {
276
+ recallMemory: vi.fn().mockResolvedValue("# Existing memory"),
277
+ getMemoryPaths: vi.fn().mockReturnValue([
278
+ { type: "project", path: ".roy/memory" },
279
+ ]),
280
+ getMemoryFile: vi.fn().mockReturnValue("memory.md"),
281
+ };
282
+
283
+ const mockPromptComponent = {
284
+ getPrompt: vi.fn().mockResolvedValue("# Memory Agent Prompt"),
285
+ has: vi.fn().mockReturnValue(true),
286
+ } as unknown as PromptComponent;
287
+
288
+ const mockEnv = {
289
+ getComponent: vi.fn((name: string) => {
290
+ if (name === "prompt") return mockPromptComponent;
291
+ if (name === "memory") return mockMemoryComponent;
292
+ return undefined;
293
+ }),
294
+ };
295
+
296
+ // Simulate what record.ts should do after refactoring
297
+ const scope = "project";
298
+ const promptName = `${scope}-memory`;
299
+
300
+ // Get prompt via PromptComponent (NEW pattern)
301
+ const prompt = await getPromptFromComponent(mockEnv, promptName);
302
+
303
+ // Get memory via MemoryComponent (already correct)
304
+ const currentMemory = await mockMemoryComponent.recallMemory(scope);
305
+
306
+ // Verify
307
+ expect(mockPromptComponent.getPrompt).toHaveBeenCalledWith(promptName);
308
+ expect(mockMemoryComponent.recallMemory).toHaveBeenCalledWith(scope);
309
+ expect(prompt).toBeDefined();
310
+ expect(currentMemory).toBeDefined();
311
+ });
312
+
313
+ test("record.ts should NOT call getBuiltInPrompt directly", async () => {
314
+ // This test documents the expected behavior
315
+ // After GREEN phase, record.ts should NOT import getBuiltInPrompt directly
316
+
317
+ // OLD pattern (should be removed):
318
+ // import { getBuiltInPrompt } from "@ai-setting/roy-agent-core";
319
+ // const prompt = getBuiltInPrompt("project-memory");
320
+
321
+ // NEW pattern (should be used):
322
+ // const prompt = await promptComponent.getPrompt("project-memory");
323
+
324
+ // Verify the NEW pattern works
325
+ const mockEnv = {
326
+ getComponent: vi.fn((name: string) => {
327
+ if (name === "prompt") {
328
+ return {
329
+ getPrompt: vi.fn().mockResolvedValue("# Memory Agent"),
330
+ };
331
+ }
332
+ return undefined;
333
+ }),
334
+ };
335
+
336
+ const prompt = await getPromptFromComponent(mockEnv, "project-memory");
337
+ expect(prompt).toBe("# Memory Agent");
338
+ expect(mockEnv.getComponent).toHaveBeenCalledWith("prompt");
339
+ });
340
+
341
+ });
342
+
343
+ });
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @fileoverview Memory Record Command Tests (TDD)
3
+ *
4
+ * TDD 测试:record 命令的 --extract 功能
5
+ */
6
+
7
+ import { describe, expect, test, vi } from "vitest";
8
+ import { RecordCommand } from "./record";
9
+
10
+ describe("RecordCommand", () => {
11
+ describe("command configuration", () => {
12
+ test("should have correct command name", () => {
13
+ expect(RecordCommand.command).toBe("record");
14
+ });
15
+
16
+ test("should have aliases", () => {
17
+ expect(RecordCommand.aliases).toContain("save");
18
+ });
19
+
20
+ test("should have description", () => {
21
+ expect(RecordCommand.describe).toBe("记录记忆内容到 memory file");
22
+ });
23
+ });
24
+
25
+ describe("RecordOptions interface", () => {
26
+ test("should include extract option", () => {
27
+ const options: any = {
28
+ mode: "append",
29
+ extract: true,
30
+ scope: "global",
31
+ "session-id": "session-abc-123",
32
+ require: "提取项目配置信息",
33
+ };
34
+
35
+ expect(options.extract).toBe(true);
36
+ expect(options.scope).toBe("global");
37
+ expect(options["session-id"]).toBe("session-abc-123");
38
+ expect(options.require).toBe("提取项目配置信息");
39
+ });
40
+
41
+ test("scope should accept both project and global", () => {
42
+ const projectOptions = { scope: "project" as const };
43
+ const globalOptions = { scope: "global" as const };
44
+
45
+ expect(projectOptions.scope).toBe("project");
46
+ expect(globalOptions.scope).toBe("global");
47
+ });
48
+
49
+ test("mode should accept standard modes", () => {
50
+ const modes = ["append", "prepend", "overwrite", "delete"];
51
+ modes.forEach(mode => {
52
+ expect(["append", "prepend", "overwrite", "delete"]).toContain(mode);
53
+ });
54
+ });
55
+
56
+ test("should support extract mode", () => {
57
+ const extractOptions = {
58
+ extract: true,
59
+ scope: "project" as const,
60
+ "session-id": "test-session",
61
+ require: "重点关注配置信息",
62
+ };
63
+
64
+ expect(extractOptions.extract).toBe(true);
65
+ expect(extractOptions.scope).toBe("project");
66
+ });
67
+ });
68
+
69
+ describe("extract functionality", () => {
70
+ test("extract mode should not require content", () => {
71
+ // Extract 模式下,不需要提供 content
72
+ const extractOnly = {
73
+ extract: true,
74
+ mode: "append" as const,
75
+ };
76
+
77
+ expect(extractOnly.extract).toBe(true);
78
+ // 不需要 content 属性
79
+ expect((extractOnly as any).content).toBeUndefined();
80
+ });
81
+
82
+ test("non-extract mode requires content for non-delete", () => {
83
+ // 普通模式下,mode 不是 delete 时需要 content
84
+ const normalMode = {
85
+ mode: "append" as const,
86
+ content: "Some memory content",
87
+ };
88
+
89
+ expect(normalMode.content).toBeDefined();
90
+ });
91
+ });
92
+ });