@travisennis/acai 0.0.9 → 0.0.11

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 (403) hide show
  1. package/README.md +51 -760
  2. package/bin/acai +52 -0
  3. package/dist/agent/index.d.ts +12 -2
  4. package/dist/agent/index.d.ts.map +1 -1
  5. package/dist/agent/index.js +380 -199
  6. package/dist/agent/sub-agent.d.ts +23 -0
  7. package/dist/agent/sub-agent.d.ts.map +1 -0
  8. package/dist/agent/sub-agent.js +109 -0
  9. package/dist/cli/index.d.ts +26 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/{cli.js → cli/index.js} +84 -77
  12. package/dist/{stdin.d.ts → cli/stdin.d.ts} +2 -1
  13. package/dist/cli/stdin.d.ts.map +1 -0
  14. package/dist/{stdin.js → cli/stdin.js} +11 -0
  15. package/dist/commands/copy/index.js +2 -2
  16. package/dist/commands/copy/utils.d.ts.map +1 -1
  17. package/dist/commands/copy/utils.js +15 -13
  18. package/dist/commands/generate-rules/index.d.ts +1 -1
  19. package/dist/commands/generate-rules/index.d.ts.map +1 -1
  20. package/dist/commands/generate-rules/index.js +16 -101
  21. package/dist/commands/generate-rules/service.d.ts +22 -0
  22. package/dist/commands/generate-rules/service.d.ts.map +1 -0
  23. package/dist/commands/generate-rules/service.js +103 -0
  24. package/dist/commands/handoff/index.js +2 -2
  25. package/dist/commands/health/index.js +1 -1
  26. package/dist/commands/health/utils.d.ts +3 -2
  27. package/dist/commands/health/utils.d.ts.map +1 -1
  28. package/dist/commands/health/utils.js +6 -0
  29. package/dist/commands/history/index.d.ts +1 -1
  30. package/dist/commands/history/index.d.ts.map +1 -1
  31. package/dist/commands/history/index.js +17 -18
  32. package/dist/commands/history/types.d.ts +38 -0
  33. package/dist/commands/history/types.d.ts.map +1 -1
  34. package/dist/commands/history/utils.d.ts.map +1 -1
  35. package/dist/commands/history/utils.js +63 -58
  36. package/dist/commands/init/index.d.ts.map +1 -1
  37. package/dist/commands/init/index.js +3 -8
  38. package/dist/commands/init-project/index.d.ts.map +1 -1
  39. package/dist/commands/init-project/index.js +3 -3
  40. package/dist/commands/init-project/utils.d.ts +2 -1
  41. package/dist/commands/init-project/utils.d.ts.map +1 -1
  42. package/dist/commands/init-project/utils.js +10 -2
  43. package/dist/commands/list-tools/index.d.ts.map +1 -1
  44. package/dist/commands/list-tools/index.js +7 -31
  45. package/dist/commands/manager.d.ts +2 -2
  46. package/dist/commands/manager.d.ts.map +1 -1
  47. package/dist/commands/manager.js +55 -33
  48. package/dist/commands/model/index.d.ts.map +1 -1
  49. package/dist/commands/model/index.js +20 -151
  50. package/dist/commands/model/model-panel.d.ts +4 -0
  51. package/dist/commands/model/model-panel.d.ts.map +1 -0
  52. package/dist/commands/model/model-panel.js +144 -0
  53. package/dist/commands/paste/index.d.ts.map +1 -1
  54. package/dist/commands/paste/index.js +59 -62
  55. package/dist/commands/paste/utils.d.ts.map +1 -1
  56. package/dist/commands/paste/utils.js +88 -58
  57. package/dist/commands/pickup/index.d.ts.map +1 -1
  58. package/dist/commands/pickup/index.js +6 -3
  59. package/dist/commands/pickup/utils.js +3 -3
  60. package/dist/commands/resources/index.d.ts.map +1 -1
  61. package/dist/commands/resources/index.js +33 -50
  62. package/dist/commands/review/index.d.ts.map +1 -1
  63. package/dist/commands/review/index.js +3 -117
  64. package/dist/commands/review/review-panel.d.ts +3 -0
  65. package/dist/commands/review/review-panel.d.ts.map +1 -0
  66. package/dist/commands/review/review-panel.js +186 -0
  67. package/dist/commands/review/utils.d.ts +15 -1
  68. package/dist/commands/review/utils.d.ts.map +1 -1
  69. package/dist/commands/review/utils.js +127 -68
  70. package/dist/commands/session/index.d.ts +1 -1
  71. package/dist/commands/session/index.d.ts.map +1 -1
  72. package/dist/commands/session/index.js +124 -135
  73. package/dist/commands/shell/index.d.ts.map +1 -1
  74. package/dist/commands/shell/index.js +16 -1
  75. package/dist/commands/types.d.ts +2 -2
  76. package/dist/commands/types.d.ts.map +1 -1
  77. package/dist/{config.d.ts → config/index.d.ts} +20 -9
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/{config.js → config/index.js} +43 -42
  80. package/dist/execution/index.d.ts.map +1 -1
  81. package/dist/execution/index.js +75 -55
  82. package/dist/index.d.ts +1 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +148 -141
  85. package/dist/middleware/cache.d.ts.map +1 -1
  86. package/dist/middleware/cache.js +18 -36
  87. package/dist/models/ai-config.d.ts +1 -0
  88. package/dist/models/ai-config.d.ts.map +1 -1
  89. package/dist/models/ai-config.js +4 -3
  90. package/dist/models/anthropic-provider.d.ts +2 -5
  91. package/dist/models/anthropic-provider.d.ts.map +1 -1
  92. package/dist/models/anthropic-provider.js +3 -70
  93. package/dist/models/deepseek-provider.d.ts +1 -0
  94. package/dist/models/deepseek-provider.d.ts.map +1 -1
  95. package/dist/models/google-provider.d.ts +2 -3
  96. package/dist/models/google-provider.d.ts.map +1 -1
  97. package/dist/models/google-provider.js +0 -26
  98. package/dist/models/groq-provider.d.ts +1 -0
  99. package/dist/models/groq-provider.d.ts.map +1 -1
  100. package/dist/models/manager.d.ts +13 -2
  101. package/dist/models/manager.d.ts.map +1 -1
  102. package/dist/models/manager.js +20 -8
  103. package/dist/models/openai-provider.d.ts +2 -5
  104. package/dist/models/openai-provider.d.ts.map +1 -1
  105. package/dist/models/openai-provider.js +0 -52
  106. package/dist/models/opencode-go-provider.d.ts +25 -0
  107. package/dist/models/opencode-go-provider.d.ts.map +1 -0
  108. package/dist/models/opencode-go-provider.js +78 -0
  109. package/dist/models/opencode-zen-provider.d.ts +7 -3
  110. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  111. package/dist/models/opencode-zen-provider.js +49 -10
  112. package/dist/models/openrouter-provider.d.ts +27 -31
  113. package/dist/models/openrouter-provider.d.ts.map +1 -1
  114. package/dist/models/openrouter-provider.js +121 -180
  115. package/dist/models/providers.d.ts +3 -3
  116. package/dist/models/providers.d.ts.map +1 -1
  117. package/dist/models/providers.js +6 -0
  118. package/dist/models/xai-provider.d.ts +4 -3
  119. package/dist/models/xai-provider.d.ts.map +1 -1
  120. package/dist/models/xai-provider.js +18 -18
  121. package/dist/modes/manager.d.ts +24 -0
  122. package/dist/modes/manager.d.ts.map +1 -0
  123. package/dist/modes/manager.js +77 -0
  124. package/dist/modes/prompts.d.ts +2 -0
  125. package/dist/modes/prompts.d.ts.map +1 -0
  126. package/dist/modes/prompts.js +142 -0
  127. package/dist/prompts/mentions.d.ts +11 -0
  128. package/dist/prompts/mentions.d.ts.map +1 -0
  129. package/dist/{mentions.js → prompts/mentions.js} +55 -85
  130. package/dist/{prompts.d.ts → prompts/system-prompt.d.ts} +7 -2
  131. package/dist/prompts/system-prompt.d.ts.map +1 -0
  132. package/dist/{prompts.js → prompts/system-prompt.js} +31 -16
  133. package/dist/repl/index.d.ts +174 -0
  134. package/dist/repl/index.d.ts.map +1 -0
  135. package/dist/{repl-new.js → repl/index.js} +397 -76
  136. package/dist/repl/project-status.d.ts +1 -0
  137. package/dist/repl/project-status.d.ts.map +1 -1
  138. package/dist/repl/project-status.js +4 -1
  139. package/dist/sessions/manager.d.ts +92 -0
  140. package/dist/sessions/manager.d.ts.map +1 -1
  141. package/dist/sessions/manager.js +262 -9
  142. package/dist/sessions/summary.d.ts +4 -0
  143. package/dist/sessions/summary.d.ts.map +1 -0
  144. package/dist/sessions/summary.js +30 -0
  145. package/dist/skills/index.d.ts +29 -0
  146. package/dist/skills/index.d.ts.map +1 -0
  147. package/dist/skills/index.js +294 -0
  148. package/dist/subagents/index.d.ts +16 -0
  149. package/dist/subagents/index.d.ts.map +1 -0
  150. package/dist/subagents/index.js +231 -0
  151. package/dist/terminal/control.d.ts +1 -1
  152. package/dist/terminal/control.d.ts.map +1 -1
  153. package/dist/terminal/control.js +3 -3
  154. package/dist/terminal/east-asian-width.d.ts.map +1 -1
  155. package/dist/terminal/east-asian-width.js +404 -351
  156. package/dist/terminal/keys.d.ts +17 -0
  157. package/dist/terminal/keys.d.ts.map +1 -1
  158. package/dist/terminal/keys.js +37 -0
  159. package/dist/terminal/select-prompt.d.ts.map +1 -1
  160. package/dist/terminal/select-prompt.js +24 -12
  161. package/dist/terminal/string-width.d.ts.map +1 -1
  162. package/dist/terminal/string-width.js +25 -27
  163. package/dist/terminal/style.d.ts.map +1 -1
  164. package/dist/terminal/style.js +4 -7
  165. package/dist/terminal/supports-color.d.ts.map +1 -1
  166. package/dist/terminal/supports-color.js +41 -27
  167. package/dist/terminal/table/cell.d.ts +12 -0
  168. package/dist/terminal/table/cell.d.ts.map +1 -1
  169. package/dist/terminal/table/cell.js +40 -25
  170. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  171. package/dist/terminal/table/layout-manager.js +100 -68
  172. package/dist/terminal/table/utils.d.ts +1 -1
  173. package/dist/terminal/table/utils.d.ts.map +1 -1
  174. package/dist/terminal/table/utils.js +17 -10
  175. package/dist/terminal/wrap-ansi.d.ts.map +1 -1
  176. package/dist/terminal/wrap-ansi.js +174 -105
  177. package/dist/tokens/tracker.d.ts +1 -0
  178. package/dist/tokens/tracker.d.ts.map +1 -1
  179. package/dist/tokens/tracker.js +3 -0
  180. package/dist/tools/agent.d.ts +27 -0
  181. package/dist/tools/agent.d.ts.map +1 -0
  182. package/dist/tools/agent.js +81 -0
  183. package/dist/tools/apply-patch.d.ts +62 -0
  184. package/dist/tools/apply-patch.d.ts.map +1 -0
  185. package/dist/tools/apply-patch.js +377 -0
  186. package/dist/tools/bash.d.ts +4 -3
  187. package/dist/tools/bash.d.ts.map +1 -1
  188. package/dist/tools/bash.js +349 -141
  189. package/dist/tools/directory-tree.d.ts +3 -3
  190. package/dist/tools/directory-tree.d.ts.map +1 -1
  191. package/dist/tools/directory-tree.js +8 -5
  192. package/dist/tools/dynamic-tool-loader.d.ts +3 -6
  193. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  194. package/dist/tools/dynamic-tool-loader.js +20 -4
  195. package/dist/tools/edit-file.d.ts +7 -7
  196. package/dist/tools/edit-file.d.ts.map +1 -1
  197. package/dist/tools/edit-file.js +292 -85
  198. package/dist/tools/glob.d.ts +6 -6
  199. package/dist/tools/glob.d.ts.map +1 -1
  200. package/dist/tools/glob.js +110 -63
  201. package/dist/tools/grep.d.ts +15 -12
  202. package/dist/tools/grep.d.ts.map +1 -1
  203. package/dist/tools/grep.js +315 -193
  204. package/dist/tools/index.d.ts +114 -9
  205. package/dist/tools/index.d.ts.map +1 -1
  206. package/dist/tools/index.js +39 -24
  207. package/dist/tools/ls.d.ts +2 -2
  208. package/dist/tools/ls.d.ts.map +1 -1
  209. package/dist/tools/ls.js +7 -5
  210. package/dist/tools/read-file.d.ts +4 -6
  211. package/dist/tools/read-file.d.ts.map +1 -1
  212. package/dist/tools/read-file.js +84 -39
  213. package/dist/tools/save-file.d.ts +3 -3
  214. package/dist/tools/save-file.d.ts.map +1 -1
  215. package/dist/tools/save-file.js +36 -31
  216. package/dist/tools/skill.d.ts +23 -0
  217. package/dist/tools/skill.d.ts.map +1 -0
  218. package/dist/tools/skill.js +65 -0
  219. package/dist/tools/think.d.ts.map +1 -1
  220. package/dist/tools/think.js +2 -9
  221. package/dist/tools/utils.d.ts +2 -0
  222. package/dist/tools/utils.d.ts.map +1 -1
  223. package/dist/tools/utils.js +12 -0
  224. package/dist/tools/web-fetch.d.ts +50 -0
  225. package/dist/tools/web-fetch.d.ts.map +1 -0
  226. package/dist/tools/web-fetch.js +446 -0
  227. package/dist/tools/web-search.d.ts +44 -0
  228. package/dist/tools/web-search.d.ts.map +1 -0
  229. package/dist/tools/web-search.js +226 -0
  230. package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
  231. package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
  232. package/dist/tui/autocomplete/attachment-provider.js +25 -78
  233. package/dist/tui/autocomplete/base-provider.d.ts +1 -0
  234. package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
  235. package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
  236. package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
  237. package/dist/tui/autocomplete/combined-provider.js +3 -17
  238. package/dist/tui/autocomplete/command-provider.d.ts +1 -0
  239. package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
  240. package/dist/tui/autocomplete/command-provider.js +3 -0
  241. package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
  242. package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
  243. package/dist/tui/autocomplete/file-search-provider.js +37 -17
  244. package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
  245. package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
  246. package/dist/tui/autocomplete/skill-provider.js +49 -0
  247. package/dist/tui/autocomplete/utils.d.ts +2 -1
  248. package/dist/tui/autocomplete/utils.d.ts.map +1 -1
  249. package/dist/tui/autocomplete/utils.js +25 -23
  250. package/dist/tui/autocomplete.d.ts +2 -2
  251. package/dist/tui/autocomplete.d.ts.map +1 -1
  252. package/dist/tui/autocomplete.js +3 -5
  253. package/dist/tui/components/assistant-message.d.ts.map +1 -1
  254. package/dist/tui/components/assistant-message.js +0 -4
  255. package/dist/tui/components/editor.d.ts +18 -3
  256. package/dist/tui/components/editor.d.ts.map +1 -1
  257. package/dist/tui/components/editor.js +211 -237
  258. package/dist/tui/components/footer.d.ts +6 -4
  259. package/dist/tui/components/footer.d.ts.map +1 -1
  260. package/dist/tui/components/footer.js +49 -25
  261. package/dist/tui/components/markdown.d.ts +10 -7
  262. package/dist/tui/components/markdown.d.ts.map +1 -1
  263. package/dist/tui/components/markdown.js +57 -39
  264. package/dist/tui/components/modal.d.ts.map +1 -1
  265. package/dist/tui/components/modal.js +35 -33
  266. package/dist/tui/components/notification.d.ts +13 -2
  267. package/dist/tui/components/notification.d.ts.map +1 -1
  268. package/dist/tui/components/notification.js +36 -2
  269. package/dist/tui/components/progress-bar.js +1 -1
  270. package/dist/tui/components/select-list.d.ts +1 -0
  271. package/dist/tui/components/select-list.d.ts.map +1 -1
  272. package/dist/tui/components/select-list.js +14 -11
  273. package/dist/tui/components/text.d.ts +16 -0
  274. package/dist/tui/components/text.d.ts.map +1 -1
  275. package/dist/tui/components/text.js +72 -57
  276. package/dist/tui/components/thinking-block.d.ts +9 -0
  277. package/dist/tui/components/thinking-block.d.ts.map +1 -1
  278. package/dist/tui/components/thinking-block.js +43 -11
  279. package/dist/tui/components/tool-execution.d.ts +5 -1
  280. package/dist/tui/components/tool-execution.d.ts.map +1 -1
  281. package/dist/tui/components/tool-execution.js +19 -10
  282. package/dist/tui/components/user-message.d.ts.map +1 -1
  283. package/dist/tui/components/user-message.js +0 -3
  284. package/dist/tui/components/welcome.d.ts +2 -1
  285. package/dist/tui/components/welcome.d.ts.map +1 -1
  286. package/dist/tui/components/welcome.js +2 -2
  287. package/dist/tui/editor-launcher.d.ts +3 -2
  288. package/dist/tui/editor-launcher.d.ts.map +1 -1
  289. package/dist/tui/index.d.ts +0 -1
  290. package/dist/tui/index.d.ts.map +1 -1
  291. package/dist/tui/terminal.d.ts.map +1 -1
  292. package/dist/tui/terminal.js +10 -2
  293. package/dist/tui/tui.d.ts +43 -0
  294. package/dist/tui/tui.d.ts.map +1 -1
  295. package/dist/tui/tui.js +166 -41
  296. package/dist/tui/utils.d.ts +1 -5
  297. package/dist/tui/utils.d.ts.map +1 -1
  298. package/dist/tui/utils.js +271 -44
  299. package/dist/utils/bash/parse.d.ts +19 -0
  300. package/dist/utils/bash/parse.d.ts.map +1 -0
  301. package/dist/utils/bash/parse.js +223 -0
  302. package/dist/utils/bash/quote.d.ts +6 -0
  303. package/dist/utils/bash/quote.d.ts.map +1 -0
  304. package/dist/utils/bash/quote.js +23 -0
  305. package/dist/utils/bash.d.ts.map +1 -1
  306. package/dist/utils/bash.js +211 -126
  307. package/dist/utils/command-protection.d.ts +28 -0
  308. package/dist/utils/command-protection.d.ts.map +1 -0
  309. package/dist/utils/command-protection.js +324 -0
  310. package/dist/utils/dedent.d.ts.map +1 -0
  311. package/dist/utils/env-expand.d.ts +2 -0
  312. package/dist/utils/env-expand.d.ts.map +1 -0
  313. package/dist/utils/env-expand.js +8 -0
  314. package/dist/utils/filesystem/path-display.d.ts +11 -0
  315. package/dist/utils/filesystem/path-display.d.ts.map +1 -0
  316. package/dist/utils/filesystem/path-display.js +32 -0
  317. package/dist/utils/filesystem/security.d.ts +2 -2
  318. package/dist/utils/filesystem/security.d.ts.map +1 -1
  319. package/dist/utils/filesystem/security.js +28 -30
  320. package/dist/utils/formatting.d.ts.map +1 -0
  321. package/dist/{formatting.js → utils/formatting.js} +1 -1
  322. package/dist/utils/git.d.ts +4 -0
  323. package/dist/utils/git.d.ts.map +1 -1
  324. package/dist/utils/git.js +30 -0
  325. package/dist/utils/glob.d.ts +1 -1
  326. package/dist/utils/glob.d.ts.map +1 -1
  327. package/dist/utils/logger.d.ts.map +1 -0
  328. package/dist/{logger.js → utils/logger.js} +1 -1
  329. package/dist/utils/parsing.d.ts.map +1 -0
  330. package/dist/utils/process.d.ts.map +1 -1
  331. package/dist/utils/process.js +90 -37
  332. package/dist/utils/templates.d.ts +2 -0
  333. package/dist/utils/templates.d.ts.map +1 -0
  334. package/dist/utils/templates.js +24 -0
  335. package/dist/utils/version.d.ts.map +1 -0
  336. package/dist/{version.js → utils/version.js} +1 -1
  337. package/package.json +35 -26
  338. package/dist/cli.d.ts +0 -23
  339. package/dist/cli.d.ts.map +0 -1
  340. package/dist/commands/add-directory/types.d.ts +0 -6
  341. package/dist/commands/add-directory/types.d.ts.map +0 -1
  342. package/dist/commands/add-directory/types.js +0 -1
  343. package/dist/commands/copy/types.d.ts +0 -3
  344. package/dist/commands/copy/types.d.ts.map +0 -1
  345. package/dist/commands/copy/types.js +0 -1
  346. package/dist/commands/exit/index.d.ts +0 -10
  347. package/dist/commands/exit/index.d.ts.map +0 -1
  348. package/dist/commands/exit/index.js +0 -21
  349. package/dist/commands/exit/types.d.ts +0 -8
  350. package/dist/commands/exit/types.d.ts.map +0 -1
  351. package/dist/commands/exit/types.js +0 -1
  352. package/dist/commands/exit/utils.d.ts +0 -2
  353. package/dist/commands/exit/utils.d.ts.map +0 -1
  354. package/dist/commands/exit/utils.js +0 -13
  355. package/dist/commands/prompt/index.d.ts +0 -5
  356. package/dist/commands/prompt/index.d.ts.map +0 -1
  357. package/dist/commands/prompt/index.js +0 -122
  358. package/dist/commands/prompt/types.d.ts +0 -15
  359. package/dist/commands/prompt/types.d.ts.map +0 -1
  360. package/dist/commands/prompt/types.js +0 -1
  361. package/dist/commands/prompt/utils.d.ts +0 -12
  362. package/dist/commands/prompt/utils.d.ts.map +0 -1
  363. package/dist/commands/prompt/utils.js +0 -107
  364. package/dist/commands/reset/index.d.ts +0 -3
  365. package/dist/commands/reset/index.d.ts.map +0 -1
  366. package/dist/commands/reset/index.js +0 -25
  367. package/dist/commands/reset/types.d.ts +0 -1
  368. package/dist/commands/reset/types.d.ts.map +0 -1
  369. package/dist/commands/reset/types.js +0 -3
  370. package/dist/commands/review/types.d.ts +0 -12
  371. package/dist/commands/review/types.d.ts.map +0 -1
  372. package/dist/commands/review/types.js +0 -1
  373. package/dist/commands/save/index.d.ts +0 -3
  374. package/dist/commands/save/index.d.ts.map +0 -1
  375. package/dist/commands/save/index.js +0 -19
  376. package/dist/config.d.ts.map +0 -1
  377. package/dist/dedent.d.ts.map +0 -1
  378. package/dist/formatting.d.ts.map +0 -1
  379. package/dist/logger.d.ts.map +0 -1
  380. package/dist/mentions.d.ts +0 -14
  381. package/dist/mentions.d.ts.map +0 -1
  382. package/dist/parsing.d.ts.map +0 -1
  383. package/dist/prompts.d.ts.map +0 -1
  384. package/dist/repl-new.d.ts +0 -65
  385. package/dist/repl-new.d.ts.map +0 -1
  386. package/dist/skills.d.ts +0 -16
  387. package/dist/skills.d.ts.map +0 -1
  388. package/dist/skills.js +0 -233
  389. package/dist/stdin.d.ts.map +0 -1
  390. package/dist/tui/autocomplete/path-provider.d.ts +0 -21
  391. package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
  392. package/dist/tui/autocomplete/path-provider.js +0 -164
  393. package/dist/utils/iterables.d.ts +0 -2
  394. package/dist/utils/iterables.d.ts.map +0 -1
  395. package/dist/utils/iterables.js +0 -6
  396. package/dist/version.d.ts.map +0 -1
  397. /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
  398. /package/dist/{dedent.js → utils/dedent.js} +0 -0
  399. /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
  400. /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
  401. /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
  402. /package/dist/{parsing.js → utils/parsing.js} +0 -0
  403. /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
@@ -16,7 +16,7 @@ declare const toolMetadataSchema: z.ZodObject<{
16
16
  }, z.core.$strip>>;
17
17
  needsApproval: z.ZodDefault<z.ZodBoolean>;
18
18
  }, z.core.$strip>;
19
- export type ToolMetadata = z.infer<typeof toolMetadataSchema>;
19
+ type ToolMetadata = z.infer<typeof toolMetadataSchema>;
20
20
  export declare function parseToolMetadata(output: string): ToolMetadata;
21
21
  interface DynamicToolObject {
22
22
  toolDef: {
@@ -24,13 +24,10 @@ interface DynamicToolObject {
24
24
  inputSchema: any;
25
25
  };
26
26
  execute: (input: Record<string, unknown>, options: ToolExecutionOptions) => Promise<string>;
27
- ask?: (input: Record<string, unknown>, options: ToolExecutionOptions) => Promise<{
28
- approve: boolean;
29
- reason?: string;
30
- }>;
31
27
  }
32
- export declare function loadDynamicTools({ baseDir }: {
28
+ export declare function loadDynamicTools({ baseDir, existingToolNames, }: {
33
29
  baseDir: string;
30
+ existingToolNames?: string[];
34
31
  }): Promise<Record<string, DynamicToolObject>>;
35
32
  export {};
36
33
  //# sourceMappingURL=dynamic-tool-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-tool-loader.d.ts","sourceRoot":"","sources":["../../source/tools/dynamic-tool-loader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;iBAatB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAS9D;AA2LD,UAAU,iBAAiB;IACzB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QAEpB,WAAW,EAAE,GAAG,CAAC;KAClB,CAAC;IACF,OAAO,EAAE,CACP,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,oBAAoB,KAC1B,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,GAAG,CAAC,EAAE,CACJ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,oBAAoB,KAC1B,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAwCD,wBAAsB,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,8CAgEtE"}
1
+ {"version":3,"file":"dynamic-tool-loader.d.ts","sourceRoot":"","sources":["../../source/tools/dynamic-tool-loader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;iBAatB,CAAC;AAEH,KAAK,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEvD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAS9D;AA2LD,UAAU,iBAAiB;IACzB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QAEpB,WAAW,EAAE,GAAG,CAAC;KAClB,CAAC;IACF,OAAO,EAAE,CACP,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,oBAAoB,KAC1B,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB;AAwCD,wBAAsB,gBAAgB,CAAC,EACrC,OAAO,EACP,iBAAsB,GACvB,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B,8CA2FA"}
@@ -3,8 +3,8 @@ import fs from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { z } from "zod";
6
- import { config } from "../config.js";
7
- import { logger } from "../logger.js";
6
+ import { config } from "../config/index.js";
7
+ import { logger } from "../utils/logger.js";
8
8
  // Tool Metadata Schema and Parser
9
9
  const toolMetadataSchema = z.object({
10
10
  name: z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/),
@@ -183,7 +183,7 @@ async function spawnChildProcess(scriptPath, params, abortSignal) {
183
183
  }
184
184
  function createDynamicTool(scriptPath, metadata) {
185
185
  const inputSchema = generateZodSchema(metadata.parameters);
186
- const toolName = `dynamic-${metadata.name}`;
186
+ const toolName = metadata.name;
187
187
  return {
188
188
  [toolName]: {
189
189
  toolDef: {
@@ -208,7 +208,7 @@ function createDynamicTool(scriptPath, metadata) {
208
208
  },
209
209
  };
210
210
  }
211
- export async function loadDynamicTools({ baseDir }) {
211
+ export async function loadDynamicTools({ baseDir, existingToolNames = [], }) {
212
212
  const projectConfig = await config.getConfig();
213
213
  const dynamicConfig = projectConfig.tools.dynamicTools;
214
214
  if (!dynamicConfig.enabled) {
@@ -260,8 +260,24 @@ export async function loadDynamicTools({ baseDir }) {
260
260
  }
261
261
  }
262
262
  const tools = {};
263
+ const conflictingTools = [];
263
264
  for (const [_, { path, metadata }] of toolMap) {
265
+ const toolName = metadata.name;
266
+ // Check for conflicts with existing tools
267
+ if (existingToolNames.includes(toolName)) {
268
+ conflictingTools.push(toolName);
269
+ logger.warn(`Dynamic tool '${toolName}' conflicts with existing tool. Skipping.`);
270
+ continue;
271
+ }
272
+ // Check for duplicate dynamic tool names
273
+ if (tools[toolName]) {
274
+ logger.warn(`Duplicate dynamic tool name '${toolName}' found. Skipping duplicate.`);
275
+ continue;
276
+ }
264
277
  Object.assign(tools, createDynamicTool(path, metadata));
265
278
  }
279
+ if (conflictingTools.length > 0) {
280
+ logger.warn(`Warning: ${conflictingTools.length} dynamic tool(s) skipped due to name conflicts: ${conflictingTools.join(", ")}`);
281
+ }
266
282
  return tools;
267
283
  }
@@ -1,28 +1,28 @@
1
1
  import { z } from "zod";
2
+ import type { WorkspaceContext } from "../index.ts";
2
3
  import type { ToolExecutionOptions } from "./types.ts";
3
4
  export declare const EditFileTool: {
4
5
  name: "Edit";
5
6
  };
6
7
  declare const inputSchema: z.ZodObject<{
7
8
  path: z.ZodString;
8
- edits: z.ZodArray<z.ZodObject<{
9
+ edits: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
9
10
  oldText: z.ZodString;
10
11
  newText: z.ZodString;
11
- }, z.core.$strip>>;
12
+ }, z.core.$strip>>>;
12
13
  }, z.core.$strip>;
13
14
  type EditFileInputSchema = z.infer<typeof inputSchema>;
14
- export declare const createEditFileTool: ({ workingDir, allowedDirs, }: {
15
- workingDir: string;
16
- allowedDirs?: string[];
15
+ export declare const createEditFileTool: (options: {
16
+ workspace: WorkspaceContext;
17
17
  }) => Promise<{
18
18
  toolDef: {
19
19
  description: string;
20
20
  inputSchema: z.ZodObject<{
21
21
  path: z.ZodString;
22
- edits: z.ZodArray<z.ZodObject<{
22
+ edits: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
23
23
  oldText: z.ZodString;
24
24
  newText: z.ZodString;
25
- }, z.core.$strip>>;
25
+ }, z.core.$strip>>>;
26
26
  }, z.core.$strip>;
27
27
  };
28
28
  display({ path, edits }: EditFileInputSchema): string;
@@ -1 +1 @@
1
- {"version":3,"file":"edit-file.d.ts","sourceRoot":"","sources":["../../source/tools/edit-file.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,YAAY;;CAExB,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;iBAcf,CAAC;AAEH,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvD,eAAO,MAAM,kBAAkB,GAAU,8BAGtC;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;;;;;;;;;;;6BAa4B,mBAAmB;6BAIzB,mBAAmB,mBACnB,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EAqBrB,CAAC;AAyBF,UAAU,QAAQ;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,UAAQ,EACd,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,MAAM,CAAC,CAsDjB"}
1
+ {"version":3,"file":"edit-file.d.ts","sourceRoot":"","sources":["../../source/tools/edit-file.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AASpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,YAAY;;CAExB,CAAC;AAIF,QAAA,MAAM,WAAW;;;;;;iBAqCf,CAAC;AAEH,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvD,eAAO,MAAM,kBAAkB,GAAU,SAAS;IAChD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;;;;6BAY4B,mBAAmB;6BAKzB,mBAAmB,mBACnB,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EA6BrB,CAAC;AAiDF,UAAU,QAAQ;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAgQD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,UAAQ,EACd,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,MAAM,CAAC,CAqEjB"}
@@ -1,46 +1,73 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { access, constants, readFile, writeFile } from "node:fs/promises";
2
2
  import { createTwoFilesPatch } from "diff";
3
3
  import { z } from "zod";
4
- import { config } from "../config.js";
4
+ import { config } from "../config/index.js";
5
5
  import { clearProjectStatusCache } from "../repl/project-status.js";
6
6
  import style from "../terminal/style.js";
7
+ import { toDisplayPath } from "../utils/filesystem/path-display.js";
7
8
  import { joinWorkingDir, validateFileNotReadOnly, validatePath, } from "../utils/filesystem/security.js";
8
9
  export const EditFileTool = {
9
10
  name: "Edit",
10
11
  };
12
+ const MAX_EDITS_PER_CALL = 10;
11
13
  const inputSchema = z.object({
12
14
  path: z.string().describe("The path of the file to edit."),
13
- edits: z.array(z.object({
15
+ edits: z
16
+ .preprocess((val) => {
17
+ // Handle case where model passes a JSON string instead of an array
18
+ if (typeof val === "string") {
19
+ const trimmed = val.trim();
20
+ // Try parsing as JSON if it looks like an array
21
+ if (trimmed.startsWith("[")) {
22
+ try {
23
+ const parsed = JSON.parse(trimmed);
24
+ if (Array.isArray(parsed)) {
25
+ return parsed;
26
+ }
27
+ }
28
+ catch {
29
+ // Not valid JSON, treat as a plain string
30
+ }
31
+ }
32
+ }
33
+ return val;
34
+ }, z.array(z.object({
14
35
  oldText: z
15
36
  .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."),
37
+ .describe("Text to search for - must match exactly. The oldText must uniquely identify the location - include enough surrounding context (e.g., 3+ lines or function/class names) to ensure only ONE match exists in the file. " +
38
+ "Special characters require JSON escaping: backticks (`\\``...\\``), quotes, backslashes. " +
39
+ "For multi-line content, include exact newlines and indentation.")
40
+ .min(1, "oldText must be at least 1 character"),
19
41
  newText: z.string().describe("Text to replace with"),
20
- })),
42
+ })))
43
+ .describe("The edits to make to the file."),
21
44
  });
22
- export const createEditFileTool = async ({ workingDir, allowedDirs, }) => {
23
- const allowedDirectory = allowedDirs ?? [workingDir];
45
+ export const createEditFileTool = async (options) => {
46
+ const { primaryDir, allowedDirs } = options.workspace;
47
+ const allowedDirectory = allowedDirs ?? [primaryDir];
48
+ // Cache config at tool creation time instead of on every execute
49
+ const projectConfig = await config.getConfig();
24
50
  return {
25
51
  toolDef: {
26
- description: "Make line-based edits to a text file. Each edit replaces exact line sequences " +
27
- "with new content. Exact literal matching is used: no whitespace, indentation, escape, or newline normalization is applied when locating matches. " +
28
- "Provide enough context so the match is unique; otherwise the operation errors. Returns a git-style diff showing the changes made. " +
29
- "Only works within allowed directories. " +
30
- "Note: Special characters in oldText must be properly escaped for JSON (e.g., backticks as \\`...\\`). " +
31
- "Multi-line strings require exact character-by-character matching including whitespace.",
52
+ description: "Edit text in files using literal search-and-replace.",
32
53
  inputSchema,
33
54
  },
34
55
  display({ path, edits }) {
35
- return `\n> ${style.cyan(path)} (${edits.length} edit${edits.length === 1 ? "" : "s"})`;
56
+ const displayPath = toDisplayPath(path);
57
+ return `${style.cyan(displayPath)} (${edits.length} edit${edits.length === 1 ? "" : "s"})`;
36
58
  },
37
59
  async execute({ path, edits }, { abortSignal }) {
38
60
  if (abortSignal?.aborted) {
39
61
  throw new Error("File editing aborted");
40
62
  }
41
- const validPath = await validatePath(joinWorkingDir(path, workingDir), allowedDirectory, { abortSignal });
42
- const projectConfig = await config.getConfig();
43
- validateFileNotReadOnly(validPath, projectConfig, workingDir);
63
+ // Check for excessive edits and return helpful message to the model
64
+ if (edits.length > MAX_EDITS_PER_CALL) {
65
+ throw new Error(`Too many edits (${edits.length}). Maximum ${MAX_EDITS_PER_CALL} edits per call. ` +
66
+ "Please split your changes into multiple tool calls. " +
67
+ "For example, if you need to make 20 edits, make 2 calls with 10 edits each.");
68
+ }
69
+ const validPath = await validatePath(joinWorkingDir(path, primaryDir), allowedDirectory, { abortSignal });
70
+ validateFileNotReadOnly(validPath, projectConfig, primaryDir);
44
71
  const result = await applyFileEdits(validPath, edits, false, abortSignal);
45
72
  clearProjectStatusCache();
46
73
  return result;
@@ -48,93 +75,273 @@ export const createEditFileTool = async ({ workingDir, allowedDirs, }) => {
48
75
  };
49
76
  };
50
77
  // file editing and diffing utilities
78
+ /** Detect the line ending used in the content */
79
+ function detectLineEnding(content) {
80
+ const crlfIdx = content.indexOf("\r\n");
81
+ const lfIdx = content.indexOf("\n");
82
+ // If no line endings at all, default to LF
83
+ if (crlfIdx === -1 && lfIdx === -1)
84
+ return "\n";
85
+ // If CRLF exists (and either no LF or CRLF comes first), return CRLF
86
+ if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx < lfIdx)) {
87
+ return "\r\n";
88
+ }
89
+ return "\n";
90
+ }
91
+ /** Normalize line endings to LF for internal processing */
51
92
  function normalizeLineEndings(text) {
52
- return text.replace(/\r\n/g, "\n");
93
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
94
+ }
95
+ /** Restore original line endings after processing */
96
+ function restoreLineEndings(text, ending) {
97
+ return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
53
98
  }
54
- function createUnifiedDiff(originalContent, newContent, filepath = "file") {
55
- // Ensure consistent line endings for diff
56
- const normalizedOriginal = normalizeLineEndings(originalContent);
57
- const normalizedNew = normalizeLineEndings(newContent);
99
+ /** Strip UTF-8 BOM if present - users won't include invisible BOM in oldText */
100
+ function stripBom(content) {
101
+ return content.startsWith("\uFEFF")
102
+ ? { bom: "\uFEFF", text: content.slice(1) }
103
+ : { bom: "", text: content };
104
+ }
105
+ function createUnifiedDiff(normalizedOriginal, normalizedNew, filepath = "file") {
58
106
  return createTwoFilesPatch(filepath, filepath, normalizedOriginal, normalizedNew, "original", "modified");
59
107
  }
60
- export async function applyFileEdits(filePath, edits, dryRun = false, abortSignal) {
61
- if (abortSignal?.aborted) {
62
- throw new Error("File edit operation aborted");
108
+ async function validateFileReadable(filePath) {
109
+ try {
110
+ await access(filePath, constants.R_OK);
63
111
  }
64
- // Read file content literally with signal
65
- const originalContent = await readFile(filePath, {
66
- encoding: "utf-8",
67
- signal: abortSignal,
68
- });
69
- if (edits.find((edit) => edit.oldText.length === 0)) {
112
+ catch {
113
+ throw new Error(`File not found or not readable: ${filePath}`);
114
+ }
115
+ }
116
+ function validateEdits(edits) {
117
+ if (edits.some((edit) => edit.oldText.length === 0)) {
70
118
  throw new Error("Invalid oldText in edit. The value of oldText must be at least one character");
71
119
  }
72
- // Apply edits sequentially using literal matches (allow multiple matches)
73
- let modifiedContent = originalContent;
74
- for (const edit of edits) {
75
- if (abortSignal?.aborted) {
76
- throw new Error("File edit operation aborted during processing");
120
+ }
121
+ /**
122
+ * Normalize text for fuzzy matching by:
123
+ * - Unicode NFKC normalization (canonical compatibility decomposition)
124
+ * - Converting smart quotes to straight quotes
125
+ * - Unifying various dash characters to hyphen
126
+ * - Normalizing whitespace characters to regular space
127
+ * - Removing trailing whitespace from each line
128
+ */
129
+ function normalizeForFuzzyMatch(text) {
130
+ return text
131
+ .normalize("NFKC")
132
+ .split("\n")
133
+ .map((line) => line.trimEnd())
134
+ .join("\n")
135
+ .replace(/[\u2018\u2019\u201A\u201B]/g, "'") // curly single quotes → '
136
+ .replace(/[\u201C\u201D\u201E\u201F]/g, '"') // curly double quotes → "
137
+ .replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g, "-") // dashes → -
138
+ .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "); // spaces → space
139
+ }
140
+ /**
141
+ * Find text in content, trying exact match first, then fuzzy match.
142
+ * When fuzzy match is used, positions are in the normalized content.
143
+ * IMPORTANT: When fuzzy matching is needed, the caller must work entirely
144
+ * in normalized space to avoid position mapping issues.
145
+ */
146
+ function fuzzyFindText(content, searchText) {
147
+ // Try exact match first
148
+ const exactIndex = content.indexOf(searchText);
149
+ if (exactIndex !== -1) {
150
+ return {
151
+ found: true,
152
+ index: exactIndex,
153
+ matchLength: searchText.length,
154
+ usedFuzzyMatch: false,
155
+ };
156
+ }
157
+ // Fall back to fuzzy matching
158
+ const fuzzyContent = normalizeForFuzzyMatch(content);
159
+ const fuzzySearch = normalizeForFuzzyMatch(searchText);
160
+ const fuzzyIndex = fuzzyContent.indexOf(fuzzySearch);
161
+ if (fuzzyIndex === -1) {
162
+ return { found: false, index: -1, matchLength: 0, usedFuzzyMatch: false };
163
+ }
164
+ return {
165
+ found: true,
166
+ index: fuzzyIndex,
167
+ matchLength: fuzzySearch.length,
168
+ usedFuzzyMatch: true,
169
+ };
170
+ }
171
+ /**
172
+ * Count how many times searchText appears in content (exact or fuzzy).
173
+ * Used to ensure uniqueness.
174
+ */
175
+ function countMatches(content, searchText) {
176
+ // Count exact matches first
177
+ const exactEscaped = searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
178
+ const exactRegex = new RegExp(exactEscaped, "g");
179
+ const exactMatches = content.match(exactRegex);
180
+ const exactCount = exactMatches ? exactMatches.length : 0;
181
+ if (exactCount > 0) {
182
+ return exactCount;
183
+ }
184
+ // Count fuzzy matches
185
+ const fuzzyContent = normalizeForFuzzyMatch(content);
186
+ const fuzzySearch = normalizeForFuzzyMatch(searchText);
187
+ const fuzzyEscaped = fuzzySearch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
188
+ const fuzzyRegex = new RegExp(fuzzyEscaped, "g");
189
+ const fuzzyMatches = fuzzyContent.match(fuzzyRegex);
190
+ return fuzzyMatches ? fuzzyMatches.length : 0;
191
+ }
192
+ /**
193
+ * Preflight validation: Find all edit positions, validate uniqueness and no overlaps.
194
+ * If any edit requires fuzzy matching, normalize the entire content and work in
195
+ * normalized space. This avoids position mapping issues.
196
+ */
197
+ function preflightEdits(edits, content) {
198
+ const normalizedEdits = edits.map((edit) => ({
199
+ oldText: normalizeLineEndings(edit.oldText),
200
+ newText: normalizeLineEndings(edit.newText),
201
+ }));
202
+ // Check if any edit requires fuzzy matching
203
+ const needsFuzzyMatching = normalizedEdits.some((edit) => content.indexOf(edit.oldText) === -1);
204
+ // Use normalized content if fuzzy matching is needed
205
+ const baseContent = needsFuzzyMatching
206
+ ? normalizeForFuzzyMatch(content)
207
+ : content;
208
+ const matchedEdits = [];
209
+ // First pass: Find all match positions
210
+ for (let i = 0; i < normalizedEdits.length; i++) {
211
+ const edit = normalizedEdits[i];
212
+ // Check uniqueness
213
+ const matchCount = countMatches(baseContent, edit.oldText);
214
+ if (matchCount === 0) {
215
+ return {
216
+ success: false,
217
+ matchedEdits: [],
218
+ errorMessage: `Edit ${i + 1}: Could not find the exact text. ` +
219
+ "The oldText must match exactly including all whitespace and newlines.",
220
+ usedFuzzyMatch: needsFuzzyMatching,
221
+ baseContent,
222
+ };
77
223
  }
78
- const result = await applyEditWithLlmFix(edit, modifiedContent);
79
- if (result.success) {
80
- modifiedContent = result.content;
224
+ if (matchCount > 1) {
225
+ const fuzzyContext = needsFuzzyMatching
226
+ ? " (including fuzzy matches)"
227
+ : "";
228
+ return {
229
+ success: false,
230
+ matchedEdits: [],
231
+ errorMessage: `Edit ${i + 1}: oldText matches ${matchCount} locations${fuzzyContext} but should match only 1. ` +
232
+ "Please provide a more specific oldText that includes more surrounding context.",
233
+ usedFuzzyMatch: needsFuzzyMatching,
234
+ baseContent,
235
+ };
81
236
  }
82
- else {
83
- throw new Error("oldText not found in content");
237
+ // Find the match position
238
+ const matchResult = fuzzyFindText(baseContent, edit.oldText);
239
+ if (!matchResult.found) {
240
+ return {
241
+ success: false,
242
+ matchedEdits: [],
243
+ errorMessage: `Edit ${i + 1}: Could not find the text (unexpected error).`,
244
+ usedFuzzyMatch: needsFuzzyMatching,
245
+ baseContent,
246
+ };
247
+ }
248
+ matchedEdits.push({
249
+ ...edit,
250
+ index: matchResult.index,
251
+ matchLength: matchResult.matchLength,
252
+ editIndex: i,
253
+ });
254
+ }
255
+ // Sort by position (ascending) for overlap detection
256
+ matchedEdits.sort((a, b) => a.index - b.index);
257
+ // Check for overlapping edits
258
+ for (let i = 0; i < matchedEdits.length - 1; i++) {
259
+ const current = matchedEdits[i];
260
+ const next = matchedEdits[i + 1];
261
+ // Check if current edit overlaps with next edit
262
+ if (current.index + current.matchLength > next.index) {
263
+ return {
264
+ success: false,
265
+ matchedEdits: [],
266
+ errorMessage: `Edits ${current.editIndex + 1} and ${next.editIndex + 1} overlap in the file. ` +
267
+ "Each edit must target a distinct region. Please combine overlapping edits into a single edit.",
268
+ usedFuzzyMatch: needsFuzzyMatching,
269
+ baseContent,
270
+ };
84
271
  }
85
272
  }
86
- // Create unified diff (createUnifiedDiff normalizes line endings internally for diffing)
87
- const diff = createUnifiedDiff(originalContent, modifiedContent, filePath);
88
- // Format diff with appropriate number of backticks
273
+ return {
274
+ success: true,
275
+ matchedEdits,
276
+ usedFuzzyMatch: needsFuzzyMatching,
277
+ baseContent,
278
+ };
279
+ }
280
+ /**
281
+ * Apply edits in reverse position order (highest index first).
282
+ * This prevents position shifting - earlier edits don't affect later ones.
283
+ * All positions are relative to baseContent (normalized if fuzzy matching).
284
+ */
285
+ function applyEditsReverseOrder(content, matchedEdits) {
286
+ let result = content;
287
+ // Process in reverse order (highest index first)
288
+ for (let i = matchedEdits.length - 1; i >= 0; i--) {
289
+ const edit = matchedEdits[i];
290
+ const before = result.slice(0, edit.index);
291
+ const after = result.slice(edit.index + edit.matchLength);
292
+ result = before + edit.newText + after;
293
+ }
294
+ return result;
295
+ }
296
+ function formatDiff(diff, _filePath) {
89
297
  let numBackticks = 3;
90
298
  while (diff.includes("`".repeat(numBackticks))) {
91
299
  numBackticks++;
92
300
  }
93
- const formattedDiff = `${"`".repeat(numBackticks)} diff\n${diff}\n${"`".repeat(numBackticks)}`;
301
+ return `${"`".repeat(numBackticks)} diff\n${diff}\n${"`".repeat(numBackticks)}`;
302
+ }
303
+ export async function applyFileEdits(filePath, edits, dryRun = false, abortSignal) {
304
+ if (abortSignal?.aborted) {
305
+ throw new Error("File edit operation aborted");
306
+ }
307
+ await validateFileReadable(filePath);
308
+ const rawContent = await readFile(filePath, {
309
+ encoding: "utf-8",
310
+ signal: abortSignal,
311
+ });
312
+ const { bom: originalBom, text: bomStrippedContent } = stripBom(rawContent);
313
+ const originalLineEnding = detectLineEnding(bomStrippedContent);
314
+ const content = normalizeLineEndings(bomStrippedContent);
315
+ validateEdits(edits);
316
+ // PREFLIGHT: Find all positions, validate no overlaps
317
+ const preflight = preflightEdits(edits, content);
318
+ if (!preflight.success) {
319
+ throw new Error(`Edit validation failed: ${preflight.errorMessage}`);
320
+ }
321
+ // All edits validated - apply in reverse order
322
+ // Note: baseContent is normalized if fuzzy matching was needed
323
+ const modifiedContent = applyEditsReverseOrder(preflight.baseContent, preflight.matchedEdits);
324
+ // Verify something actually changed
325
+ if (modifiedContent === preflight.baseContent) {
326
+ throw new Error("No changes were made - all edits resulted in identical content");
327
+ }
328
+ const finalContentWithLineEndings = restoreLineEndings(modifiedContent, originalLineEnding);
329
+ const finalContent = originalBom + finalContentWithLineEndings;
330
+ // Use baseContent for diff (normalized if fuzzy matching)
331
+ const diff = createUnifiedDiff(preflight.baseContent, modifiedContent, filePath);
332
+ const formattedDiff = formatDiff(diff, filePath);
333
+ // Add fuzzy match indicator if applicable
334
+ const result = preflight.usedFuzzyMatch
335
+ ? `${formattedDiff}\n\n(Note: Used fuzzy matching - file content has been normalized)`
336
+ : formattedDiff;
94
337
  if (!dryRun) {
95
338
  if (abortSignal?.aborted) {
96
339
  throw new Error("File edit operation aborted before writing");
97
340
  }
98
- // Write the modified content with signal
99
- await writeFile(filePath, modifiedContent, {
341
+ await writeFile(filePath, finalContent, {
100
342
  encoding: "utf-8",
101
343
  signal: abortSignal,
102
344
  });
103
345
  }
104
- return formattedDiff;
105
- }
106
- /**
107
- * Applies a single edit
108
- */
109
- async function applyEditWithLlmFix(edit, content) {
110
- const { oldText, newText } = edit;
111
- // Try the original edit first
112
- const originalResult = applyLiteralEdit(content, oldText, newText);
113
- if (originalResult.matchCount > 0) {
114
- return { success: true, content: originalResult.content };
115
- }
116
- return { success: false, content };
117
- }
118
- /**
119
- * Applies a literal search and replace operation
120
- */
121
- function applyLiteralEdit(content, search, replace) {
122
- let modifiedContent = content;
123
- let matchCount = 0;
124
- let currentIndex = 0;
125
- while (currentIndex < modifiedContent.length) {
126
- const matchIndex = modifiedContent.indexOf(search, currentIndex);
127
- if (matchIndex === -1) {
128
- break;
129
- }
130
- matchCount++;
131
- // Apply the replacement
132
- modifiedContent =
133
- modifiedContent.slice(0, matchIndex) +
134
- replace +
135
- modifiedContent.slice(matchIndex + search.length);
136
- // Move current index past the replacement
137
- currentIndex = matchIndex + replace.length;
138
- }
139
- return { matchCount, content: modifiedContent };
346
+ return result;
140
347
  }
@@ -4,12 +4,12 @@ export declare const GlobTool: {
4
4
  name: "Glob";
5
5
  };
6
6
  export declare const inputSchema: z.ZodObject<{
7
- patterns: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
8
- path: z.ZodString;
7
+ patterns: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
8
+ path: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodString>;
9
9
  gitignore: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
10
10
  recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
11
11
  expandDirectories: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
12
- ignoreFiles: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
12
+ ignoreFiles: z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
13
13
  cwd: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
14
14
  maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
15
15
  }, z.core.$strip>;
@@ -18,12 +18,12 @@ export declare const createGlobTool: () => {
18
18
  toolDef: {
19
19
  description: string;
20
20
  inputSchema: z.ZodObject<{
21
- patterns: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
22
- path: z.ZodString;
21
+ patterns: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
22
+ path: z.ZodPipe<z.ZodTransform<{}, unknown>, z.ZodString>;
23
23
  gitignore: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
24
24
  recursive: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
25
25
  expandDirectories: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedBoolean<unknown>>>;
26
- ignoreFiles: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
26
+ ignoreFiles: z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
27
27
  cwd: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedString<unknown>>>;
28
28
  maxResults: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
29
29
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../source/tools/glob.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;iBA4BtB,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;gCAMK,eAAe;wGAkBtC,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CA4ErB,CAAC"}
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../source/tools/glob.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AA6EvD,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;iBA0EtB,CAAC;AAEH,KAAK,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;gCAMK,eAAe;wGAmBtC,eAAe,mBACD,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;CAuCrB,CAAC"}