deeper-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 (188) hide show
  1. package/README.md +254 -0
  2. package/dist/cli/index.d.ts +1 -0
  3. package/dist/cli/index.js +12067 -0
  4. package/dist/cli/index.js.map +1 -0
  5. package/dist/index.d.ts +415 -0
  6. package/dist/index.js +1599 -0
  7. package/dist/index.js.map +1 -0
  8. package/docs/superpowers/plans/2026-05-14-deepercode-implementation.md +24 -0
  9. package/docs/superpowers/plans/2026-05-14-deepercode-plan.md +1248 -0
  10. package/docs/superpowers/specs/2026-05-14-deepercode-design.md +560 -0
  11. package/package.json +60 -0
  12. package/src/cli/bootstrap.ts +69 -0
  13. package/src/cli/chat-repl.ts +932 -0
  14. package/src/cli/commands/chat.ts +39 -0
  15. package/src/cli/commands/chat.tsx +39 -0
  16. package/src/cli/commands/config.ts +133 -0
  17. package/src/cli/commands/mcp.ts +172 -0
  18. package/src/cli/commands/run.ts +147 -0
  19. package/src/cli/commands/skill.ts +152 -0
  20. package/src/cli/index.ts +184 -0
  21. package/src/core/bugscan.ts +145 -0
  22. package/src/core/config.ts +285 -0
  23. package/src/core/constants.ts +49 -0
  24. package/src/core/eventbus.ts +202 -0
  25. package/src/core/logger.ts +109 -0
  26. package/src/core/storage.ts +96 -0
  27. package/src/index.ts +26 -0
  28. package/src/mcp/ConfigLoader.ts +74 -0
  29. package/src/mcp/MCPClient.ts +326 -0
  30. package/src/mcp/ResourceAdapter.ts +58 -0
  31. package/src/mcp/SSETransport.ts +133 -0
  32. package/src/mcp/StdioTransport.ts +116 -0
  33. package/src/mcp/ToolAdapter.ts +71 -0
  34. package/src/mcp/types.ts +58 -0
  35. package/src/memory/xmemory.ts +275 -0
  36. package/src/model/DeepSeekClient.ts +292 -0
  37. package/src/model/MessageBuilder.ts +155 -0
  38. package/src/model/RetryManager.ts +82 -0
  39. package/src/model/StreamHandler.ts +158 -0
  40. package/src/model/types.ts +86 -0
  41. package/src/skills/SkillCreator.ts +153 -0
  42. package/src/skills/SkillEngine.ts +158 -0
  43. package/src/skills/SkillExecutor.ts +107 -0
  44. package/src/skills/SkillLoader.ts +182 -0
  45. package/src/skills/SkillRegistry.ts +73 -0
  46. package/src/skills/SkillTrigger.ts +82 -0
  47. package/src/skills/types.ts +28 -0
  48. package/src/tools/ToolExecutor.ts +103 -0
  49. package/src/tools/ToolRegistry.ts +71 -0
  50. package/src/tools/ToolValidator.ts +103 -0
  51. package/src/tools/builtin/ai/context_summarize.ts +76 -0
  52. package/src/tools/builtin/ai/memory_store.ts +86 -0
  53. package/src/tools/builtin/ai/prompt_template.ts +71 -0
  54. package/src/tools/builtin/ai/skill_create.ts +53 -0
  55. package/src/tools/builtin/ai/subagent.ts +39 -0
  56. package/src/tools/builtin/ai/todo_manager.ts +157 -0
  57. package/src/tools/builtin/ai/token_count.ts +196 -0
  58. package/src/tools/builtin/ai/tool_create.ts +52 -0
  59. package/src/tools/builtin/code/analyze_deps.ts +72 -0
  60. package/src/tools/builtin/code/bug_scan.ts +80 -0
  61. package/src/tools/builtin/code/code_metrics.ts +111 -0
  62. package/src/tools/builtin/code/extract_function.ts +86 -0
  63. package/src/tools/builtin/code/format_code.ts +57 -0
  64. package/src/tools/builtin/code/generate_code.ts +75 -0
  65. package/src/tools/builtin/code/import_organizer.ts +82 -0
  66. package/src/tools/builtin/code/lint_code.ts +48 -0
  67. package/src/tools/builtin/code/parse_ast.ts +86 -0
  68. package/src/tools/builtin/code/refactor_code.ts +63 -0
  69. package/src/tools/builtin/code/type_check.ts +48 -0
  70. package/src/tools/builtin/data/chart_generate.ts +62 -0
  71. package/src/tools/builtin/data/csv_parse.ts +56 -0
  72. package/src/tools/builtin/data/data_diff.ts +79 -0
  73. package/src/tools/builtin/data/data_transform.ts +74 -0
  74. package/src/tools/builtin/data/data_validate.ts +75 -0
  75. package/src/tools/builtin/data/json_parse.ts +71 -0
  76. package/src/tools/builtin/data/template_render.ts +58 -0
  77. package/src/tools/builtin/data/toml_parse.ts +42 -0
  78. package/src/tools/builtin/data/xml_parse.ts +79 -0
  79. package/src/tools/builtin/data/yaml_parse.ts +42 -0
  80. package/src/tools/builtin/database/db_backup.ts +53 -0
  81. package/src/tools/builtin/database/db_restore.ts +51 -0
  82. package/src/tools/builtin/database/db_schema.ts +66 -0
  83. package/src/tools/builtin/database/nosql_query.ts +50 -0
  84. package/src/tools/builtin/database/orm_generate.ts +66 -0
  85. package/src/tools/builtin/database/redis_command.ts +46 -0
  86. package/src/tools/builtin/database/sql_migrate.ts +55 -0
  87. package/src/tools/builtin/database/sql_query.ts +60 -0
  88. package/src/tools/builtin/filesystem/batch_read.ts +56 -0
  89. package/src/tools/builtin/filesystem/batch_write.ts +67 -0
  90. package/src/tools/builtin/filesystem/copy_file.ts +36 -0
  91. package/src/tools/builtin/filesystem/create_dir.ts +30 -0
  92. package/src/tools/builtin/filesystem/delete_file.ts +30 -0
  93. package/src/tools/builtin/filesystem/diff_files.ts +47 -0
  94. package/src/tools/builtin/filesystem/edit_file.ts +47 -0
  95. package/src/tools/builtin/filesystem/file_info.ts +52 -0
  96. package/src/tools/builtin/filesystem/glob_find.ts +44 -0
  97. package/src/tools/builtin/filesystem/list_dir.ts +51 -0
  98. package/src/tools/builtin/filesystem/merge_files.ts +44 -0
  99. package/src/tools/builtin/filesystem/move_file.ts +37 -0
  100. package/src/tools/builtin/filesystem/read_file.ts +55 -0
  101. package/src/tools/builtin/filesystem/watch_file.ts +33 -0
  102. package/src/tools/builtin/filesystem/write_file.ts +45 -0
  103. package/src/tools/builtin/index.ts +244 -0
  104. package/src/tools/builtin/network/api_call.ts +79 -0
  105. package/src/tools/builtin/network/browser_action.ts +54 -0
  106. package/src/tools/builtin/network/check_url.ts +59 -0
  107. package/src/tools/builtin/network/download_file.ts +64 -0
  108. package/src/tools/builtin/network/graphql_query.ts +46 -0
  109. package/src/tools/builtin/network/http_request.ts +61 -0
  110. package/src/tools/builtin/network/parse_html.ts +101 -0
  111. package/src/tools/builtin/network/proxy_request.ts +53 -0
  112. package/src/tools/builtin/network/screenshot_page.ts +58 -0
  113. package/src/tools/builtin/network/web_fetch.ts +70 -0
  114. package/src/tools/builtin/network/web_search.ts +128 -0
  115. package/src/tools/builtin/network/websocket_connect.ts +70 -0
  116. package/src/tools/builtin/project/build_project.ts +68 -0
  117. package/src/tools/builtin/project/config_manage.ts +99 -0
  118. package/src/tools/builtin/project/coverage_report.ts +59 -0
  119. package/src/tools/builtin/project/docker_manage.ts +90 -0
  120. package/src/tools/builtin/project/env_manage.ts +88 -0
  121. package/src/tools/builtin/project/npm_manage.ts +71 -0
  122. package/src/tools/builtin/project/project_init.ts +59 -0
  123. package/src/tools/builtin/project/run_test.ts +74 -0
  124. package/src/tools/builtin/search/codebase_search.ts +76 -0
  125. package/src/tools/builtin/search/find_definition.ts +84 -0
  126. package/src/tools/builtin/search/find_references.ts +75 -0
  127. package/src/tools/builtin/search/fuzzy_find.ts +75 -0
  128. package/src/tools/builtin/search/grep_search.ts +90 -0
  129. package/src/tools/builtin/search/regex_find.ts +91 -0
  130. package/src/tools/builtin/search/search_docs.ts +51 -0
  131. package/src/tools/builtin/search/search_package.ts +50 -0
  132. package/src/tools/builtin/search/symbol_search.ts +82 -0
  133. package/src/tools/builtin/search/text_search.ts +63 -0
  134. package/src/tools/builtin/security/decrypt_file.ts +54 -0
  135. package/src/tools/builtin/security/encrypt_file.ts +52 -0
  136. package/src/tools/builtin/security/hash_generate.ts +48 -0
  137. package/src/tools/builtin/security/jwt_decode.ts +53 -0
  138. package/src/tools/builtin/security/secret_scan.ts +82 -0
  139. package/src/tools/builtin/security/vulnerability_check.ts +71 -0
  140. package/src/tools/builtin/shell/background_terminal.ts +38 -0
  141. package/src/tools/builtin/shell/check_status.ts +48 -0
  142. package/src/tools/builtin/shell/interactive_terminal.ts +31 -0
  143. package/src/tools/builtin/shell/kill_terminal.ts +29 -0
  144. package/src/tools/builtin/shell/list_terminals.ts +61 -0
  145. package/src/tools/builtin/shell/pipe_commands.ts +55 -0
  146. package/src/tools/builtin/shell/process-pool.ts +150 -0
  147. package/src/tools/builtin/shell/run_async.ts +73 -0
  148. package/src/tools/builtin/shell/run_command.ts +60 -0
  149. package/src/tools/builtin/shell/send_ctrl_keys.ts +43 -0
  150. package/src/tools/builtin/shell/send_keys.ts +36 -0
  151. package/src/tools/builtin/shell/send_text.ts +35 -0
  152. package/src/tools/builtin/shell/shell_script.ts +65 -0
  153. package/src/tools/builtin/shell/stop_command.ts +40 -0
  154. package/src/tools/builtin/shell/terminal_resize.ts +31 -0
  155. package/src/tools/builtin/shell/terminal_screenshot.ts +28 -0
  156. package/src/tools/builtin/system/log_viewer.ts +89 -0
  157. package/src/tools/builtin/system/notify_user.ts +55 -0
  158. package/src/tools/builtin/system/process_list.ts +66 -0
  159. package/src/tools/builtin/system/resource_monitor.ts +66 -0
  160. package/src/tools/builtin/system/system_info.ts +41 -0
  161. package/src/tools/tool-types.ts +97 -0
  162. package/src/ui/AgentTree.tsx +98 -0
  163. package/src/ui/App.tsx +46 -0
  164. package/src/ui/ChatView.tsx +278 -0
  165. package/src/ui/ConfirmDialog.tsx +68 -0
  166. package/src/ui/DiffView.tsx +64 -0
  167. package/src/ui/FilePreview.tsx +59 -0
  168. package/src/ui/InputBox.tsx +267 -0
  169. package/src/ui/MessageBubble.tsx +30 -0
  170. package/src/ui/Spinner.tsx +35 -0
  171. package/src/ui/StatusBar.tsx +41 -0
  172. package/src/ui/ToolCallCard.tsx +73 -0
  173. package/src/ui/ansi.ts +50 -0
  174. package/src/ui/markdown.ts +238 -0
  175. package/src/ui/themes/dark.ts +4 -0
  176. package/src/ui/themes/default.ts +25 -0
  177. package/src/ui/themes/light.ts +14 -0
  178. package/tests/unit/BuiltinTools.test.ts +129 -0
  179. package/tests/unit/BuiltinToolsIntegration.test.ts +111 -0
  180. package/tests/unit/FilesystemTools.test.ts +211 -0
  181. package/tests/unit/SkillLoader.test.ts +141 -0
  182. package/tests/unit/SkillRegistry.test.ts +113 -0
  183. package/tests/unit/ToolExecutor.test.ts +160 -0
  184. package/tests/unit/ToolRegistry.test.ts +103 -0
  185. package/tests/unit/ToolValidator.test.ts +137 -0
  186. package/tsconfig.json +28 -0
  187. package/tsup.config.ts +17 -0
  188. package/vitest.config.ts +20 -0
@@ -0,0 +1,238 @@
1
+ const A = { R: '\x1b[0m', b: '\x1b[1m', d: '\x1b[2m', i: '\x1b[3m', u: '\x1b[4m', g: '\x1b[32m', inv: '\x1b[7m' };
2
+ function ansi(code: string, text: string) { return code + text + A.R; }
3
+
4
+ export class MarkdownStreamRenderer {
5
+ private lineBuf = '';
6
+ private inCodeBlock = false;
7
+ private codeLang = '';
8
+ private codeContent = '';
9
+ private codeDropped = 0;
10
+ private seenFirstLine = false;
11
+ private tableRows: string[] = [];
12
+ private inTable = false;
13
+ private tableAligns: ('L'|'C'|'R')[] = [];
14
+
15
+ private static readonly MAX_LINE = 4096;
16
+ private static readonly MAX_CODE = 51200;
17
+
18
+ feed(chunk: string): string | null {
19
+ this.lineBuf += chunk;
20
+ if (this.lineBuf.length > MarkdownStreamRenderer.MAX_LINE) this.lineBuf = this.lineBuf.slice(-MarkdownStreamRenderer.MAX_LINE);
21
+
22
+ const idx = this.lineBuf.indexOf('\n');
23
+ if (idx === -1) return null;
24
+ const line = this.lineBuf.slice(0, idx);
25
+ this.lineBuf = this.lineBuf.slice(idx + 1);
26
+
27
+ if (this.inCodeBlock) {
28
+ if (/^```\s*$/.test(line.trim())) {
29
+ this.inCodeBlock = false;
30
+ let out = this.flushCodeBlock() + '\n';
31
+ if (this.codeLang.toLowerCase() === 'markdown') out = '';
32
+ return out;
33
+ }
34
+ if (this.codeContent.length < MarkdownStreamRenderer.MAX_CODE) {
35
+ this.codeContent += line + '\n';
36
+ } else {
37
+ this.codeDropped++;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ if (/^```(\w*)\s*$/.test(line.trim())) {
43
+ this.inCodeBlock = true;
44
+ this.codeLang = line.trim().slice(3).trim();
45
+ this.codeContent = '';
46
+ if (!this.seenFirstLine) { this.seenFirstLine = true; return ansi(A.d, ` ┌ ${this.codeLang || 'code'}`) + '\n'; }
47
+ return `\n` + ansi(A.d, ` ┌ ${this.codeLang || 'code'}`) + '\n';
48
+ }
49
+
50
+ // ── Table detection ──
51
+ const isPipeLine = /^\|.+|\s+\|/.test(line) && line.includes('|');
52
+ const isSepLine = /^\|?[\s\-:]+(\|[\s\-:]+)+\|?\s*$/.test(line.trim()) && !/[a-zA-Z\u4e00-\u9fff]/.test(line.trim());
53
+
54
+ if (isPipeLine) {
55
+ if (!this.inTable) {
56
+ this.inTable = true;
57
+ this.tableRows = [];
58
+ this.tableAligns = [];
59
+ }
60
+ if (isSepLine) {
61
+ // Parse alignment from separator row
62
+ const cells = line.split('|').filter(c => /[\-:]/.test(c));
63
+ this.tableAligns = cells.map(c => {
64
+ const t = c.trim();
65
+ if (t.startsWith(':') && t.endsWith(':')) return 'C';
66
+ if (t.endsWith(':')) return 'R';
67
+ return 'L';
68
+ });
69
+ return null;
70
+ }
71
+ this.tableRows.push(line);
72
+ return null;
73
+ }
74
+
75
+ // End of table
76
+ if (this.inTable) {
77
+ const out = this.renderTable();
78
+ this.inTable = false;
79
+ this.tableRows = [];
80
+ this.tableAligns = [];
81
+ this.seenFirstLine = true;
82
+ return out + '\n' + this.renderLine(line) + '\n';
83
+ }
84
+
85
+ this.seenFirstLine = true;
86
+ return this.renderLine(line) + '\n';
87
+ }
88
+
89
+ flush(): string {
90
+ let out = '';
91
+ if (this.inTable) {
92
+ out += this.renderTable();
93
+ this.inTable = false;
94
+ this.tableRows = [];
95
+ }
96
+ if (this.inCodeBlock) {
97
+ out += this.flushCodeBlock();
98
+ this.inCodeBlock = false;
99
+ }
100
+ if (this.lineBuf) {
101
+ out += this.lineBuf;
102
+ this.lineBuf = '';
103
+ }
104
+ return out;
105
+ }
106
+
107
+ reset(): void {
108
+ this.lineBuf = ''; this.inCodeBlock = false; this.codeLang = ''; this.codeContent = ''; this.codeDropped = 0; this.seenFirstLine = false;
109
+ this.inTable = false; this.tableRows = []; this.tableAligns = [];
110
+ }
111
+
112
+ // ── Table rendering ──
113
+
114
+ private renderTable(): string {
115
+ if (this.tableRows.length === 0) return '';
116
+ const headerRow = this.tableRows[0];
117
+ const dataRows = this.tableRows.slice(this.tableAligns.length === 0 ? 0 : 1);
118
+ const headerCells = headerRow ? this.splitCells(headerRow) : [];
119
+ const allRows: string[][] = [];
120
+
121
+ if (this.tableAligns.length > 0 && headerRow) {
122
+ allRows.push(headerCells);
123
+ allRows.push(...dataRows.map(r => this.splitCells(r)));
124
+ } else {
125
+ allRows.push(...this.tableRows.map(r => this.splitCells(r)));
126
+ }
127
+
128
+ const colCount = Math.max(...allRows.map(r => r.length), 1);
129
+ const colWidths: number[] = Array(colCount).fill(3);
130
+ for (const row of allRows) {
131
+ for (let i = 0; i < row.length; i++) {
132
+ const w = this.visualLen(row[i]);
133
+ if (w > colWidths[i]) colWidths[i] = Math.min(w, 30);
134
+ }
135
+ }
136
+
137
+ const aligns = Array(colCount).fill('L');
138
+ for (let i = 0; i < this.tableAligns.length && i < colCount; i++) aligns[i] = this.tableAligns[i];
139
+
140
+ const F = ansi(A.d + '\x1b[90m', '│');
141
+ let out = '';
142
+
143
+ // Top border
144
+ out += A.d + '\x1b[90m' + ' ┌' + colWidths.map(w => '─'.repeat(Math.max(0, w + 2))).join('┬') + '┐' + A.R + '\n';
145
+
146
+ // Header row
147
+ if (allRows.length > 0 && colWidths.length > 0) {
148
+ out += ` ${F} ` + allRows[0].map((c, i) => this.padCell(c, colWidths[i] || 3, 'C', true)).join(` ${F} `) + ` ${F}\n`;
149
+ // Separator
150
+ out += A.d + '\x1b[90m' + ' ├' + colWidths.map(w => '─'.repeat(Math.max(0, w + 2))).join('┼') + '┤' + A.R + '\n';
151
+ }
152
+
153
+ // Data rows
154
+ const maxRows = 12;
155
+ for (let r = 1; r < allRows.length && r <= maxRows; r++) {
156
+ out += ` ${F} ` + allRows[r].map((c, i) => this.padCell(c, colWidths[i] || 3, aligns[i] || 'L', false)).join(` ${F} `) + ` ${F}\n`;
157
+ }
158
+ if (allRows.length > maxRows + 1) {
159
+ out += ansi(A.d + '\x1b[90m', ` ${F} ${ansi(A.d, '...')} `.padEnd(colWidths.reduce((a, w) => a + w + 3, 3))) + ` ${F}` + '\n';
160
+ }
161
+
162
+ // Bottom border
163
+ out += A.d + '\x1b[90m' + ' └' + colWidths.map(w => '─'.repeat(Math.max(0, w + 2))).join('┴') + '┘' + A.R;
164
+ return out;
165
+ }
166
+
167
+ private splitCells(line: string): string[] {
168
+ let s = line.trim();
169
+ if (s.startsWith('|')) s = s.slice(1);
170
+ if (s.endsWith('|')) s = s.slice(0, -1);
171
+ return s.split('|').map(c => c.trim());
172
+ }
173
+
174
+ private visualLen(s: string): number {
175
+ return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/[^\x00-\xff]/g, ' ').length;
176
+ }
177
+
178
+ private padCell(text: string, width: number, align: string, bold: boolean): string {
179
+ const raw = text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
180
+ const visual = this.visualLen(raw);
181
+ const pad = Math.max(0, width - visual);
182
+ const left = align === 'R' ? pad : align === 'C' ? Math.floor(pad / 2) : 0;
183
+ const right = Math.max(0, pad - left);
184
+ const content = bold ? ansi(A.b, raw) : raw;
185
+ return ' '.repeat(left) + content + ' '.repeat(right);
186
+ }
187
+
188
+ // ── Flush code ──
189
+
190
+ private flushCodeBlock(): string {
191
+ const lines = this.codeContent.split('\n').filter(l => l || true);
192
+ let out = '';
193
+ const max = Math.min(lines.length, 20);
194
+ for (let i = 0; i < max; i++) {
195
+ out += ` ${ansi(A.d + '\x1b[90m', String(i + 1).padStart(3, ' '))} ${ansi(A.d, '│')} ${lines[i]}\n`;
196
+ }
197
+ if (lines.length > 20) out += ansi(A.d + '\x1b[90m', ` ... (${lines.length - 20} lines omitted)\n`);
198
+ if (this.codeDropped > 0) out += ansi(A.d + '\x1b[90m', ` ... (${this.codeDropped} lines dropped, code too large)\n`);
199
+ out += ansi(A.d, ` └ ${this.codeLang || 'code'}`);
200
+ this.codeContent = '';
201
+ this.codeDropped = 0;
202
+ return out;
203
+ }
204
+
205
+ // ── Render line ──
206
+
207
+ private renderLine(line: string): string {
208
+ const t = line.trim();
209
+ if (t === '') return '';
210
+
211
+ let m: RegExpMatchArray | null;
212
+ if ((m = line.match(/^#{4}\s+(.+)/))) return ansi(A.b + '\x1b[36m', ` ## ${m[1]}`);
213
+ if ((m = line.match(/^#{3}\s+(.+)/))) return ansi(A.b + '\x1b[33m', ` ▸ ${m[1]}`);
214
+ if ((m = line.match(/^#{2}\s+(.+)/))) return ansi(A.b + '\x1b[34m', ` ▸▸ ${m[1]}`);
215
+ if ((m = line.match(/^#\s+(.+)/))) return ansi(A.b + '\x1b[35m', ` █ ${m[1]}`);
216
+ if (line.startsWith('> ')) return ansi(A.d + '\x1b[90m', ` │ ${line.slice(2)}`);
217
+ if (/^[-*_]{3,}\s*$/.test(t) && !t.includes('|')) return ansi(A.d + '\x1b[90m', ' ──'.repeat(12));
218
+ if ((m = line.match(/^(\s*)(\d+)\.\s+(.+)/))) { const sp = m[1].length; return ' '.repeat(Math.max(0, sp)) + ansi(A.b + '\x1b[36m', `${m[2]}.`) + ' ' + this.inline(m[3]); }
219
+ if ((m = line.match(/^(\s*)[*+-]\s+(.+)/))) { const sp = m[1].length; return ' '.repeat(Math.max(0, sp)) + ansi(A.g, '• ') + this.inline(m[2]); }
220
+ if ((m = line.match(/^(\s*)- \[([ x])\]\s+(.+)/))) {
221
+ const ck = m[2] === 'x' ? ansi(A.g, '✓') : ansi(A.d + '\x1b[90m', '○');
222
+ return ` ${ck} ${this.inline(m[3])}`;
223
+ }
224
+ return ' ' + this.inline(line);
225
+ }
226
+
227
+ private inline(text: string): string {
228
+ let s = text;
229
+ s = s.replace(/`([^`]+)`/g, (_, c) => ansi(A.inv + '\x1b[33m', c));
230
+ s = s.replace(/\*\*\*(.+?)\*\*\*/g, (_, c) => ansi(A.b + A.i, c));
231
+ s = s.replace(/\*\*(.+?)\*\*/g, (_, c) => ansi(A.b, c));
232
+ s = s.replace(/\*(.+?)\*/g, (_, c) => ansi(A.i, c));
233
+ s = s.replace(/~~(.+?)~~/g, (_, c) => ansi(A.d + '\x1b[9m', c));
234
+ s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, txt, url) =>
235
+ ansi(A.u + '\x1b[36m', txt) + ansi(A.d + '\x1b[90m', ` (${url.slice(0, 40)})`));
236
+ return s;
237
+ }
238
+ }
@@ -0,0 +1,4 @@
1
+ import type { Theme } from './default.ts';
2
+ import { defaultTheme } from './default.ts';
3
+
4
+ export const darkTheme: Theme = { ...defaultTheme };
@@ -0,0 +1,25 @@
1
+ export interface Theme {
2
+ primary: string;
3
+ secondary: string;
4
+ success: string;
5
+ warning: string;
6
+ error: string;
7
+ text: string;
8
+ dimText: string;
9
+ border: string;
10
+ background: string;
11
+ cardBackground: string;
12
+ }
13
+
14
+ export const defaultTheme: Theme = {
15
+ primary: '#00BFA5',
16
+ secondary: '#7C4DFF',
17
+ success: '#00E676',
18
+ warning: '#FFAB40',
19
+ error: '#FF5252',
20
+ text: '#E0E0E0',
21
+ dimText: '#757575',
22
+ border: '#424242',
23
+ background: '#121212',
24
+ cardBackground: '#1E1E1E',
25
+ };
@@ -0,0 +1,14 @@
1
+ import type { Theme } from './default.ts';
2
+
3
+ export const lightTheme: Theme = {
4
+ primary: '#00897B',
5
+ secondary: '#6200EA',
6
+ success: '#00C853',
7
+ warning: '#FF6D00',
8
+ error: '#D50000',
9
+ text: '#212121',
10
+ dimText: '#9E9E9E',
11
+ border: '#E0E0E0',
12
+ background: '#FAFAFA',
13
+ cardBackground: '#FFFFFF',
14
+ };
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { writeFileSync, mkdirSync, rmSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { randomUUID } from 'node:crypto';
6
+
7
+ describe('GrepSearch 工具', () => {
8
+ let testDir: string;
9
+
10
+ function setup() {
11
+ testDir = join(tmpdir(), `deeper-grep-${randomUUID()}`);
12
+ mkdirSync(testDir, { recursive: true });
13
+ writeFileSync(join(testDir, 'file1.ts'), 'export function hello() {\n return "world";\n}\n', 'utf-8');
14
+ writeFileSync(join(testDir, 'file2.ts'), 'const x = hello();\nconst y = 42;\n', 'utf-8');
15
+ mkdirSync(join(testDir, 'subdir'), { recursive: true });
16
+ writeFileSync(join(testDir, 'subdir', 'nested.ts'), 'import { test } from "vitest";', 'utf-8');
17
+ return testDir;
18
+ }
19
+
20
+ function cleanup() {
21
+ if (existsSync(testDir)) rmSync(testDir, { recursive: true, force: true });
22
+ }
23
+
24
+ it('grep_search 应找到匹配的行', async () => {
25
+ const dir = setup();
26
+ try {
27
+ const { grep_search } = await import('../../src/tools/builtin/search/grep_search.js');
28
+ const result = await grep_search.execute({ pattern: 'hello', dir_path: dir });
29
+ expect(result.success).toBe(true);
30
+ expect(result.output).toContain('hello');
31
+ } finally {
32
+ cleanup();
33
+ }
34
+ });
35
+
36
+ it('grep_search 不匹配时应返回提示', async () => {
37
+ const dir = setup();
38
+ try {
39
+ const { grep_search } = await import('../../src/tools/builtin/search/grep_search.js');
40
+ const result = await grep_search.execute({ pattern: 'NONEXISTENT_PATTERN', dir_path: dir });
41
+ expect(result.success).toBe(true);
42
+ expect(result.output).not.toContain('NONEXISTENT_PATTERN');
43
+ } finally {
44
+ cleanup();
45
+ }
46
+ });
47
+ });
48
+
49
+ describe('RunCommand 工具', () => {
50
+ it('run_command 应执行简单命令', async () => {
51
+ const { run_command } = await import('../../src/tools/builtin/shell/run_command.js');
52
+ const result = await run_command.execute({ command: 'echo hello_from_test' });
53
+ expect(result.success).toBe(true);
54
+ expect(result.output).toContain('hello_from_test');
55
+ });
56
+
57
+ it('run_command 应执行 node --version', async () => {
58
+ const { run_command } = await import('../../src/tools/builtin/shell/run_command.js');
59
+ const result = await run_command.execute({ command: 'node --version' });
60
+ expect(result.success).toBe(true);
61
+ expect(result.output).toMatch(/v\d+/);
62
+ });
63
+
64
+ it('run_command 错误命令应返回失败', async () => {
65
+ const { run_command } = await import('../../src/tools/builtin/shell/run_command.js');
66
+ const result = await run_command.execute({ command: 'nonexistent_command_xyz' });
67
+ expect(result.success).toBe(false);
68
+ expect(result.error).toBeDefined();
69
+ });
70
+ });
71
+
72
+ describe('JSON/YAML 数据工具', () => {
73
+ it('json_parse 应正确解析 JSON', async () => {
74
+ const { json_parse } = await import('../../src/tools/builtin/data/json_parse.js');
75
+ const result = await json_parse.execute({ content: '{"name":"deeper","version":"1.0.0"}' });
76
+ expect(result.success).toBe(true);
77
+ expect(result.output).toContain('deeper');
78
+ });
79
+
80
+ it('json_parse 对无效 JSON 应返回错误', async () => {
81
+ const { json_parse } = await import('../../src/tools/builtin/data/json_parse.js');
82
+ const result = await json_parse.execute({ content: 'not valid json' });
83
+ expect(result.success).toBe(false);
84
+ expect(result.error).toBeDefined();
85
+ });
86
+
87
+ it('yaml_parse 应正确解析 YAML', async () => {
88
+ const { yaml_parse } = await import('../../src/tools/builtin/data/yaml_parse.js');
89
+ const result = await yaml_parse.execute({ content: 'name: deeper\nversion: 1.0.0' });
90
+ expect(result.success).toBe(true);
91
+ });
92
+
93
+ it('csv_parse 应正确解析 CSV', async () => {
94
+ const { csv_parse } = await import('../../src/tools/builtin/data/csv_parse.js');
95
+ const result = await csv_parse.execute({ content: 'name,version\ndeeper,1.0.0' });
96
+ expect(result.success).toBe(true);
97
+ expect(result.output).toContain('deeper');
98
+ });
99
+ });
100
+
101
+ describe('AI 工具', () => {
102
+ it('token_count 应正确计数', async () => {
103
+ const { token_count } = await import('../../src/tools/builtin/ai/token_count.js');
104
+ const result = await token_count.execute({ text: 'Hello 你好 world' });
105
+ expect(result.success).toBe(true);
106
+ expect(result.output).toBeDefined();
107
+ });
108
+
109
+ it('memory_store 应存储记忆', async () => {
110
+ const { memory_store } = await import('../../src/tools/builtin/ai/memory_store.js');
111
+ const result = await memory_store.execute({ action: 'set', key: 'test_key', value: 'test_value' });
112
+ expect(result.success).toBe(true);
113
+ });
114
+ });
115
+
116
+ describe('系统工具', () => {
117
+ it('system_info 应返回系统信息', async () => {
118
+ const { system_info } = await import('../../src/tools/builtin/system/system_info.js');
119
+ const result = await system_info.execute({});
120
+ expect(result.success).toBe(true);
121
+ expect(result.output).toBeDefined();
122
+ });
123
+
124
+ it('process_list 应返回进程列表', async () => {
125
+ const { process_list } = await import('../../src/tools/builtin/system/process_list.js');
126
+ const result = await process_list.execute({});
127
+ expect(result.success).toBe(true);
128
+ });
129
+ });
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { builtinTools } from '../../src/tools/builtin/index.js';
3
+ import { TOOL_CATEGORIES } from '../../src/core/constants.js';
4
+
5
+ describe('内置工具集成', () => {
6
+ it('应至少有 100 个内置工具', () => {
7
+ expect(builtinTools.length).toBeGreaterThanOrEqual(100);
8
+ });
9
+
10
+ it('所有工具名称应唯一', () => {
11
+ const names = builtinTools.map(t => t.name);
12
+ const unique = new Set(names);
13
+ expect(unique.size).toBe(builtinTools.length);
14
+ });
15
+
16
+ it('所有工具应都有必需的属性', () => {
17
+ for (const tool of builtinTools) {
18
+ expect(tool.name).toBeTruthy();
19
+ expect(tool.description).toBeTruthy();
20
+ expect(tool.category).toBeTruthy();
21
+ expect(tool.parameters).toBeDefined();
22
+ expect(tool.parameters.type).toBe('object');
23
+ expect(typeof tool.execute).toBe('function');
24
+ }
25
+ });
26
+
27
+ it('所有工具分类应在允许范围内', () => {
28
+ for (const tool of builtinTools) {
29
+ expect(TOOL_CATEGORIES).toContain(tool.category);
30
+ }
31
+ });
32
+
33
+ it('每个分类应至少有 5 个工具', () => {
34
+ for (const category of TOOL_CATEGORIES) {
35
+ const count = builtinTools.filter(t => t.category === category).length;
36
+ expect(count).toBeGreaterThanOrEqual(5);
37
+ }
38
+ });
39
+
40
+ it('文件系统分类应至少有 15 个工具', () => {
41
+ const fsTools = builtinTools.filter(t => t.category === 'filesystem');
42
+ expect(fsTools.length).toBeGreaterThanOrEqual(15);
43
+ });
44
+
45
+ it('Shell 分类应至少有 15 个工具', () => {
46
+ const shellTools = builtinTools.filter(t => t.category === 'shell');
47
+ expect(shellTools.length).toBeGreaterThanOrEqual(15);
48
+ });
49
+
50
+ it('安全工具 execute 应返回 Promise 对象', () => {
51
+ const testTools = builtinTools.filter(t =>
52
+ ['read_file', 'list_dir', 'file_info', 'token_count',
53
+ 'system_info', 'web_fetch', 'grep_search',
54
+ 'codebase_search', 'json_parse', 'csv_parse',
55
+ 'yaml_parse', 'toml_parse', 'data_transform',
56
+ 'text_search', 'fuzzy_find', 'find_references',
57
+ 'find_definition', 'symbol_search', 'search_package',
58
+ 'search_docs', 'process_list', 'resource_monitor',
59
+ 'log_viewer', 'check_url', 'parse_html'
60
+ ].includes(t.name)
61
+ );
62
+ for (const tool of testTools) {
63
+ const result = tool.execute({});
64
+ expect(result).toBeInstanceOf(Promise);
65
+ }
66
+ expect(testTools.length).toBeGreaterThanOrEqual(10);
67
+ });
68
+
69
+ it('工具定义应具有正确的格式', () => {
70
+ for (const tool of builtinTools) {
71
+ const def: any = {
72
+ name: tool.name,
73
+ description: tool.description,
74
+ category: tool.category,
75
+ parameters: tool.parameters,
76
+ };
77
+ expect(def.name).toBeTruthy();
78
+ expect(def.description).toBeTruthy();
79
+ expect(def.parameters).toBeDefined();
80
+ expect(typeof def.parameters).toBe('object');
81
+ }
82
+ });
83
+ });
84
+
85
+ describe('工具安全分级', () => {
86
+ it('安全工具应正确标记', () => {
87
+ const safeTools = builtinTools.filter(t => t.dangerous === false && t.requiresApproval === false);
88
+ expect(safeTools.length).toBeGreaterThan(0);
89
+ // 读文件、列表操作等应是安全的
90
+ const readFile = builtinTools.find(t => t.name === 'read_file');
91
+ expect(readFile).toBeDefined();
92
+ expect(readFile!.dangerous).toBe(false);
93
+ expect(readFile!.requiresApproval).toBe(false);
94
+ });
95
+
96
+ it('危险工具应正确标记', () => {
97
+ const dangerousTools = builtinTools.filter(t => t.dangerous === true);
98
+ // 可能有 background_terminal, kill_terminal 等
99
+ expect(dangerousTools.length).toBeGreaterThanOrEqual(0); // 可能不直接标记 dangerous
100
+ });
101
+
102
+ it('write_file 应需要确认', () => {
103
+ const writeFile = builtinTools.find(t => t.name === 'write_file');
104
+ expect(writeFile).toBeDefined();
105
+ });
106
+
107
+ it('delete_file 应需要确认', () => {
108
+ const deleteFile = builtinTools.find(t => t.name === 'delete_file');
109
+ expect(deleteFile).toBeDefined();
110
+ });
111
+ });