@abacus-ai/cli 2.0.0-canary.0 → 2.0.0-canary.10

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 (198) hide show
  1. package/dist/index.mjs +825 -577
  2. package/package.json +4 -1
  3. package/.oxlintrc.json +0 -8
  4. package/resources/abacus.ico +0 -0
  5. package/resources/entitlements.plist +0 -9
  6. package/src/__e2e__/README.md +0 -196
  7. package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
  8. package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
  9. package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
  10. package/src/__e2e__/conversation.e2e.test.tsx +0 -56
  11. package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
  12. package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
  13. package/src/__e2e__/helpers/test-helpers.ts +0 -450
  14. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
  15. package/src/__e2e__/llm-models.e2e.test.ts +0 -402
  16. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
  17. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
  18. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
  19. package/src/__e2e__/repl.e2e.test.tsx +0 -78
  20. package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
  21. package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
  22. package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
  23. package/src/args.ts +0 -22
  24. package/src/components/__tests__/react-compiler.test.tsx +0 -78
  25. package/src/components/__tests__/status-indicator.test.tsx +0 -403
  26. package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
  27. package/src/components/composer/agent-mode-indicator.tsx +0 -63
  28. package/src/components/composer/bash-runner.tsx +0 -54
  29. package/src/components/composer/commands/default-commands.tsx +0 -615
  30. package/src/components/composer/commands/handler.tsx +0 -59
  31. package/src/components/composer/commands/picker.tsx +0 -273
  32. package/src/components/composer/commands/registry.ts +0 -233
  33. package/src/components/composer/commands/types.ts +0 -33
  34. package/src/components/composer/context.tsx +0 -88
  35. package/src/components/composer/file-mention-picker.tsx +0 -83
  36. package/src/components/composer/help.tsx +0 -44
  37. package/src/components/composer/index.tsx +0 -1006
  38. package/src/components/composer/mentions.ts +0 -57
  39. package/src/components/composer/message-queue.tsx +0 -70
  40. package/src/components/composer/mode-panel.tsx +0 -35
  41. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
  42. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
  43. package/src/components/composer/modes/bash-handler.tsx +0 -132
  44. package/src/components/composer/modes/bash-renderer.tsx +0 -175
  45. package/src/components/composer/modes/default-handlers.tsx +0 -33
  46. package/src/components/composer/modes/index.ts +0 -41
  47. package/src/components/composer/modes/types.ts +0 -21
  48. package/src/components/composer/persistent-shell.ts +0 -283
  49. package/src/components/composer/process.ts +0 -65
  50. package/src/components/composer/types.ts +0 -9
  51. package/src/components/composer/use-mention-search.ts +0 -68
  52. package/src/components/error-boundry.tsx +0 -60
  53. package/src/components/exit-message.tsx +0 -29
  54. package/src/components/expanded-view.tsx +0 -74
  55. package/src/components/file-completion.tsx +0 -127
  56. package/src/components/header.tsx +0 -47
  57. package/src/components/logo.tsx +0 -37
  58. package/src/components/segments.tsx +0 -356
  59. package/src/components/status-indicator.tsx +0 -306
  60. package/src/components/tool-group-summary.tsx +0 -263
  61. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -312
  62. package/src/components/tool-permissions/diff-preview.tsx +0 -355
  63. package/src/components/tool-permissions/index.ts +0 -5
  64. package/src/components/tool-permissions/permission-options.tsx +0 -375
  65. package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
  66. package/src/components/tool-permissions/tool-permission-ui.tsx +0 -398
  67. package/src/components/tools/agent/ask-user-question.tsx +0 -101
  68. package/src/components/tools/agent/enter-plan-mode.tsx +0 -49
  69. package/src/components/tools/agent/exit-plan-mode.tsx +0 -75
  70. package/src/components/tools/agent/handoff-to-main.tsx +0 -27
  71. package/src/components/tools/agent/subagent.tsx +0 -37
  72. package/src/components/tools/agent/todo-write.tsx +0 -104
  73. package/src/components/tools/browser/close-tab.tsx +0 -58
  74. package/src/components/tools/browser/computer.tsx +0 -70
  75. package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
  76. package/src/components/tools/browser/get-tab-content.tsx +0 -51
  77. package/src/components/tools/browser/navigate-to.tsx +0 -59
  78. package/src/components/tools/browser/new-tab.tsx +0 -60
  79. package/src/components/tools/browser/perform-action.tsx +0 -63
  80. package/src/components/tools/browser/refresh-tab.tsx +0 -43
  81. package/src/components/tools/browser/switch-tab.tsx +0 -58
  82. package/src/components/tools/filesystem/delete-file.tsx +0 -104
  83. package/src/components/tools/filesystem/edit.tsx +0 -220
  84. package/src/components/tools/filesystem/list-dir.tsx +0 -78
  85. package/src/components/tools/filesystem/read-file.tsx +0 -180
  86. package/src/components/tools/filesystem/upload-image.tsx +0 -76
  87. package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
  88. package/src/components/tools/index.ts +0 -91
  89. package/src/components/tools/mcp/mcp-tool.tsx +0 -158
  90. package/src/components/tools/search/fetch-url.tsx +0 -73
  91. package/src/components/tools/search/file-search.tsx +0 -78
  92. package/src/components/tools/search/grep.tsx +0 -90
  93. package/src/components/tools/search/semantic-search.tsx +0 -66
  94. package/src/components/tools/search/web-search.tsx +0 -71
  95. package/src/components/tools/shared/index.tsx +0 -48
  96. package/src/components/tools/shared/zod-coercion.ts +0 -35
  97. package/src/components/tools/terminal/bash-tool-output.tsx +0 -174
  98. package/src/components/tools/terminal/get-terminal-output.tsx +0 -85
  99. package/src/components/tools/terminal/run-in-terminal.tsx +0 -106
  100. package/src/components/tools/types.ts +0 -16
  101. package/src/components/tools.tsx +0 -66
  102. package/src/components/ui/__tests__/divider.test.tsx +0 -61
  103. package/src/components/ui/__tests__/gradient.test.tsx +0 -125
  104. package/src/components/ui/__tests__/input.test.tsx +0 -166
  105. package/src/components/ui/__tests__/select.test.tsx +0 -273
  106. package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
  107. package/src/components/ui/blinking-indicator.tsx +0 -25
  108. package/src/components/ui/divider.tsx +0 -162
  109. package/src/components/ui/gradient.tsx +0 -56
  110. package/src/components/ui/input.tsx +0 -228
  111. package/src/components/ui/select.tsx +0 -151
  112. package/src/components/ui/shimmer.tsx +0 -84
  113. package/src/context/agent-mode.tsx +0 -95
  114. package/src/context/extension-file.tsx +0 -136
  115. package/src/context/network-activity.tsx +0 -45
  116. package/src/context/notification.tsx +0 -62
  117. package/src/context/shell-size.tsx +0 -49
  118. package/src/context/shell-title.tsx +0 -38
  119. package/src/entrypoints/print-mode.ts +0 -312
  120. package/src/entrypoints/repl.tsx +0 -401
  121. package/src/hooks/use-agent.ts +0 -15
  122. package/src/hooks/use-api-client.ts +0 -1
  123. package/src/hooks/use-available-height.ts +0 -8
  124. package/src/hooks/use-cleanup.ts +0 -29
  125. package/src/hooks/use-interrupt-manager.ts +0 -242
  126. package/src/hooks/use-models.ts +0 -22
  127. package/src/index.ts +0 -217
  128. package/src/lib/__tests__/ansi.test.ts +0 -255
  129. package/src/lib/__tests__/cli.test.ts +0 -122
  130. package/src/lib/__tests__/commands.test.ts +0 -325
  131. package/src/lib/__tests__/constants.test.ts +0 -15
  132. package/src/lib/__tests__/focusables.test.ts +0 -25
  133. package/src/lib/__tests__/fs.test.ts +0 -231
  134. package/src/lib/__tests__/markdown.test.tsx +0 -348
  135. package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
  136. package/src/lib/__tests__/mcpManagement.test.ts +0 -38
  137. package/src/lib/__tests__/path-paste.test.ts +0 -144
  138. package/src/lib/__tests__/path.test.ts +0 -300
  139. package/src/lib/__tests__/queries.test.ts +0 -39
  140. package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
  141. package/src/lib/__tests__/text-buffer.test.ts +0 -328
  142. package/src/lib/__tests__/text-utils.test.ts +0 -32
  143. package/src/lib/__tests__/timing.test.ts +0 -78
  144. package/src/lib/__tests__/utils.test.ts +0 -238
  145. package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
  146. package/src/lib/ansi.ts +0 -150
  147. package/src/lib/cli-push-server.ts +0 -112
  148. package/src/lib/cli.ts +0 -44
  149. package/src/lib/clipboard.ts +0 -226
  150. package/src/lib/command-utils.ts +0 -93
  151. package/src/lib/commands.ts +0 -270
  152. package/src/lib/constants.ts +0 -3
  153. package/src/lib/extension-connection.ts +0 -181
  154. package/src/lib/focusables.ts +0 -7
  155. package/src/lib/fs.ts +0 -533
  156. package/src/lib/markdown/code-block.tsx +0 -63
  157. package/src/lib/markdown/index.ts +0 -4
  158. package/src/lib/markdown/link.tsx +0 -19
  159. package/src/lib/markdown/markdown.tsx +0 -372
  160. package/src/lib/markdown/types.ts +0 -15
  161. package/src/lib/mcpCommandHandler.ts +0 -121
  162. package/src/lib/mcpManagement.ts +0 -44
  163. package/src/lib/path-paste.ts +0 -185
  164. package/src/lib/path.ts +0 -179
  165. package/src/lib/queries.ts +0 -15
  166. package/src/lib/standaloneMcpService.ts +0 -688
  167. package/src/lib/status-utils.ts +0 -237
  168. package/src/lib/test-utils.tsx +0 -72
  169. package/src/lib/text-buffer.ts +0 -2415
  170. package/src/lib/text-utils.ts +0 -272
  171. package/src/lib/timing.ts +0 -63
  172. package/src/lib/types.ts +0 -295
  173. package/src/lib/utils.ts +0 -182
  174. package/src/lib/vim-buffer-actions.ts +0 -732
  175. package/src/providers/agent.tsx +0 -1075
  176. package/src/providers/api-client.tsx +0 -43
  177. package/src/services/logger.ts +0 -85
  178. package/src/terminal/detection.ts +0 -187
  179. package/src/terminal/exit.ts +0 -279
  180. package/src/terminal/notification.ts +0 -83
  181. package/src/terminal/progress.ts +0 -201
  182. package/src/terminal/setup.ts +0 -797
  183. package/src/terminal/suspend.ts +0 -58
  184. package/src/terminal/types.ts +0 -51
  185. package/src/theme/context.tsx +0 -57
  186. package/src/theme/index.ts +0 -4
  187. package/src/theme/themed.tsx +0 -35
  188. package/src/theme/themes.json +0 -546
  189. package/src/theme/types.ts +0 -110
  190. package/src/tools/types.ts +0 -59
  191. package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
  192. package/src/tools/utils/tool-ui-components.tsx +0 -631
  193. package/src/tools/utils/zod-coercion.ts +0 -35
  194. package/tsconfig.json +0 -11
  195. package/tsconfig.node.json +0 -29
  196. package/tsconfig.test.json +0 -27
  197. package/tsdown.config.ts +0 -17
  198. package/vitest.config.ts +0 -76
package/src/lib/fs.ts DELETED
@@ -1,533 +0,0 @@
1
- import {
2
- getFileRecency,
3
- prepareQuery,
4
- scoreItemFuzzy,
5
- compareItemsByFuzzyScore,
6
- type IPreparedQuery,
7
- type IItemAccessor,
8
- type FuzzyScorerCache,
9
- LABEL_SCORE_THRESHOLD,
10
- } from "@codellm/agent/utils";
11
- import { rgPath } from "@vscode/ripgrep";
12
- import { spawn } from "node:child_process";
13
- import { promises as fs } from "node:fs";
14
- import path from "node:path";
15
- import { createInterface } from "node:readline";
16
-
17
- import { unescapePath } from "./path.js";
18
-
19
- class TtlCache<K, V> {
20
- private cache = new Map<K, { value: V; expiresAt: number }>();
21
- private ttlMs: number;
22
-
23
- constructor(ttlMs: number) {
24
- this.ttlMs = ttlMs;
25
- }
26
-
27
- get(key: K): V | undefined {
28
- const entry = this.cache.get(key);
29
- if (!entry) {
30
- return undefined;
31
- }
32
- if (Date.now() > entry.expiresAt) {
33
- this.cache.delete(key);
34
- return undefined;
35
- }
36
- return entry.value;
37
- }
38
-
39
- set(key: K, value: V): void {
40
- this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });
41
- }
42
-
43
- clear(): void {
44
- this.cache.clear();
45
- }
46
-
47
- delete(key: K): void {
48
- this.cache.delete(key);
49
- }
50
- }
51
-
52
- type SearchOptions = {
53
- root: string;
54
- maxResults?: number;
55
- includeHidden?: boolean;
56
- globs?: string[];
57
- ignoreGlobs?: string[];
58
- useRipgrep?: boolean;
59
- useGitignore?: boolean;
60
- };
61
-
62
- type FileDoc = {
63
- path: string;
64
- name: string;
65
- dir: string;
66
- ext: string;
67
- isDirectory?: boolean;
68
- };
69
-
70
- type FileIndex = {
71
- docs: FileDoc[];
72
- building: boolean;
73
- builtAt?: number;
74
- buildError?: Error;
75
- };
76
-
77
- type SearchResult = {
78
- paths: string[];
79
- isDirectory: boolean[];
80
- };
81
-
82
- const SEARCH_RESULT_CACHE_TTL = 5 * 1000;
83
- const FILE_INDEX_CACHE_TTL = 60 * 1000;
84
-
85
- const searchResultCache = new TtlCache<string, SearchResult>(SEARCH_RESULT_CACHE_TTL);
86
- const fileIndexCache = new TtlCache<string, FileIndex>(FILE_INDEX_CACHE_TTL);
87
-
88
- const RECENCY_BOOST_SCORE = LABEL_SCORE_THRESHOLD / 4;
89
-
90
- function toDoc(filePath: string, isDirectory = false): FileDoc {
91
- const normalized = filePath.replace(/\\/g, "/");
92
- const name = path.posix.basename(normalized);
93
- const dir = path.posix.dirname(normalized);
94
- const ext = path.posix.extname(normalized);
95
- return { path: normalized, name, dir, ext, isDirectory };
96
- }
97
-
98
- function getIndexKey(root: string): string {
99
- return path.resolve(root).replace(/\\/g, "/");
100
- }
101
-
102
- function getResultCacheKey(
103
- query: string,
104
- options: Required<Pick<SearchOptions, "root" | "maxResults">>,
105
- ): string {
106
- return `${getIndexKey(options.root)}::${query}::${options.maxResults}`;
107
- }
108
-
109
- const fileDocAccessor: IItemAccessor<FileDoc> = {
110
- getItemLabel(item: FileDoc): string {
111
- return item.name;
112
- },
113
- getItemDescription(item: FileDoc): string {
114
- return item.dir;
115
- },
116
- getItemPath(item: FileDoc): string {
117
- return item.path;
118
- },
119
- };
120
-
121
- function searchAndRank(
122
- docs: ReadonlyArray<FileDoc>,
123
- query: IPreparedQuery,
124
- maxResults: number,
125
- ): SearchResult {
126
- if (docs.length === 0) {
127
- return { paths: [], isDirectory: [] };
128
- }
129
-
130
- const scorerCache: FuzzyScorerCache = {};
131
- const queryLower = query.normalizedLowercase;
132
- const queryLength = query.normalized.length;
133
-
134
- if (queryLength === 0) {
135
- return { paths: [], isDirectory: [] };
136
- }
137
-
138
- const scoredDocs: Array<{ doc: FileDoc; score: number; recencyBoost: number }> = [];
139
- const candidateLimit = Math.min(docs.length, maxResults * 15);
140
-
141
- const candidates: Array<{ doc: FileDoc; recency: number }> = [];
142
-
143
- for (const doc of docs) {
144
- const nameLower = doc.name.toLowerCase();
145
- const pathLower = doc.path.toLowerCase();
146
-
147
- const nameStarts = nameLower.startsWith(queryLower);
148
- const nameIncludes = nameLower.includes(queryLower);
149
- const pathIncludes = pathLower.includes(queryLower);
150
-
151
- if (nameStarts || nameIncludes || pathIncludes) {
152
- const recency = getFileRecency(doc.path);
153
- candidates.push({ doc, recency });
154
- }
155
- }
156
-
157
- candidates.sort((a, b) => b.recency - a.recency);
158
-
159
- let candidatesChecked = 0;
160
- for (const { doc, recency } of candidates) {
161
- if (candidatesChecked >= candidateLimit && scoredDocs.length >= maxResults * 2) {
162
- break;
163
- }
164
-
165
- candidatesChecked++;
166
-
167
- const recencyBoost = recency > 0 ? RECENCY_BOOST_SCORE : 0;
168
-
169
- const itemScore = scoreItemFuzzy(doc, query, true, fileDocAccessor, scorerCache);
170
- if (itemScore.score > 0) {
171
- scoredDocs.push({
172
- doc,
173
- score: itemScore.score + recencyBoost,
174
- recencyBoost,
175
- });
176
- }
177
- }
178
-
179
- scoredDocs.sort((a, b) => {
180
- if (a.score !== b.score) {
181
- return b.score - a.score;
182
- }
183
- if (a.recencyBoost !== b.recencyBoost) {
184
- return b.recencyBoost - a.recencyBoost;
185
- }
186
- return compareItemsByFuzzyScore(a.doc, b.doc, query, true, fileDocAccessor, scorerCache);
187
- });
188
-
189
- const topResults = scoredDocs.slice(0, maxResults);
190
- return {
191
- paths: topResults.map((item) => item.doc.path),
192
- isDirectory: topResults.map((item) => item.doc.isDirectory ?? false),
193
- };
194
- }
195
-
196
- function getBaselineResults(docs: ReadonlyArray<FileDoc>, maxResults: number): SearchResult {
197
- const withRecency = docs.map((doc) => ({
198
- doc,
199
- recency: getFileRecency(doc.path),
200
- }));
201
-
202
- const sorted = withRecency
203
- .sort((a, b) => {
204
- if (a.recency !== b.recency) {
205
- return b.recency - a.recency;
206
- }
207
- const aDepth = a.doc.path.split("/").length;
208
- const bDepth = b.doc.path.split("/").length;
209
- return aDepth - bDepth || a.doc.name.localeCompare(b.doc.name);
210
- })
211
- .slice(0, maxResults);
212
-
213
- return {
214
- paths: sorted.map((item) => item.doc.path),
215
- isDirectory: sorted.map((item) => item.doc.isDirectory ?? false),
216
- };
217
- }
218
-
219
- async function ensureIndex(
220
- options: Required<
221
- Pick<SearchOptions, "root" | "includeHidden" | "globs" | "ignoreGlobs" | "useRipgrep">
222
- > &
223
- Pick<SearchOptions, "useGitignore">,
224
- ): Promise<FileIndex> {
225
- const key = getIndexKey(options.root);
226
- const cached = fileIndexCache.get(key);
227
- if (cached) {
228
- if (cached.docs.length === 0 && !cached.building && !cached.buildError) {
229
- fileIndexCache.delete(key);
230
- } else {
231
- return cached;
232
- }
233
- }
234
-
235
- const index: FileIndex = { docs: [], building: true };
236
- fileIndexCache.set(key, index);
237
-
238
- const seenPaths = new Set<string>();
239
-
240
- const addDoc = (doc: FileDoc) => {
241
- if (seenPaths.has(doc.path)) {
242
- return;
243
- }
244
- seenPaths.add(doc.path);
245
- index.docs.push(doc);
246
- };
247
-
248
- if (options.useRipgrep !== false) {
249
- const args: string[] = ["--files", "--no-messages", "--max-depth", "100"];
250
-
251
- if (options.includeHidden) {
252
- args.push("--hidden");
253
- }
254
-
255
- if (options.useGitignore === false) {
256
- args.push("--no-ignore");
257
- }
258
-
259
- const include = options.globs ?? [];
260
- const ignore = options.ignoreGlobs ?? [];
261
-
262
- for (const g of include) {
263
- args.push("-g", g);
264
- }
265
- for (const g of ignore) {
266
- args.push("-g", g);
267
- }
268
-
269
- const child = spawn(rgPath, args, { cwd: options.root, stdio: ["ignore", "pipe", "pipe"] });
270
- const rl = createInterface({ input: child.stdout });
271
-
272
- rl.on("line", (line: string) => {
273
- const raw = unescapePath(line.trim());
274
- if (!raw) {
275
- return;
276
- }
277
- const absolute = path.resolve(options.root, raw);
278
- const doc = toDoc(absolute, false);
279
- if (!options.includeHidden) {
280
- if (
281
- doc.name.startsWith(".") ||
282
- doc.path.split("/").some((seg) => seg.startsWith(".") && seg !== ".")
283
- ) {
284
- return;
285
- }
286
- }
287
- addDoc(doc);
288
- });
289
-
290
- const performFallbackWalk = async () => {
291
- const MAX_DEPTH = 12;
292
- const MAX_FILES = 15000;
293
- const SKIP_DIRS = /^(node_modules|\.git|dist|build|out|coverage|\.vscode|\.idea|\.DS_Store)$/;
294
-
295
- const walk = async (dir: string, depth: number): Promise<void> => {
296
- if (depth > MAX_DEPTH || index.docs.length >= MAX_FILES) {
297
- return;
298
- }
299
-
300
- try {
301
- const entries = await fs.readdir(dir, { withFileTypes: true });
302
- const dirs: string[] = [];
303
-
304
- for (const entry of entries) {
305
- if (!options.includeHidden && entry.name.startsWith(".")) {
306
- continue;
307
- }
308
-
309
- const full = path.join(dir, entry.name);
310
-
311
- if (entry.isDirectory()) {
312
- if (SKIP_DIRS.test(entry.name)) {
313
- continue;
314
- }
315
- addDoc(toDoc(full, true));
316
- dirs.push(full);
317
- } else if (entry.isFile()) {
318
- addDoc(toDoc(full));
319
- if (index.docs.length >= MAX_FILES) {
320
- return;
321
- }
322
- }
323
- }
324
-
325
- await Promise.all(dirs.map((d) => walk(d, depth + 1)));
326
- } catch {
327
- // Skip directories we can't read
328
- }
329
- };
330
-
331
- await walk(options.root, 0);
332
- };
333
-
334
- const done = new Promise<void>((resolve) => {
335
- child.on("close", (code) => {
336
- if (code !== 0 && code !== null) {
337
- void performFallbackWalk()
338
- .then(() => {
339
- index.building = false;
340
- index.builtAt = Date.now();
341
- resolve();
342
- })
343
- .catch((err: unknown) => {
344
- index.building = false;
345
- index.buildError = err instanceof Error ? err : new Error("Indexing failed");
346
- resolve();
347
- });
348
- } else {
349
- // Collect directories after ripgrep finishes successfully
350
- void (async () => {
351
- const MAX_DEPTH = 12;
352
- const SKIP_DIRS =
353
- /^(node_modules|\.git|dist|build|out|coverage|\.vscode|\.idea|\.DS_Store)$/;
354
- const walk = async (dir: string, depth: number): Promise<void> => {
355
- if (depth > MAX_DEPTH) {
356
- return;
357
- }
358
- try {
359
- const entries = await fs.readdir(dir, { withFileTypes: true });
360
- await Promise.all(
361
- entries
362
- .filter(
363
- (e) =>
364
- e.isDirectory() &&
365
- (options.includeHidden || !e.name.startsWith(".")) &&
366
- !SKIP_DIRS.test(e.name),
367
- )
368
- .map(async (e) => {
369
- const full = path.join(dir, e.name);
370
- addDoc(toDoc(full, true));
371
- await walk(full, depth + 1);
372
- }),
373
- );
374
- } catch {}
375
- };
376
- await walk(options.root, 0);
377
- })().finally(() => {
378
- index.building = false;
379
- index.builtAt = Date.now();
380
- resolve();
381
- });
382
- }
383
- });
384
- child.on("error", () => {
385
- void performFallbackWalk()
386
- .then(() => {
387
- index.building = false;
388
- index.builtAt = Date.now();
389
- resolve();
390
- })
391
- .catch((err: unknown) => {
392
- index.building = false;
393
- index.buildError = err instanceof Error ? err : new Error("Indexing failed");
394
- resolve();
395
- });
396
- });
397
- });
398
-
399
- void done.then(() => {
400
- if (index.building) {
401
- index.building = false;
402
- index.builtAt = Date.now();
403
- }
404
- });
405
- } else {
406
- const MAX_DEPTH = 12;
407
- const MAX_FILES = 15000;
408
- const SKIP_DIRS = /^(node_modules|\.git|dist|build|out|coverage|\.vscode|\.idea|\.DS_Store)$/;
409
-
410
- const walk = async (dir: string, depth: number): Promise<void> => {
411
- if (depth > MAX_DEPTH || index.docs.length >= MAX_FILES) {
412
- return;
413
- }
414
-
415
- try {
416
- const entries = await fs.readdir(dir, { withFileTypes: true });
417
- const dirs: string[] = [];
418
-
419
- for (const entry of entries) {
420
- if (!options.includeHidden && entry.name.startsWith(".")) {
421
- continue;
422
- }
423
-
424
- const full = path.join(dir, entry.name);
425
-
426
- if (entry.isDirectory()) {
427
- if (SKIP_DIRS.test(entry.name)) {
428
- continue;
429
- }
430
- addDoc(toDoc(full, true));
431
- dirs.push(full);
432
- } else if (entry.isFile()) {
433
- addDoc(toDoc(full));
434
- if (index.docs.length >= MAX_FILES) {
435
- return;
436
- }
437
- }
438
- }
439
-
440
- await Promise.all(dirs.map((d) => walk(d, depth + 1)));
441
- } catch {}
442
- };
443
-
444
- void walk(options.root, 0)
445
- .then(() => {
446
- index.building = false;
447
- index.builtAt = Date.now();
448
- })
449
- .catch((err: unknown) => {
450
- index.building = false;
451
- index.buildError = err instanceof Error ? err : new Error("Indexing failed");
452
- });
453
- }
454
-
455
- return index;
456
- }
457
-
458
- export async function* searchFiles(
459
- query: string,
460
- options: SearchOptions,
461
- ): AsyncGenerator<SearchResult, void, void> {
462
- const root = options.root;
463
- const maxResults = options.maxResults ?? 100;
464
- const includeHidden = options.includeHidden ?? false;
465
- const globs = options.globs ?? [];
466
- const ignoreGlobs = options.ignoreGlobs ?? [];
467
- const useRipgrep = options.useRipgrep ?? true;
468
- const useGitignore = options.useGitignore ?? true;
469
-
470
- const stat = await fs.stat(root).catch(() => null);
471
- if (!stat || !stat.isDirectory()) {
472
- return;
473
- }
474
-
475
- const index = await ensureIndex({
476
- root,
477
- includeHidden,
478
- globs,
479
- ignoreGlobs,
480
- useRipgrep,
481
- useGitignore,
482
- });
483
-
484
- const trimmedQuery = query.trim();
485
- const cachedKey = getResultCacheKey(trimmedQuery, { root, maxResults });
486
- const cached = searchResultCache.get(cachedKey);
487
- if (cached && cached.paths.length > 0) {
488
- yield cached;
489
- if (!index.building) {
490
- return;
491
- }
492
- }
493
-
494
- if (trimmedQuery.length === 0) {
495
- const baseline = getBaselineResults(index.docs, maxResults);
496
- if (baseline.paths.length > 0) {
497
- searchResultCache.set(cachedKey, baseline);
498
- yield baseline;
499
- }
500
- return;
501
- }
502
-
503
- const preparedQuery = prepareQuery(trimmedQuery);
504
- const results = searchAndRank(index.docs, preparedQuery, maxResults);
505
-
506
- if (results.paths.length > 0) {
507
- searchResultCache.set(cachedKey, results);
508
- yield results;
509
- }
510
-
511
- if (index.building) {
512
- await new Promise<void>((resolve) => {
513
- const checkInterval = setInterval(() => {
514
- if (!index.building) {
515
- clearTimeout(timeout);
516
- clearInterval(checkInterval);
517
- resolve();
518
- }
519
- }, 50);
520
- const timeout = setTimeout(() => {
521
- clearInterval(checkInterval);
522
- resolve();
523
- }, 5000);
524
- });
525
-
526
- const finalResults = searchAndRank(index.docs, preparedQuery, maxResults);
527
- if (finalResults.paths.length > 0) {
528
- const finalKey = getResultCacheKey(trimmedQuery, { root, maxResults });
529
- searchResultCache.set(finalKey, finalResults);
530
- yield finalResults;
531
- }
532
- }
533
- }
@@ -1,63 +0,0 @@
1
- import { View, Text } from "@codellm/jar";
2
- import { renderMermaidAscii } from "beautiful-mermaid";
3
- import { highlight, supportsLanguage } from "cli-highlight";
4
-
5
- import type { CodeBlockProps } from "./types.js";
6
-
7
- /**
8
- * Check if the language is mermaid
9
- */
10
- function isMermaidLanguage(language: string | undefined): boolean {
11
- if (!language) return false;
12
- const normalizedLang = language.toLowerCase().trim();
13
- return normalizedLang === "mermaid" || normalizedLang === "mmd";
14
- }
15
-
16
- /**
17
- * Try to render mermaid diagram as ASCII art
18
- * Returns null if parsing fails
19
- */
20
- function tryRenderMermaid(content: string): string | null {
21
- try {
22
- return renderMermaidAscii(content);
23
- } catch {
24
- // Fallback to regular code block rendering
25
- return null;
26
- }
27
- }
28
-
29
- export function CodeBlock({ children, language }: CodeBlockProps) {
30
- // Try to render mermaid diagrams as ASCII art
31
- if (isMermaidLanguage(language)) {
32
- const mermaidOutput = tryRenderMermaid(children || "");
33
- if (mermaidOutput !== null) {
34
- return (
35
- <View paddingX={1} flexDirection="row" alignSelf="flex-start">
36
- <Text>{mermaidOutput}</Text>
37
- </View>
38
- );
39
- }
40
- // If mermaid parsing fails, fall through to regular code block rendering
41
- }
42
-
43
- let highlighted = children;
44
-
45
- if (language && supportsLanguage(language)) {
46
- try {
47
- // Use cli-highlight's default theme which works well with terminal colors
48
- highlighted = highlight(children || "", {
49
- language,
50
- });
51
- } catch {
52
- // Fallback to plain text if highlighting fails
53
- }
54
- }
55
-
56
- return (
57
- <View paddingX={1} flexDirection="row" alignSelf="flex-start">
58
- <Text>{highlighted}</Text>
59
- </View>
60
- );
61
- }
62
-
63
- CodeBlock.displayName = "CodeBlock";
@@ -1,4 +0,0 @@
1
- export { Markdown } from "./markdown.js";
2
- export { CodeBlock } from "./code-block.js";
3
- export { Link } from "./link.js";
4
- export type { MarkdownProps, CodeBlockProps, LinkProps } from "./types.js";
@@ -1,19 +0,0 @@
1
- import { Text } from "@codellm/jar";
2
-
3
- import type { LinkProps } from "./types.js";
4
-
5
- import { useTheme } from "../../theme/index.js";
6
-
7
- export function Link({ href, children }: LinkProps) {
8
- const { colors } = useTheme();
9
- // ANSI hyperlink: ESC]8;;url ST text ESC]8;; ST
10
- const open = `\x1b]8;;${href}\x07`;
11
- const close = `\x1b]8;;\x07`;
12
- return (
13
- <Text color={colors.link} underline transform={(s) => `${open}${s}${close}`}>
14
- {children}
15
- </Text>
16
- );
17
- }
18
-
19
- Link.displayName = "Link";