@travisennis/acai 0.0.5 → 0.0.6

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 (311) hide show
  1. package/README.md +4 -2
  2. package/dist/agent/index.d.ts +119 -0
  3. package/dist/agent/index.d.ts.map +1 -0
  4. package/dist/agent/index.js +406 -0
  5. package/dist/agent/manual-loop.d.ts +41 -0
  6. package/dist/agent/manual-loop.d.ts.map +1 -0
  7. package/dist/agent/manual-loop.js +278 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +27 -33
  11. package/dist/commands/add-directory-command.d.ts +3 -0
  12. package/dist/commands/add-directory-command.d.ts.map +1 -0
  13. package/dist/commands/add-directory-command.js +85 -0
  14. package/dist/commands/application-log-command.d.ts.map +1 -1
  15. package/dist/commands/application-log-command.js +34 -0
  16. package/dist/commands/clear-command.d.ts.map +1 -1
  17. package/dist/commands/clear-command.js +8 -0
  18. package/dist/commands/compact-command.d.ts.map +1 -1
  19. package/dist/commands/compact-command.js +15 -2
  20. package/dist/commands/context-command.d.ts +3 -0
  21. package/dist/commands/context-command.d.ts.map +1 -0
  22. package/dist/commands/context-command.js +183 -0
  23. package/dist/commands/copy-command.d.ts.map +1 -1
  24. package/dist/commands/copy-command.js +28 -0
  25. package/dist/commands/edit-command.d.ts.map +1 -1
  26. package/dist/commands/edit-command.js +33 -0
  27. package/dist/commands/edit-prompt-command.d.ts.map +1 -1
  28. package/dist/commands/edit-prompt-command.js +28 -0
  29. package/dist/commands/exit-command.d.ts.map +1 -1
  30. package/dist/commands/exit-command.js +20 -0
  31. package/dist/commands/files-command.d.ts.map +1 -1
  32. package/dist/commands/files-command.js +57 -0
  33. package/dist/commands/generate-rules-command.d.ts.map +1 -1
  34. package/dist/commands/generate-rules-command.js +311 -1
  35. package/dist/commands/handoff-command.d.ts +3 -0
  36. package/dist/commands/handoff-command.d.ts.map +1 -0
  37. package/dist/commands/handoff-command.js +202 -0
  38. package/dist/commands/health-command.d.ts.map +1 -1
  39. package/dist/commands/health-command.js +119 -2
  40. package/dist/commands/help-command.d.ts.map +1 -1
  41. package/dist/commands/help-command.js +28 -0
  42. package/dist/commands/history-command.d.ts +3 -0
  43. package/dist/commands/history-command.d.ts.map +1 -0
  44. package/dist/commands/history-command.js +534 -0
  45. package/dist/commands/init-command.d.ts +1 -1
  46. package/dist/commands/init-command.d.ts.map +1 -1
  47. package/dist/commands/init-command.js +55 -18
  48. package/dist/commands/last-log-command.d.ts.map +1 -1
  49. package/dist/commands/last-log-command.js +27 -0
  50. package/dist/commands/list-directories-command.d.ts +3 -0
  51. package/dist/commands/list-directories-command.d.ts.map +1 -0
  52. package/dist/commands/list-directories-command.js +48 -0
  53. package/dist/commands/list-tools-command.d.ts.map +1 -1
  54. package/dist/commands/list-tools-command.js +66 -3
  55. package/dist/commands/manager.d.ts +15 -3
  56. package/dist/commands/manager.d.ts.map +1 -1
  57. package/dist/commands/manager.js +86 -26
  58. package/dist/commands/model-command.d.ts +22 -0
  59. package/dist/commands/model-command.d.ts.map +1 -1
  60. package/dist/commands/model-command.js +256 -0
  61. package/dist/commands/paste-command.d.ts.map +1 -1
  62. package/dist/commands/paste-command.js +92 -0
  63. package/dist/commands/pickup-command.d.ts +3 -0
  64. package/dist/commands/pickup-command.d.ts.map +1 -0
  65. package/dist/commands/pickup-command.js +161 -0
  66. package/dist/commands/prompt-command.d.ts +1 -1
  67. package/dist/commands/prompt-command.d.ts.map +1 -1
  68. package/dist/commands/prompt-command.js +117 -2
  69. package/dist/commands/remove-directory-command.d.ts +3 -0
  70. package/dist/commands/remove-directory-command.d.ts.map +1 -0
  71. package/dist/commands/remove-directory-command.js +87 -0
  72. package/dist/commands/reset-command.d.ts +1 -1
  73. package/dist/commands/reset-command.d.ts.map +1 -1
  74. package/dist/commands/reset-command.js +13 -2
  75. package/dist/commands/rules-command.d.ts.map +1 -1
  76. package/dist/commands/rules-command.js +65 -0
  77. package/dist/commands/save-command.d.ts.map +1 -1
  78. package/dist/commands/save-command.js +12 -0
  79. package/dist/commands/shell-command.d.ts.map +1 -1
  80. package/dist/commands/shell-command.js +68 -0
  81. package/dist/commands/types.d.ts +9 -4
  82. package/dist/commands/types.d.ts.map +1 -1
  83. package/dist/commands/usage-command.d.ts.map +1 -1
  84. package/dist/commands/usage-command.js +22 -0
  85. package/dist/config.d.ts +6 -7
  86. package/dist/config.d.ts.map +1 -1
  87. package/dist/config.js +23 -29
  88. package/dist/formatting.d.ts +108 -0
  89. package/dist/formatting.d.ts.map +1 -1
  90. package/dist/formatting.js +147 -0
  91. package/dist/index.d.ts +7 -2
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +140 -38
  94. package/dist/logger.d.ts.map +1 -1
  95. package/dist/logger.js +47 -18
  96. package/dist/mentions.d.ts +2 -1
  97. package/dist/mentions.d.ts.map +1 -1
  98. package/dist/mentions.js +16 -1
  99. package/dist/messages.d.ts +8 -0
  100. package/dist/messages.d.ts.map +1 -1
  101. package/dist/messages.js +56 -19
  102. package/dist/middleware/cache.d.ts +3 -0
  103. package/dist/middleware/cache.d.ts.map +1 -0
  104. package/dist/middleware/cache.js +53 -0
  105. package/dist/middleware/index.d.ts +1 -0
  106. package/dist/middleware/index.d.ts.map +1 -1
  107. package/dist/middleware/index.js +1 -0
  108. package/dist/models/ai-config.d.ts +4 -2
  109. package/dist/models/ai-config.d.ts.map +1 -1
  110. package/dist/models/ai-config.js +12 -2
  111. package/dist/models/anthropic-provider.d.ts.map +1 -1
  112. package/dist/models/anthropic-provider.js +3 -60
  113. package/dist/models/manager.d.ts +2 -1
  114. package/dist/models/manager.d.ts.map +1 -1
  115. package/dist/models/manager.js +26 -2
  116. package/dist/models/openrouter-provider.d.ts +7 -14
  117. package/dist/models/openrouter-provider.d.ts.map +1 -1
  118. package/dist/models/openrouter-provider.js +114 -169
  119. package/dist/models/providers.d.ts +1 -1
  120. package/dist/models/providers.d.ts.map +1 -1
  121. package/dist/prompts.d.ts +1 -0
  122. package/dist/prompts.d.ts.map +1 -1
  123. package/dist/prompts.js +53 -4
  124. package/dist/repl/display-tool-messages.d.ts +1 -1
  125. package/dist/repl/display-tool-messages.d.ts.map +1 -1
  126. package/dist/repl/display-tool-messages.js +47 -44
  127. package/dist/repl/get-prompt-header.d.ts.map +1 -1
  128. package/dist/repl/get-prompt-header.js +1 -30
  129. package/dist/repl/project-status-line.d.ts +2 -0
  130. package/dist/repl/project-status-line.d.ts.map +1 -0
  131. package/dist/repl/project-status-line.js +31 -0
  132. package/dist/repl/prompt.d.ts +21 -0
  133. package/dist/repl/prompt.d.ts.map +1 -0
  134. package/dist/{repl-prompt.js → repl/prompt.js} +119 -22
  135. package/dist/repl/tool-call-repair.d.ts.map +1 -1
  136. package/dist/repl/tool-call-repair.js +8 -4
  137. package/dist/repl-new.d.ts +53 -0
  138. package/dist/repl-new.d.ts.map +1 -0
  139. package/dist/repl-new.js +374 -0
  140. package/dist/repl.d.ts +3 -5
  141. package/dist/repl.d.ts.map +1 -1
  142. package/dist/repl.js +74 -166
  143. package/dist/terminal/checkbox-prompt.d.ts.map +1 -1
  144. package/dist/terminal/checkbox-prompt.js +10 -4
  145. package/dist/terminal/index.d.ts +7 -0
  146. package/dist/terminal/index.d.ts.map +1 -1
  147. package/dist/terminal/index.js +94 -0
  148. package/dist/terminal/input-prompt.d.ts +2 -1
  149. package/dist/terminal/input-prompt.d.ts.map +1 -1
  150. package/dist/terminal/markdown.js +3 -0
  151. package/dist/terminal/search-prompt.d.ts.map +1 -1
  152. package/dist/terminal/search-prompt.js +11 -10
  153. package/dist/terminal/select-prompt.d.ts +2 -2
  154. package/dist/terminal/select-prompt.d.ts.map +1 -1
  155. package/dist/terminal/select-prompt.js +47 -39
  156. package/dist/tokens/threshold.d.ts +35 -0
  157. package/dist/tokens/threshold.d.ts.map +1 -0
  158. package/dist/tokens/threshold.js +85 -0
  159. package/dist/tools/advanced-edit-file.d.ts +69 -0
  160. package/dist/tools/advanced-edit-file.d.ts.map +1 -0
  161. package/dist/tools/advanced-edit-file.js +281 -0
  162. package/dist/tools/agent.d.ts +16 -5
  163. package/dist/tools/agent.d.ts.map +1 -1
  164. package/dist/tools/agent.js +71 -58
  165. package/dist/tools/bash-utils.d.ts +1 -1
  166. package/dist/tools/bash-utils.d.ts.map +1 -1
  167. package/dist/tools/bash-utils.js +14 -6
  168. package/dist/tools/bash.d.ts +21 -12
  169. package/dist/tools/bash.d.ts.map +1 -1
  170. package/dist/tools/bash.js +88 -135
  171. package/dist/tools/code-interpreter.d.ts +21 -9
  172. package/dist/tools/code-interpreter.d.ts.map +1 -1
  173. package/dist/tools/code-interpreter.js +138 -137
  174. package/dist/tools/delete-file.d.ts +17 -10
  175. package/dist/tools/delete-file.d.ts.map +1 -1
  176. package/dist/tools/delete-file.js +51 -95
  177. package/dist/tools/directory-tree.d.ts +17 -6
  178. package/dist/tools/directory-tree.d.ts.map +1 -1
  179. package/dist/tools/directory-tree.js +47 -49
  180. package/dist/tools/dynamic-tool-loader.d.ts +18 -8
  181. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  182. package/dist/tools/dynamic-tool-loader.js +121 -129
  183. package/dist/tools/dynamic-tool-parser.d.ts +1 -0
  184. package/dist/tools/dynamic-tool-parser.d.ts.map +1 -1
  185. package/dist/tools/dynamic-tool-parser.js +1 -0
  186. package/dist/tools/edit-file.d.ts +35 -15
  187. package/dist/tools/edit-file.d.ts.map +1 -1
  188. package/dist/tools/edit-file.js +112 -112
  189. package/dist/tools/filesystem-utils.d.ts +2 -1
  190. package/dist/tools/filesystem-utils.d.ts.map +1 -1
  191. package/dist/tools/filesystem-utils.js +31 -17
  192. package/dist/tools/glob.d.ts +36 -0
  193. package/dist/tools/glob.d.ts.map +1 -0
  194. package/dist/tools/glob.js +143 -0
  195. package/dist/tools/grep.d.ts +73 -12
  196. package/dist/tools/grep.d.ts.map +1 -1
  197. package/dist/tools/grep.js +413 -168
  198. package/dist/tools/index.d.ts +204 -124
  199. package/dist/tools/index.d.ts.map +1 -1
  200. package/dist/tools/index.js +242 -135
  201. package/dist/tools/llm-edit-fixer.d.ts +25 -0
  202. package/dist/tools/llm-edit-fixer.d.ts.map +1 -0
  203. package/dist/tools/llm-edit-fixer.js +150 -0
  204. package/dist/tools/move-file.d.ts +19 -7
  205. package/dist/tools/move-file.d.ts.map +1 -1
  206. package/dist/tools/move-file.js +40 -33
  207. package/dist/tools/read-file.d.ts +47 -9
  208. package/dist/tools/read-file.d.ts.map +1 -1
  209. package/dist/tools/read-file.js +74 -69
  210. package/dist/tools/read-multiple-files.d.ts +17 -6
  211. package/dist/tools/read-multiple-files.d.ts.map +1 -1
  212. package/dist/tools/read-multiple-files.js +76 -73
  213. package/dist/tools/save-file.d.ts +45 -12
  214. package/dist/tools/save-file.d.ts.map +1 -1
  215. package/dist/tools/save-file.js +58 -101
  216. package/dist/tools/think.d.ts +15 -7
  217. package/dist/tools/think.d.ts.map +1 -1
  218. package/dist/tools/think.js +30 -22
  219. package/dist/tools/types.d.ts +4 -10
  220. package/dist/tools/types.d.ts.map +1 -1
  221. package/dist/tools/types.js +9 -0
  222. package/dist/tools/utils.d.ts +14 -0
  223. package/dist/tools/utils.d.ts.map +1 -0
  224. package/dist/tools/utils.js +16 -0
  225. package/dist/tools/web-fetch.d.ts +11 -4
  226. package/dist/tools/web-fetch.d.ts.map +1 -1
  227. package/dist/tools/web-fetch.js +39 -38
  228. package/dist/tools/web-search.d.ts +15 -6
  229. package/dist/tools/web-search.d.ts.map +1 -1
  230. package/dist/tools/web-search.js +50 -32
  231. package/dist/tui/autocomplete.d.ts +44 -0
  232. package/dist/tui/autocomplete.d.ts.map +1 -0
  233. package/dist/tui/autocomplete.js +466 -0
  234. package/dist/tui/components/assistant-message.d.ts +18 -0
  235. package/dist/tui/components/assistant-message.d.ts.map +1 -0
  236. package/dist/tui/components/assistant-message.js +29 -0
  237. package/dist/tui/components/editor.d.ts +51 -0
  238. package/dist/tui/components/editor.d.ts.map +1 -0
  239. package/dist/tui/components/editor.js +758 -0
  240. package/dist/tui/components/footer.d.ts +24 -0
  241. package/dist/tui/components/footer.d.ts.map +1 -0
  242. package/dist/tui/components/footer.js +197 -0
  243. package/dist/tui/components/input.d.ts +14 -0
  244. package/dist/tui/components/input.d.ts.map +1 -0
  245. package/dist/tui/components/input.js +122 -0
  246. package/dist/tui/components/loader.d.ts +19 -0
  247. package/dist/tui/components/loader.d.ts.map +1 -0
  248. package/dist/tui/components/loader.js +45 -0
  249. package/dist/tui/components/markdown.d.ts +103 -0
  250. package/dist/tui/components/markdown.d.ts.map +1 -0
  251. package/dist/tui/components/markdown.js +533 -0
  252. package/dist/tui/components/modal.d.ts +40 -0
  253. package/dist/tui/components/modal.d.ts.map +1 -0
  254. package/dist/tui/components/modal.js +292 -0
  255. package/dist/tui/components/prompt-status.d.ts +16 -0
  256. package/dist/tui/components/prompt-status.d.ts.map +1 -0
  257. package/dist/tui/components/prompt-status.js +21 -0
  258. package/dist/tui/components/select-list.d.ts +22 -0
  259. package/dist/tui/components/select-list.d.ts.map +1 -0
  260. package/dist/tui/components/select-list.js +143 -0
  261. package/dist/tui/components/spacer.d.ts +16 -0
  262. package/dist/tui/components/spacer.d.ts.map +1 -0
  263. package/dist/tui/components/spacer.js +27 -0
  264. package/dist/tui/components/text.d.ts +26 -0
  265. package/dist/tui/components/text.d.ts.map +1 -0
  266. package/dist/tui/components/text.js +143 -0
  267. package/dist/tui/components/thinking-block.d.ts +14 -0
  268. package/dist/tui/components/thinking-block.d.ts.map +1 -0
  269. package/dist/tui/components/thinking-block.js +30 -0
  270. package/dist/tui/components/tool-execution.d.ts +17 -0
  271. package/dist/tui/components/tool-execution.d.ts.map +1 -0
  272. package/dist/tui/components/tool-execution.js +153 -0
  273. package/dist/tui/components/user-message.d.ts +9 -0
  274. package/dist/tui/components/user-message.d.ts.map +1 -0
  275. package/dist/tui/components/user-message.js +21 -0
  276. package/dist/tui/components/welcome.d.ts +6 -0
  277. package/dist/tui/components/welcome.d.ts.map +1 -0
  278. package/dist/tui/components/welcome.js +30 -0
  279. package/dist/tui/index.d.ts +14 -0
  280. package/dist/tui/index.d.ts.map +1 -0
  281. package/dist/tui/index.js +18 -0
  282. package/dist/tui/terminal.d.ts +37 -0
  283. package/dist/tui/terminal.d.ts.map +1 -0
  284. package/dist/tui/terminal.js +104 -0
  285. package/dist/tui/tui.d.ts +67 -0
  286. package/dist/tui/tui.d.ts.map +1 -0
  287. package/dist/tui/tui.js +184 -0
  288. package/dist/tui/utils.d.ts +19 -0
  289. package/dist/tui/utils.d.ts.map +1 -0
  290. package/dist/tui/utils.js +31 -0
  291. package/dist/utils/generators.d.ts +3 -0
  292. package/dist/utils/generators.d.ts.map +1 -0
  293. package/dist/utils/generators.js +25 -0
  294. package/dist/utils/iterables.d.ts +2 -0
  295. package/dist/utils/iterables.d.ts.map +1 -0
  296. package/dist/utils/iterables.js +6 -0
  297. package/package.json +16 -16
  298. package/dist/conversation-analyzer.d.ts +0 -11
  299. package/dist/conversation-analyzer.d.ts.map +0 -1
  300. package/dist/conversation-analyzer.js +0 -88
  301. package/dist/repl-prompt.d.ts +0 -15
  302. package/dist/repl-prompt.d.ts.map +0 -1
  303. package/dist/tokens/manage-output.d.ts +0 -34
  304. package/dist/tokens/manage-output.d.ts.map +0 -1
  305. package/dist/tokens/manage-output.js +0 -44
  306. package/dist/tool-executor.d.ts +0 -28
  307. package/dist/tool-executor.d.ts.map +0 -1
  308. package/dist/tool-executor.js +0 -74
  309. package/dist/tools/file-editing-utils.d.ts +0 -2
  310. package/dist/tools/file-editing-utils.d.ts.map +0 -1
  311. package/dist/tools/file-editing-utils.js +0 -135
@@ -1,122 +1,71 @@
1
1
  import { readFile, writeFile } from "node:fs/promises";
2
- import { tool } from "ai";
3
2
  import { createTwoFilesPatch } from "diff";
4
3
  import { z } from "zod";
4
+ import { logger } from "../logger.js";
5
5
  import style from "../terminal/style.js";
6
6
  import { joinWorkingDir, validatePath } from "./filesystem-utils.js";
7
+ import { fixLlmEditWithInstruction } from "./llm-edit-fixer.js";
7
8
  export const EditFileTool = {
8
9
  name: "editFile",
9
10
  };
10
- export const createEditFileTool = async ({ workingDir, terminal, sendData, toolExecutor, }) => {
11
- const allowedDirectory = workingDir;
11
+ const inputSchema = z.object({
12
+ path: z.string().describe("The path of the file to edit."),
13
+ edits: z.array(z.object({
14
+ oldText: z
15
+ .string()
16
+ .describe("Text to search for - must match exactly and enough context must be provided to uniquely match the target text. " +
17
+ "Special characters require JSON escaping: backticks (\\`...\\`), quotes, backslashes. " +
18
+ "For multi-line content, include exact newlines and indentation."),
19
+ newText: z.string().describe("Text to replace with"),
20
+ })),
21
+ });
22
+ export const createEditFileTool = async ({ workingDir, allowedDirs, modelManager, tokenTracker, enableLlmFix = true, }) => {
23
+ const allowedDirectory = allowedDirs ?? [workingDir];
12
24
  return {
13
- [EditFileTool.name]: tool({
25
+ toolDef: {
14
26
  description: "Make line-based edits to a text file. Each edit replaces exact line sequences " +
15
27
  "with new content. Exact literal matching is used: no whitespace, indentation, escape, or newline normalization is applied when locating matches. " +
16
28
  "Provide enough context so the match is unique; otherwise the operation errors. Returns a git-style diff showing the changes made. " +
17
29
  "Only works within allowed directories. " +
18
30
  "Note: Special characters in oldText must be properly escaped for JSON (e.g., backticks as \\`...\\`). " +
19
31
  "Multi-line strings require exact character-by-character matching including whitespace.",
20
- inputSchema: z.object({
21
- path: z.string().describe("The path of the file to edit."),
22
- edits: z.array(z.object({
23
- oldText: z
24
- .string()
25
- .describe("Text to search for - must match exactly and enough context must be provided to uniquely match the target text. " +
26
- "Special characters require JSON escaping: backticks (\\`...\\`), quotes, backslashes. " +
27
- "For multi-line content, include exact newlines and indentation."),
28
- newText: z.string().describe("Text to replace with"),
29
- })),
30
- }),
31
- execute: async ({ path, edits }, { toolCallId, abortSignal }) => {
32
- // Check if execution has been aborted
32
+ inputSchema,
33
+ },
34
+ async *execute({ path, edits }, { toolCallId, abortSignal }) {
35
+ try {
33
36
  if (abortSignal?.aborted) {
34
37
  throw new Error("File editing aborted");
35
38
  }
36
- sendData?.({
39
+ yield {
40
+ name: EditFileTool.name,
37
41
  id: toolCallId,
38
42
  event: "tool-init",
39
- data: `Editing file: ${style.cyan(path)}`,
43
+ data: `${style.cyan(path)}`,
44
+ };
45
+ const validPath = await validatePath(joinWorkingDir(path, workingDir), allowedDirectory, { abortSignal });
46
+ const result = await applyFileEdits(validPath, edits, false, abortSignal, {
47
+ modelManager,
48
+ tokenTracker,
49
+ enableLlmFix,
40
50
  });
41
- try {
42
- const validPath = await validatePath(joinWorkingDir(path, workingDir), allowedDirectory, { abortSignal });
43
- if (terminal) {
44
- terminal.writeln(`\n${style.blue.bold("")} Proposing file changes: ${style.cyan(path)}`);
45
- terminal.lineBreak();
46
- const result = await applyFileEdits(validPath, edits, true, abortSignal);
47
- terminal.writeln(`The agent is proposing the following ${style.cyan(edits.length)} edits:`);
48
- terminal.hr();
49
- terminal.display(result);
50
- terminal.hr();
51
- let userResponse;
52
- if (toolExecutor) {
53
- const ctx = {
54
- toolName: EditFileTool.name,
55
- toolCallId,
56
- message: "What would you like to do with these changes?",
57
- choices: {
58
- accept: "Accept these changes",
59
- acceptAll: "Accept all future edits (including these)",
60
- reject: "Reject these changes",
61
- },
62
- };
63
- try {
64
- userResponse = await toolExecutor.ask(ctx, { abortSignal });
65
- }
66
- catch (e) {
67
- if (e.name === "AbortError") {
68
- throw new Error("File editing aborted during user input");
69
- }
70
- throw e;
71
- }
72
- }
73
- const { result: userChoice, reason } = userResponse ?? {
74
- result: "accept",
75
- };
76
- terminal.lineBreak();
77
- if (userChoice === "accept-all") {
78
- terminal.writeln(style.yellow("✓ Auto-accept mode enabled for all edits"));
79
- terminal.lineBreak();
80
- }
81
- if (userChoice === "accept" || userChoice === "accept-all") {
82
- const finalEdits = await applyFileEdits(validPath, edits, false, abortSignal);
83
- // Send completion message indicating success
84
- sendData?.({
85
- id: toolCallId,
86
- event: "tool-completion",
87
- data: "Edits accepted and applied successfully.",
88
- });
89
- return finalEdits;
90
- }
91
- terminal.lineBreak();
92
- // Send completion message indicating rejection
93
- const rejectionReason = reason || "No reason provided";
94
- sendData?.({
95
- id: toolCallId,
96
- event: "tool-completion",
97
- data: `Edits rejected by user. Reason: ${rejectionReason}`,
98
- });
99
- return `The user rejected these changes. Reason: ${rejectionReason}`;
100
- }
101
- const finalEdits = await applyFileEdits(validPath, edits, false, abortSignal);
102
- // Send completion message indicating success
103
- sendData?.({
104
- id: toolCallId,
105
- event: "tool-completion",
106
- data: "Edits accepted and applied successfully.",
107
- });
108
- return finalEdits;
109
- }
110
- catch (error) {
111
- sendData?.({
112
- event: "tool-error",
113
- id: toolCallId,
114
- data: `Failed to edit file: ${error.message}`,
115
- });
116
- return `Failed to edit file: ${error.message}`;
117
- }
118
- },
119
- }),
51
+ yield {
52
+ name: EditFileTool.name,
53
+ id: toolCallId,
54
+ event: "tool-completion",
55
+ data: `Applied ${edits.length} edits`,
56
+ };
57
+ yield result;
58
+ }
59
+ catch (error) {
60
+ yield {
61
+ name: EditFileTool.name,
62
+ event: "tool-error",
63
+ id: toolCallId,
64
+ data: error.message,
65
+ };
66
+ yield `Failed to edit file: ${error.message}`;
67
+ }
68
+ },
120
69
  };
121
70
  };
122
71
  // file editing and diffing utilities
@@ -129,7 +78,7 @@ function createUnifiedDiff(originalContent, newContent, filepath = "file") {
129
78
  const normalizedNew = normalizeLineEndings(newContent);
130
79
  return createTwoFilesPatch(filepath, filepath, normalizedOriginal, normalizedNew, "original", "modified");
131
80
  }
132
- export async function applyFileEdits(filePath, edits, dryRun = false, abortSignal) {
81
+ export async function applyFileEdits(filePath, edits, dryRun = false, abortSignal, options) {
133
82
  if (abortSignal?.aborted) {
134
83
  throw new Error("File edit operation aborted");
135
84
  }
@@ -141,26 +90,19 @@ export async function applyFileEdits(filePath, edits, dryRun = false, abortSigna
141
90
  if (edits.find((edit) => edit.oldText.length === 0)) {
142
91
  throw new Error("Invalid oldText in edit. The value of oldText must be at least one character");
143
92
  }
144
- // Apply edits sequentially using strict literal, unique matches
93
+ // Apply edits sequentially using literal matches (allow multiple matches)
145
94
  let modifiedContent = originalContent;
146
95
  for (const edit of edits) {
147
96
  if (abortSignal?.aborted) {
148
97
  throw new Error("File edit operation aborted during processing");
149
98
  }
150
- const { oldText, newText } = edit; // Use literal oldText and newText
151
- // Strict literal match: find exactly one occurrence without any normalization
152
- const firstIndex = modifiedContent.indexOf(oldText);
153
- if (firstIndex === -1) {
154
- throw new Error("oldText not found in content");
99
+ const result = await applyEditWithLlmFix(edit, modifiedContent, abortSignal, options);
100
+ if (result.success) {
101
+ modifiedContent = result.content;
155
102
  }
156
- const secondIndex = modifiedContent.indexOf(oldText, firstIndex + oldText.length);
157
- if (secondIndex !== -1) {
158
- throw new Error("oldText found multiple times and requires more code context to uniquely identify the intended match");
103
+ else {
104
+ throw new Error("oldText not found in content");
159
105
  }
160
- modifiedContent =
161
- modifiedContent.slice(0, firstIndex) +
162
- newText +
163
- modifiedContent.slice(firstIndex + oldText.length);
164
106
  }
165
107
  // Create unified diff (createUnifiedDiff normalizes line endings internally for diffing)
166
108
  const diff = createUnifiedDiff(originalContent, modifiedContent, filePath);
@@ -182,3 +124,61 @@ export async function applyFileEdits(filePath, edits, dryRun = false, abortSigna
182
124
  }
183
125
  return formattedDiff;
184
126
  }
127
+ /**
128
+ * Applies a single edit with LLM fix fallback
129
+ */
130
+ async function applyEditWithLlmFix(edit, content, abortSignal, options) {
131
+ const { oldText, newText } = edit;
132
+ // Try the original edit first
133
+ const originalResult = applyLiteralEdit(content, oldText, newText);
134
+ if (originalResult.matchCount > 0) {
135
+ return { success: true, content: originalResult.content };
136
+ }
137
+ // If LLM fix is enabled and dependencies are available, try to fix the edit
138
+ if (options?.enableLlmFix !== false &&
139
+ options?.modelManager &&
140
+ options?.tokenTracker) {
141
+ try {
142
+ const fixedEdit = await fixLlmEditWithInstruction(options.instruction, oldText, newText, "oldText not found in content", content, options.modelManager ?? undefined, abortSignal);
143
+ if (fixedEdit && !fixedEdit.noChangesRequired) {
144
+ // Retry the edit with the corrected search string
145
+ const correctedResult = applyLiteralEdit(content, fixedEdit.search, fixedEdit.replace);
146
+ if (correctedResult.matchCount > 0) {
147
+ return { success: true, content: correctedResult.content };
148
+ }
149
+ }
150
+ else if (fixedEdit?.noChangesRequired) {
151
+ // No changes required, skip this edit
152
+ return { success: true, content };
153
+ }
154
+ }
155
+ catch (llmError) {
156
+ // If LLM fix fails, fall back to original error
157
+ logger.warn(llmError, "LLM edit fix failed:");
158
+ }
159
+ }
160
+ return { success: false, content };
161
+ }
162
+ /**
163
+ * Applies a literal search and replace operation
164
+ */
165
+ function applyLiteralEdit(content, search, replace) {
166
+ let modifiedContent = content;
167
+ let matchCount = 0;
168
+ let currentIndex = 0;
169
+ while (currentIndex < modifiedContent.length) {
170
+ const matchIndex = modifiedContent.indexOf(search, currentIndex);
171
+ if (matchIndex === -1) {
172
+ break;
173
+ }
174
+ matchCount++;
175
+ // Apply the replacement
176
+ modifiedContent =
177
+ modifiedContent.slice(0, matchIndex) +
178
+ replace +
179
+ modifiedContent.slice(matchIndex + search.length);
180
+ // Move current index past the replacement
181
+ currentIndex = matchIndex + replace.length;
182
+ }
183
+ return { matchCount, content: modifiedContent };
184
+ }
@@ -1,6 +1,7 @@
1
1
  export declare function joinWorkingDir(userPath: string, workingDir: string): string;
2
2
  export declare function isPathWithinBaseDir(requestedPath: string, baseDir: string): boolean;
3
- export declare function validatePath(requestedPath: string, allowedDirectory: string, options?: {
3
+ export declare function isPathWithinAllowedDirs(requestedPath: string, allowedDirs: string[]): boolean;
4
+ export declare function validatePath(requestedPath: string, allowedDirectory: string | string[], options?: {
4
5
  requireExistence?: boolean;
5
6
  abortSignal?: AbortSignal;
6
7
  }): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem-utils.d.ts","sourceRoot":"","sources":["../../source/tools/filesystem-utils.ts"],"names":[],"mappings":"AAWA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAK3E;AAUD,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAsBT;AAGD,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAA;CAAO,GACtE,OAAO,CAAC,MAAM,CAAC,CA0GjB"}
1
+ {"version":3,"file":"filesystem-utils.d.ts","sourceRoot":"","sources":["../../source/tools/filesystem-utils.ts"],"names":[],"mappings":"AAWA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAK3E;AAUD,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAsBT;AAGD,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAIT;AAGD,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,GAAG,MAAM,EAAE,EACnC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAA;CAAO,GACtE,OAAO,CAAC,MAAM,CAAC,CAwHjB"}
@@ -42,6 +42,10 @@ export function isPathWithinBaseDir(requestedPath, baseDir) {
42
42
  const rel = path.relative(baseReal, target);
43
43
  return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
44
44
  }
45
+ // Check if path is within any of the allowed directories
46
+ export function isPathWithinAllowedDirs(requestedPath, allowedDirs) {
47
+ return allowedDirs.some((allowedDir) => isPathWithinBaseDir(requestedPath, allowedDir));
48
+ }
45
49
  // Security utilities
46
50
  export async function validatePath(requestedPath, allowedDirectory, options = {}) {
47
51
  const { requireExistence = true, abortSignal } = options;
@@ -53,27 +57,37 @@ export async function validatePath(requestedPath, allowedDirectory, options = {}
53
57
  ? path.resolve(expandedPath)
54
58
  : path.resolve(process.cwd(), expandedPath);
55
59
  const normalizedRequested = normalizePath(absolute);
56
- let normalizedAllowed = normalizePath(path.resolve(allowedDirectory));
57
- // Try to resolve real path for allowedDirectory when it exists to handle symlinked roots
58
- try {
59
- const stats = await fs.stat(normalizedAllowed);
60
- if (stats.isDirectory()) {
61
- const allowedReal = await fs.realpath(normalizedAllowed);
62
- normalizedAllowed = normalizePath(allowedReal);
60
+ // Handle both single directory and array of directories
61
+ const allowedDirectories = Array.isArray(allowedDirectory)
62
+ ? allowedDirectory
63
+ : [allowedDirectory];
64
+ // Resolve and normalize all allowed directories
65
+ const normalizedAllowedDirs = await Promise.all(allowedDirectories.map(async (dir) => {
66
+ let normalizedDir = normalizePath(path.resolve(dir));
67
+ // Try to resolve real path for allowedDirectory when it exists to handle symlinked roots
68
+ try {
69
+ const stats = await fs.stat(normalizedDir);
70
+ if (stats.isDirectory()) {
71
+ const allowedReal = await fs.realpath(normalizedDir);
72
+ normalizedDir = normalizePath(allowedReal);
73
+ }
63
74
  }
64
- }
65
- catch (_err) {
66
- // If allowedDirectory doesn't exist, keep normalizedAllowed as-is
67
- }
68
- // Helper to check if a path is within the allowed directory using path.relative
75
+ catch (_err) {
76
+ // If allowedDirectory doesn't exist, keep normalizedDir as-is
77
+ }
78
+ return normalizedDir;
79
+ }));
80
+ // Helper to check if a path is within any allowed directory using path.relative
69
81
  const isWithinAllowed = (targetPath) => {
70
- const rel = path.relative(normalizedAllowed, targetPath);
71
- // Allow the allowed directory itself (rel === "") and any descendant paths
72
- return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
82
+ return normalizedAllowedDirs.some((normalizedAllowed) => {
83
+ const rel = path.relative(normalizedAllowed, targetPath);
84
+ // Allow the allowed directory itself (rel === "") and any descendant paths
85
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
86
+ });
73
87
  };
74
- // Check intended path is within allowed directory
88
+ // Check intended path is within any allowed directory
75
89
  if (!isWithinAllowed(normalizedRequested)) {
76
- throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectory}`);
90
+ throw new Error(`Access denied - path outside allowed directories: ${absolute} not in any of ${allowedDirectories.join(", ")}`);
77
91
  }
78
92
  let validatedPath;
79
93
  // Try to resolve real path for existing targets to handle symlinks safely
@@ -0,0 +1,36 @@
1
+ import type { ToolCallOptions } from "ai";
2
+ import { z } from "zod";
3
+ import type { TokenCounter } from "../tokens/counter.ts";
4
+ import type { ToolResult } from "./types.ts";
5
+ export declare const GlobTool: {
6
+ name: "globFiles";
7
+ };
8
+ export declare const inputSchema: z.ZodObject<{
9
+ patterns: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
10
+ path: z.ZodString;
11
+ gitignore: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
12
+ recursive: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
13
+ expandDirectories: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
14
+ ignoreFiles: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
15
+ cwd: z.ZodNullable<z.ZodCoercedString<unknown>>;
16
+ }, z.core.$strip>;
17
+ type GlobInputSchema = z.infer<typeof inputSchema>;
18
+ export declare const createGlobTool: (options: {
19
+ tokenCounter: TokenCounter;
20
+ }) => {
21
+ toolDef: {
22
+ description: string;
23
+ inputSchema: z.ZodObject<{
24
+ patterns: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
25
+ path: z.ZodString;
26
+ gitignore: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
27
+ recursive: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
28
+ expandDirectories: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
29
+ ignoreFiles: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
30
+ cwd: z.ZodNullable<z.ZodCoercedString<unknown>>;
31
+ }, z.core.$strip>;
32
+ };
33
+ execute({ patterns, path, gitignore, recursive, expandDirectories, ignoreFiles, cwd, }: GlobInputSchema, { toolCallId, abortSignal }: ToolCallOptions): AsyncGenerator<ToolResult>;
34
+ };
35
+ export {};
36
+ //# sourceMappingURL=glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../source/tools/glob.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;iBA2BtB,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc,GAAI,SAAS;IAAE,YAAY,EAAE,YAAY,CAAA;CAAE;;;;;;;;;;;;;4FAkB7D,eAAe,+BACW,eAAe,GAC3C,cAAc,CAAC,UAAU,CAAC;CA2HhC,CAAC"}
@@ -0,0 +1,143 @@
1
+ import * as fs from "node:fs";
2
+ import * as nodePath from "node:path";
3
+ import { inspect } from "node:util";
4
+ import { z } from "zod";
5
+ import style from "../terminal/style.js";
6
+ import { manageTokenLimit } from "../tokens/threshold.js";
7
+ import { glob } from "../utils/glob.js";
8
+ export const GlobTool = {
9
+ name: "globFiles",
10
+ };
11
+ export const inputSchema = z.object({
12
+ patterns: z
13
+ .union([z.string(), z.array(z.string())])
14
+ .describe("Glob patterns to search for (e.g., '*.ts', '**/*.test.ts', 'src/**/*.js')"),
15
+ path: z.string().describe("Base directory to search in"),
16
+ gitignore: z.coerce
17
+ .boolean()
18
+ .nullable()
19
+ .describe("Respect ignore patterns in .gitignore files. (default: true)"),
20
+ recursive: z.coerce
21
+ .boolean()
22
+ .nullable()
23
+ .describe("Search recursively. (default: true)"),
24
+ expandDirectories: z.coerce
25
+ .boolean()
26
+ .nullable()
27
+ .describe("Automatically expand directories to files. (default: true)"),
28
+ ignoreFiles: z
29
+ .union([z.string(), z.array(z.string())])
30
+ .nullable()
31
+ .describe("Glob patterns to look for ignore files. (default: undefined)"),
32
+ cwd: z.coerce
33
+ .string()
34
+ .nullable()
35
+ .describe("Current working directory override. (default: process.cwd())"),
36
+ });
37
+ export const createGlobTool = (options) => {
38
+ const { tokenCounter } = options;
39
+ return {
40
+ toolDef: {
41
+ description: "Search for files using glob patterns (e.g., `*.ts`, `**/*.test.ts`, `src/**/*.js`). Uses the fast-glob library with support for gitignore, recursive searching, and directory expansion.",
42
+ inputSchema,
43
+ },
44
+ async *execute({ patterns, path, gitignore, recursive, expandDirectories, ignoreFiles, cwd, }, { toolCallId, abortSignal }) {
45
+ // Check if execution has been aborted
46
+ if (abortSignal?.aborted) {
47
+ throw new Error("Glob search aborted");
48
+ }
49
+ try {
50
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
51
+ yield {
52
+ name: GlobTool.name,
53
+ event: "tool-init",
54
+ id: toolCallId,
55
+ data: `${style.cyan(inspect(patternArray))} in ${style.cyan(path)}`,
56
+ };
57
+ // Build glob options
58
+ const globOptions = {
59
+ cwd: cwd || process.cwd(),
60
+ };
61
+ if (gitignore !== null) {
62
+ globOptions["gitignore"] = gitignore;
63
+ }
64
+ if (recursive !== null) {
65
+ globOptions["recursive"] = recursive;
66
+ }
67
+ if (expandDirectories !== null) {
68
+ globOptions["expandDirectories"] = expandDirectories;
69
+ }
70
+ if (ignoreFiles !== null) {
71
+ globOptions["ignoreFiles"] = ignoreFiles;
72
+ }
73
+ // Execute glob search
74
+ const matchingFiles = await glob(patternArray, {
75
+ ...globOptions,
76
+ cwd: path,
77
+ });
78
+ // Get file stats and sort by recency then alphabetically
79
+ const filesWithStats = await Promise.all(matchingFiles.map(async (filePath) => {
80
+ const fullPath = nodePath.join(path, filePath);
81
+ try {
82
+ const stats = await fs.promises.stat(fullPath);
83
+ return {
84
+ path: filePath,
85
+ mtime: stats.mtime,
86
+ isRecent: Date.now() - stats.mtime.getTime() < 7 * 24 * 60 * 60 * 1000, // 7 days
87
+ };
88
+ }
89
+ catch {
90
+ // If stat fails, treat as old file
91
+ return {
92
+ path: filePath,
93
+ mtime: new Date(0),
94
+ isRecent: false,
95
+ };
96
+ }
97
+ }));
98
+ // Sort files: recent files first (newest to oldest), then older files alphabetically
99
+ const sortedFiles = filesWithStats
100
+ .sort((a, b) => {
101
+ // Recent files come first
102
+ if (a.isRecent && !b.isRecent)
103
+ return -1;
104
+ if (!a.isRecent && b.isRecent)
105
+ return 1;
106
+ // Both recent: sort by modification time (newest first)
107
+ if (a.isRecent && b.isRecent) {
108
+ return b.mtime.getTime() - a.mtime.getTime();
109
+ }
110
+ // Both old: sort alphabetically by path
111
+ return a.path.localeCompare(b.path);
112
+ })
113
+ .map((file) => file.path);
114
+ // Format results
115
+ const resultContent = sortedFiles.length > 0
116
+ ? sortedFiles.join("\n")
117
+ : "No files found matching the specified patterns.";
118
+ const result = await manageTokenLimit(resultContent, tokenCounter, "Glob", "Use more specific glob patterns or recursive=false to reduce matches");
119
+ let completionMessage = `Found ${style.cyan(sortedFiles.length)} files.`;
120
+ if (result.truncated) {
121
+ completionMessage += ` ${result.content}`;
122
+ }
123
+ completionMessage += ` (${result.tokenCount} tokens)`;
124
+ yield {
125
+ name: GlobTool.name,
126
+ event: "tool-completion",
127
+ id: toolCallId,
128
+ data: completionMessage,
129
+ };
130
+ yield result.content;
131
+ }
132
+ catch (error) {
133
+ yield {
134
+ name: GlobTool.name,
135
+ event: "tool-error",
136
+ id: toolCallId,
137
+ data: error.message,
138
+ };
139
+ yield error.message;
140
+ }
141
+ },
142
+ };
143
+ };
@@ -1,22 +1,40 @@
1
+ import type { ToolCallOptions } from "ai";
2
+ import { z } from "zod";
1
3
  import type { TokenCounter } from "../tokens/counter.ts";
2
- import type { SendData } from "./types.ts";
4
+ import type { ToolResult } from "./types.ts";
3
5
  export declare const GrepTool: {
4
6
  name: "grepFiles";
5
7
  };
8
+ declare const inputSchema: z.ZodObject<{
9
+ pattern: z.ZodString;
10
+ path: z.ZodString;
11
+ recursive: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
12
+ ignoreCase: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
13
+ filePattern: z.ZodNullable<z.ZodCoercedString<unknown>>;
14
+ contextLines: z.ZodNullable<z.ZodCoercedNumber<unknown>>;
15
+ searchIgnored: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
16
+ literal: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
17
+ maxResults: z.ZodNullable<z.ZodCoercedNumber<unknown>>;
18
+ }, z.core.$strip>;
19
+ type GrepInputSchema = z.infer<typeof inputSchema>;
6
20
  export declare const createGrepTool: (options: {
7
- sendData?: SendData | undefined;
8
21
  tokenCounter: TokenCounter;
9
22
  }) => {
10
- grepFiles: import("ai").Tool<{
11
- pattern: string;
12
- path: string;
13
- recursive: boolean | null;
14
- ignoreCase: boolean | null;
15
- filePattern: string | null;
16
- contextLines: number | null;
17
- searchIgnored: boolean | null;
18
- literal: boolean | null;
19
- }, string>;
23
+ toolDef: {
24
+ description: string;
25
+ inputSchema: z.ZodObject<{
26
+ pattern: z.ZodString;
27
+ path: z.ZodString;
28
+ recursive: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
29
+ ignoreCase: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
30
+ filePattern: z.ZodNullable<z.ZodCoercedString<unknown>>;
31
+ contextLines: z.ZodNullable<z.ZodCoercedNumber<unknown>>;
32
+ searchIgnored: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
33
+ literal: z.ZodNullable<z.ZodCoercedBoolean<unknown>>;
34
+ maxResults: z.ZodNullable<z.ZodCoercedNumber<unknown>>;
35
+ }, z.core.$strip>;
36
+ };
37
+ execute({ pattern, path, recursive, ignoreCase, filePattern, contextLines, searchIgnored, literal, maxResults, }: GrepInputSchema, { toolCallId, abortSignal }: ToolCallOptions): AsyncGenerator<ToolResult>;
20
38
  };
21
39
  interface GrepOptions {
22
40
  recursive?: boolean | null;
@@ -25,7 +43,9 @@ interface GrepOptions {
25
43
  contextLines?: number | null;
26
44
  searchIgnored?: boolean | null;
27
45
  literal?: boolean | null;
46
+ maxResults?: number | null;
28
47
  }
48
+ export declare function likelyUnbalancedRegex(pattern: string): boolean;
29
49
  /**
30
50
  * Search files for patterns using ripgrep
31
51
  *
@@ -35,5 +55,46 @@ interface GrepOptions {
35
55
  * @returns The result of the grep command
36
56
  */
37
57
  export declare function buildGrepCommand(pattern: string, path: string, options?: GrepOptions): string;
58
+ export interface ParsedMatch {
59
+ file?: string;
60
+ line: number;
61
+ content: string;
62
+ isMatch: boolean;
63
+ isContext?: boolean;
64
+ }
65
+ interface GrepResult {
66
+ rawOutput: string;
67
+ parsedMatches: ParsedMatch[];
68
+ matchCount: number;
69
+ displayedCount?: number;
70
+ contextCount: number;
71
+ hasMatches: boolean;
72
+ isTruncated?: boolean;
73
+ }
74
+ /**
75
+ * Parse ripgrep output and extract structured match information
76
+ */
77
+ export declare function parseRipgrepOutput(content: string): ParsedMatch[];
78
+ /**
79
+ * Count actual matches (excluding context lines)
80
+ */
81
+ export declare function countActualMatches(parsed: ParsedMatch[]): number;
82
+ /**
83
+ * Count context lines
84
+ */
85
+ export declare function countContextLines(parsed: ParsedMatch[]): number;
86
+ /**
87
+ * Truncate matches to a maximum number of results
88
+ */
89
+ export declare function truncateMatches(matches: ParsedMatch[], maxResults: number | null | undefined): {
90
+ truncated: ParsedMatch[];
91
+ isTruncated: boolean;
92
+ };
93
+ /**
94
+ * Extract matches from content (backwards compatibility wrapper)
95
+ */
96
+ export declare function extractMatches(content: string): string[];
97
+ export declare function grepFiles(pattern: string, path: string, options?: GrepOptions): string;
98
+ export declare function grepFilesStructured(pattern: string, path: string, options?: GrepOptions): GrepResult;
38
99
  export {};
39
100
  //# sourceMappingURL=grep.d.ts.map