@kolisachint/hoocode-agent 0.4.162 → 0.4.164

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 (339) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cli/args.d.ts +0 -6
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +0 -31
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/config.d.ts +0 -40
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +4 -50
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-frontmatter.d.ts +0 -28
  11. package/dist/core/agent-frontmatter.d.ts.map +1 -1
  12. package/dist/core/agent-frontmatter.js +0 -6
  13. package/dist/core/agent-frontmatter.js.map +1 -1
  14. package/dist/core/agent-session-services.d.ts +0 -2
  15. package/dist/core/agent-session-services.d.ts.map +1 -1
  16. package/dist/core/agent-session-services.js +0 -2
  17. package/dist/core/agent-session-services.js.map +1 -1
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +0 -2
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/auth-guidance.d.ts +0 -1
  22. package/dist/core/auth-guidance.d.ts.map +1 -1
  23. package/dist/core/auth-guidance.js +1 -1
  24. package/dist/core/auth-guidance.js.map +1 -1
  25. package/dist/core/embsearch/chunker.d.ts +0 -2
  26. package/dist/core/embsearch/chunker.d.ts.map +1 -1
  27. package/dist/core/embsearch/chunker.js +1 -1
  28. package/dist/core/embsearch/chunker.js.map +1 -1
  29. package/dist/core/embsearch/index-meta.d.ts +0 -2
  30. package/dist/core/embsearch/index-meta.d.ts.map +1 -1
  31. package/dist/core/embsearch/index-meta.js +0 -6
  32. package/dist/core/embsearch/index-meta.js.map +1 -1
  33. package/dist/core/export-html/ansi-to-html.d.ts +0 -4
  34. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  35. package/dist/core/export-html/ansi-to-html.js +1 -1
  36. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  37. package/dist/core/extensions/index.d.ts +3 -3
  38. package/dist/core/extensions/index.d.ts.map +1 -1
  39. package/dist/core/extensions/index.js +1 -1
  40. package/dist/core/extensions/index.js.map +1 -1
  41. package/dist/core/extensions/plugins/authoring.d.ts +2 -2
  42. package/dist/core/extensions/plugins/authoring.d.ts.map +1 -1
  43. package/dist/core/extensions/plugins/authoring.js +0 -0
  44. package/dist/core/extensions/plugins/authoring.js.map +1 -1
  45. package/dist/core/extensions/plugins/formats/index.d.ts +2 -6
  46. package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -1
  47. package/dist/core/extensions/plugins/formats/index.js +2 -3
  48. package/dist/core/extensions/plugins/formats/index.js.map +1 -1
  49. package/dist/core/extensions/plugins/formats/shared.d.ts +0 -2
  50. package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -1
  51. package/dist/core/extensions/plugins/formats/shared.js +1 -1
  52. package/dist/core/extensions/plugins/formats/shared.js.map +1 -1
  53. package/dist/core/extensions/plugins/formats/types.d.ts +4 -3
  54. package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -1
  55. package/dist/core/extensions/plugins/formats/types.js.map +1 -1
  56. package/dist/core/extensions/plugins/install.d.ts +0 -2
  57. package/dist/core/extensions/plugins/install.d.ts.map +1 -1
  58. package/dist/core/extensions/plugins/install.js +1 -1
  59. package/dist/core/extensions/plugins/install.js.map +1 -1
  60. package/dist/core/extensions/plugins/manifest.d.ts +0 -6
  61. package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
  62. package/dist/core/extensions/plugins/manifest.js +0 -6
  63. package/dist/core/extensions/plugins/manifest.js.map +1 -1
  64. package/dist/core/extensions/plugins/marketplace.d.ts +3 -3
  65. package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
  66. package/dist/core/extensions/plugins/marketplace.js.map +1 -1
  67. package/dist/core/extensions/types.d.ts +28 -28
  68. package/dist/core/extensions/types.d.ts.map +1 -1
  69. package/dist/core/extensions/types.js.map +1 -1
  70. package/dist/core/keybindings.d.ts +3 -3
  71. package/dist/core/keybindings.d.ts.map +1 -1
  72. package/dist/core/keybindings.js.map +1 -1
  73. package/dist/core/model-resolver.d.ts +0 -7
  74. package/dist/core/model-resolver.d.ts.map +1 -1
  75. package/dist/core/model-resolver.js +0 -56
  76. package/dist/core/model-resolver.js.map +1 -1
  77. package/dist/core/package-resource-discovery.d.ts +0 -10
  78. package/dist/core/package-resource-discovery.d.ts.map +1 -1
  79. package/dist/core/package-resource-discovery.js +7 -7
  80. package/dist/core/package-resource-discovery.js.map +1 -1
  81. package/dist/core/provider-health.d.ts +0 -2
  82. package/dist/core/provider-health.d.ts.map +1 -1
  83. package/dist/core/provider-health.js +1 -1
  84. package/dist/core/provider-health.js.map +1 -1
  85. package/dist/core/resolve-config-value.d.ts +0 -4
  86. package/dist/core/resolve-config-value.d.ts.map +1 -1
  87. package/dist/core/resolve-config-value.js +0 -15
  88. package/dist/core/resolve-config-value.js.map +1 -1
  89. package/dist/core/sdk.d.ts +3 -23
  90. package/dist/core/sdk.d.ts.map +1 -1
  91. package/dist/core/sdk.js +3 -5
  92. package/dist/core/sdk.js.map +1 -1
  93. package/dist/core/search/eval.d.ts +0 -3
  94. package/dist/core/search/eval.d.ts.map +1 -1
  95. package/dist/core/search/eval.js +1 -1
  96. package/dist/core/search/eval.js.map +1 -1
  97. package/dist/core/search/types.d.ts +2 -1
  98. package/dist/core/search/types.d.ts.map +1 -1
  99. package/dist/core/search/types.js.map +1 -1
  100. package/dist/core/session-cwd.d.ts +0 -1
  101. package/dist/core/session-cwd.d.ts.map +1 -1
  102. package/dist/core/session-cwd.js +1 -1
  103. package/dist/core/session-cwd.js.map +1 -1
  104. package/dist/core/settings-defaults.d.ts +0 -3
  105. package/dist/core/settings-defaults.d.ts.map +1 -1
  106. package/dist/core/settings-defaults.js +0 -3
  107. package/dist/core/settings-defaults.js.map +1 -1
  108. package/dist/core/settings-manager.d.ts +2 -9
  109. package/dist/core/settings-manager.d.ts.map +1 -1
  110. package/dist/core/settings-manager.js +0 -25
  111. package/dist/core/settings-manager.js.map +1 -1
  112. package/dist/core/settings-types.d.ts +9 -11
  113. package/dist/core/settings-types.d.ts.map +1 -1
  114. package/dist/core/settings-types.js.map +1 -1
  115. package/dist/core/subagent-inbox.d.ts +1 -3
  116. package/dist/core/subagent-inbox.d.ts.map +1 -1
  117. package/dist/core/subagent-inbox.js +1 -1
  118. package/dist/core/subagent-inbox.js.map +1 -1
  119. package/dist/core/subagent-pool.d.ts +0 -13
  120. package/dist/core/subagent-pool.d.ts.map +1 -1
  121. package/dist/core/subagent-pool.js +2 -2
  122. package/dist/core/subagent-pool.js.map +1 -1
  123. package/dist/core/task-store.d.ts +3 -3
  124. package/dist/core/task-store.d.ts.map +1 -1
  125. package/dist/core/task-store.js.map +1 -1
  126. package/dist/core/team-auto.d.ts +0 -2
  127. package/dist/core/team-auto.d.ts.map +1 -1
  128. package/dist/core/team-auto.js +1 -1
  129. package/dist/core/team-auto.js.map +1 -1
  130. package/dist/core/token-budget.d.ts +0 -13
  131. package/dist/core/token-budget.d.ts.map +1 -1
  132. package/dist/core/token-budget.js.map +1 -1
  133. package/dist/core/tools/bash.d.ts.map +1 -1
  134. package/dist/core/tools/bash.js +3 -1
  135. package/dist/core/tools/bash.js.map +1 -1
  136. package/dist/core/tools/edit-diff.d.ts +0 -38
  137. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  138. package/dist/core/tools/edit-diff.js +2 -9
  139. package/dist/core/tools/edit-diff.js.map +1 -1
  140. package/dist/core/tools/edit.d.ts.map +1 -1
  141. package/dist/core/tools/edit.js +5 -5
  142. package/dist/core/tools/edit.js.map +1 -1
  143. package/dist/core/tools/find.d.ts.map +1 -1
  144. package/dist/core/tools/find.js +8 -10
  145. package/dist/core/tools/find.js.map +1 -1
  146. package/dist/core/tools/grep.d.ts.map +1 -1
  147. package/dist/core/tools/grep.js +4 -4
  148. package/dist/core/tools/grep.js.map +1 -1
  149. package/dist/core/tools/index.d.ts +0 -24
  150. package/dist/core/tools/index.d.ts.map +1 -1
  151. package/dist/core/tools/index.js +0 -20
  152. package/dist/core/tools/index.js.map +1 -1
  153. package/dist/core/tools/ls.d.ts.map +1 -1
  154. package/dist/core/tools/ls.js +2 -2
  155. package/dist/core/tools/ls.js.map +1 -1
  156. package/dist/core/tools/native-search.d.ts +3 -2
  157. package/dist/core/tools/native-search.d.ts.map +1 -1
  158. package/dist/core/tools/native-search.js.map +1 -1
  159. package/dist/core/tools/path-utils.d.ts +0 -10
  160. package/dist/core/tools/path-utils.d.ts.map +1 -1
  161. package/dist/core/tools/path-utils.js +2 -2
  162. package/dist/core/tools/path-utils.js.map +1 -1
  163. package/dist/core/tools/plugins.d.ts +1 -20
  164. package/dist/core/tools/plugins.d.ts.map +1 -1
  165. package/dist/core/tools/plugins.js +2 -2
  166. package/dist/core/tools/plugins.js.map +1 -1
  167. package/dist/core/tools/propose-plugin.d.ts +0 -12
  168. package/dist/core/tools/propose-plugin.d.ts.map +1 -1
  169. package/dist/core/tools/propose-plugin.js +0 -1
  170. package/dist/core/tools/propose-plugin.js.map +1 -1
  171. package/dist/core/tools/read-dedup.d.ts +0 -2
  172. package/dist/core/tools/read-dedup.d.ts.map +1 -1
  173. package/dist/core/tools/read-dedup.js +1 -1
  174. package/dist/core/tools/read-dedup.js.map +1 -1
  175. package/dist/core/tools/read.d.ts.map +1 -1
  176. package/dist/core/tools/read.js +10 -8
  177. package/dist/core/tools/read.js.map +1 -1
  178. package/dist/core/tools/render-utils.d.ts +0 -5
  179. package/dist/core/tools/render-utils.d.ts.map +1 -1
  180. package/dist/core/tools/render-utils.js.map +1 -1
  181. package/dist/core/tools/search.d.ts +1 -4
  182. package/dist/core/tools/search.d.ts.map +1 -1
  183. package/dist/core/tools/search.js +6 -6
  184. package/dist/core/tools/search.js.map +1 -1
  185. package/dist/core/tools/webfetch.d.ts +1 -2
  186. package/dist/core/tools/webfetch.d.ts.map +1 -1
  187. package/dist/core/tools/webfetch.js.map +1 -1
  188. package/dist/core/tools/websearch.d.ts +1 -2
  189. package/dist/core/tools/websearch.d.ts.map +1 -1
  190. package/dist/core/tools/websearch.js.map +1 -1
  191. package/dist/core/tools/webtools-shared.d.ts +3 -5
  192. package/dist/core/tools/webtools-shared.d.ts.map +1 -1
  193. package/dist/core/tools/webtools-shared.js +1 -1
  194. package/dist/core/tools/webtools-shared.js.map +1 -1
  195. package/dist/core/warm-subagent-pool-instance.d.ts +0 -4
  196. package/dist/core/warm-subagent-pool-instance.d.ts.map +1 -1
  197. package/dist/core/warm-subagent-pool-instance.js +0 -10
  198. package/dist/core/warm-subagent-pool-instance.js.map +1 -1
  199. package/dist/core/wordmark.d.ts +1 -9
  200. package/dist/core/wordmark.d.ts.map +1 -1
  201. package/dist/core/wordmark.js +2 -26
  202. package/dist/core/wordmark.js.map +1 -1
  203. package/dist/extensions/core/config.d.ts +4 -3
  204. package/dist/extensions/core/config.d.ts.map +1 -1
  205. package/dist/extensions/core/config.js.map +1 -1
  206. package/dist/extensions/core/mcp-loader.d.ts +0 -18
  207. package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
  208. package/dist/extensions/core/mcp-loader.js.map +1 -1
  209. package/dist/extensions/core/prompt-reactive/policy.d.ts +0 -8
  210. package/dist/extensions/core/prompt-reactive/policy.d.ts.map +1 -1
  211. package/dist/extensions/core/prompt-reactive/policy.js +1 -1
  212. package/dist/extensions/core/prompt-reactive/policy.js.map +1 -1
  213. package/dist/main.d.ts.map +1 -1
  214. package/dist/main.js +0 -30
  215. package/dist/main.js.map +1 -1
  216. package/dist/migrations.d.ts +0 -16
  217. package/dist/migrations.d.ts.map +1 -1
  218. package/dist/migrations.js +2 -2
  219. package/dist/migrations.js.map +1 -1
  220. package/dist/modes/interactive/brand.d.ts +0 -4
  221. package/dist/modes/interactive/brand.d.ts.map +1 -1
  222. package/dist/modes/interactive/brand.js +0 -4
  223. package/dist/modes/interactive/brand.js.map +1 -1
  224. package/dist/modes/interactive/components/index.d.ts +0 -4
  225. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  226. package/dist/modes/interactive/components/index.js +0 -5
  227. package/dist/modes/interactive/components/index.js.map +1 -1
  228. package/dist/modes/interactive/components/read-output.d.ts +0 -9
  229. package/dist/modes/interactive/components/read-output.d.ts.map +1 -1
  230. package/dist/modes/interactive/components/read-output.js +0 -20
  231. package/dist/modes/interactive/components/read-output.js.map +1 -1
  232. package/dist/modes/interactive/components/session-selector-search.d.ts +0 -17
  233. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/session-selector-search.js +2 -2
  235. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  236. package/dist/modes/interactive/components/settings-selector.d.ts +5 -14
  237. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  238. package/dist/modes/interactive/components/settings-selector.js +2 -2
  239. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  240. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  241. package/dist/modes/interactive/interactive-mode.js +0 -18
  242. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  243. package/dist/modes/interactive/login-controller.d.ts +1 -1
  244. package/dist/modes/interactive/login-controller.d.ts.map +1 -1
  245. package/dist/modes/interactive/login-controller.js +1 -1
  246. package/dist/modes/interactive/login-controller.js.map +1 -1
  247. package/dist/modes/interactive/resource-display.d.ts +0 -20
  248. package/dist/modes/interactive/resource-display.d.ts.map +1 -1
  249. package/dist/modes/interactive/resource-display.js +7 -7
  250. package/dist/modes/interactive/resource-display.js.map +1 -1
  251. package/dist/modes/interactive/theme/theme.d.ts +0 -4
  252. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  253. package/dist/modes/interactive/theme/theme.js +0 -7
  254. package/dist/modes/interactive/theme/theme.js.map +1 -1
  255. package/dist/modes/rpc/rpc-mode.d.ts +0 -1
  256. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  257. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  258. package/dist/modes/rpc/rpc-types.d.ts +0 -1
  259. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  260. package/dist/modes/rpc/rpc-types.js +3 -0
  261. package/dist/modes/rpc/rpc-types.js.map +1 -1
  262. package/dist/package-manager-cli.d.ts +0 -1
  263. package/dist/package-manager-cli.d.ts.map +1 -1
  264. package/dist/package-manager-cli.js.map +1 -1
  265. package/dist/utils/changelog.d.ts +0 -4
  266. package/dist/utils/changelog.d.ts.map +1 -1
  267. package/dist/utils/changelog.js +1 -1
  268. package/dist/utils/changelog.js.map +1 -1
  269. package/dist/utils/clipboard-native.d.ts +1 -1
  270. package/dist/utils/clipboard-native.d.ts.map +1 -1
  271. package/dist/utils/clipboard-native.js.map +1 -1
  272. package/dist/utils/paths.d.ts +0 -2
  273. package/dist/utils/paths.d.ts.map +1 -1
  274. package/dist/utils/paths.js +1 -1
  275. package/dist/utils/paths.js.map +1 -1
  276. package/dist/utils/plan/parser.d.ts +5 -4
  277. package/dist/utils/plan/parser.d.ts.map +1 -1
  278. package/dist/utils/plan/parser.js.map +1 -1
  279. package/dist/utils/tools-manager.d.ts +1 -2
  280. package/dist/utils/tools-manager.d.ts.map +1 -1
  281. package/dist/utils/tools-manager.js +2 -50
  282. package/dist/utils/tools-manager.js.map +1 -1
  283. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  284. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  285. package/examples/extensions/sandbox/package.json +1 -1
  286. package/examples/extensions/with-deps/package.json +1 -1
  287. package/package.json +4 -7
  288. package/dist/core/index.d.ts +0 -12
  289. package/dist/core/index.d.ts.map +0 -1
  290. package/dist/core/index.js +0 -12
  291. package/dist/core/index.js.map +0 -1
  292. package/dist/core/tools/browser/browser-continue.d.ts +0 -25
  293. package/dist/core/tools/browser/browser-continue.d.ts.map +0 -1
  294. package/dist/core/tools/browser/browser-continue.js +0 -84
  295. package/dist/core/tools/browser/browser-continue.js.map +0 -1
  296. package/dist/core/tools/browser/browser-run.d.ts +0 -48
  297. package/dist/core/tools/browser/browser-run.d.ts.map +0 -1
  298. package/dist/core/tools/browser/browser-run.js +0 -377
  299. package/dist/core/tools/browser/browser-run.js.map +0 -1
  300. package/dist/core/tools/browser/browsertools-shared.d.ts +0 -190
  301. package/dist/core/tools/browser/browsertools-shared.d.ts.map +0 -1
  302. package/dist/core/tools/browser/browsertools-shared.js +0 -335
  303. package/dist/core/tools/browser/browsertools-shared.js.map +0 -1
  304. package/dist/core/tools/browser/index.d.ts +0 -8
  305. package/dist/core/tools/browser/index.d.ts.map +0 -1
  306. package/dist/core/tools/browser/index.js +0 -8
  307. package/dist/core/tools/browser/index.js.map +0 -1
  308. package/dist/core/tools/doc/docedit.d.ts +0 -44
  309. package/dist/core/tools/doc/docedit.d.ts.map +0 -1
  310. package/dist/core/tools/doc/docedit.js +0 -133
  311. package/dist/core/tools/doc/docedit.js.map +0 -1
  312. package/dist/core/tools/doc/docgrep.d.ts +0 -28
  313. package/dist/core/tools/doc/docgrep.d.ts.map +0 -1
  314. package/dist/core/tools/doc/docgrep.js +0 -106
  315. package/dist/core/tools/doc/docgrep.js.map +0 -1
  316. package/dist/core/tools/doc/docpeek.d.ts +0 -25
  317. package/dist/core/tools/doc/docpeek.d.ts.map +0 -1
  318. package/dist/core/tools/doc/docpeek.js +0 -112
  319. package/dist/core/tools/doc/docpeek.js.map +0 -1
  320. package/dist/core/tools/doc/docread.d.ts +0 -32
  321. package/dist/core/tools/doc/docread.d.ts.map +0 -1
  322. package/dist/core/tools/doc/docread.js +0 -113
  323. package/dist/core/tools/doc/docread.js.map +0 -1
  324. package/dist/core/tools/doc/docscan.d.ts +0 -29
  325. package/dist/core/tools/doc/docscan.d.ts.map +0 -1
  326. package/dist/core/tools/doc/docscan.js +0 -110
  327. package/dist/core/tools/doc/docscan.js.map +0 -1
  328. package/dist/core/tools/doc/docwrite.d.ts +0 -44
  329. package/dist/core/tools/doc/docwrite.d.ts.map +0 -1
  330. package/dist/core/tools/doc/docwrite.js +0 -102
  331. package/dist/core/tools/doc/docwrite.js.map +0 -1
  332. package/dist/core/tools/doc/index.d.ts +0 -11
  333. package/dist/core/tools/doc/index.d.ts.map +0 -1
  334. package/dist/core/tools/doc/index.js +0 -11
  335. package/dist/core/tools/doc/index.js.map +0 -1
  336. package/dist/core/tools/filetools-shared.d.ts +0 -271
  337. package/dist/core/tools/filetools-shared.d.ts.map +0 -1
  338. package/dist/core/tools/filetools-shared.js +0 -403
  339. package/dist/core/tools/filetools-shared.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,GAAG,EAA2B,MAAM,0BAA0B,CAAC;AAGxE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,cAAc,EAKnB,MAAM,gBAAgB,CAAC;AASxB,KAAK,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACtB,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAmBF,QAAA,MAAM,UAAU;;;;;;;EASf,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAMtD,MAAM,WAAW,eAAe;IAC/B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,qCAAqC;IACrC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,4DAA4D;IAC5D,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAgDD,KAAK,uBAAuB,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAmJF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CAwMjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Box, Container, Spacer, Text } from \"@kolisachint/hoocode-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n/** Maximum characters for diff output sent to the LLM. */\nconst MAX_DIFF_CHARS = 4000;\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file (unless replaceAll is true) and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t\treplaceAll: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription:\n\t\t\t\t\t\"When true, replace every occurrence of oldText instead of requiring it to be unique. Use for renaming a symbol/string throughout the file. Default false.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\nexport interface EditToolDetails {\n\t/** Unified diff of the changes made */\n\tdiff: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) args.edits = parsed;\n\t\t} catch {}\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(\"Edit tool input is invalid. edits must contain at least one replacement.\");\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 0, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst invalidArg = invalidArgText(theme);\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file, unless that edit sets replaceAll: true to replace all of its occurrences. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet:\n\t\t\t\"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use edit for precise changes (edits[].oldText must match exactly)\",\n\t\t\t\"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\",\n\t\t\t\"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\",\n\t\t\t\"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\",\n\t\t\t\"To rename or replace every occurrence of a string in the file, set edits[].replaceAll: true on that edit instead of requiring oldText to be unique.\",\n\t\t],\n\t\tparameters: editSchema,\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tabsolutePath,\n\t\t\t\t() =>\n\t\t\t\t\tnew Promise<{\n\t\t\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t\t\t}>((resolve, reject) => {\n\t\t\t\t\t\t// Check if already aborted.\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet aborted = false;\n\n\t\t\t\t\t\t// Set up abort handler.\n\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Perform the edit operation.\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Check if file exists.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treject(new Error(`Could not edit file: ${path}. ${errorMessage}.`));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if aborted before reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Read the file.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t\t\t// Check if aborted after reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(\n\t\t\t\t\t\t\t\t\tnormalizedContent,\n\t\t\t\t\t\t\t\t\tedits,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Check if aborted before writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\n\t\t\t\t\t\t\t\t// Check if aborted after writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\t\t\t\t\t// Truncate large diffs to save tokens\n\t\t\t\t\t\t\t\tconst diff =\n\t\t\t\t\t\t\t\t\tdiffResult.diff.length > MAX_DIFF_CHARS\n\t\t\t\t\t\t\t\t\t\t? `${diffResult.diff.slice(0, MAX_DIFF_CHARS)}\\n\\n[diff truncated for brevity]`\n\t\t\t\t\t\t\t\t\t\t: diffResult.diff;\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tdetails: { diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\t\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(callComponent, context.args as RenderableEditArgs | undefined, theme);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,GAAG,EAA2B,MAAM,0BAA0B,CAAC;AAGxE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,cAAc,EAKnB,MAAM,gBAAgB,CAAC;AASxB,KAAK,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACtB,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAmBF,QAAA,MAAM,UAAU;;;;;;;EASf,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAMtD,MAAM,WAAW,eAAe;IAC/B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,qCAAqC;IACrC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,4DAA4D;IAC5D,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAgDD,KAAK,uBAAuB,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAmJF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CAwMjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Box, Container, Spacer, Text } from \"@kolisachint/hoocode-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n/** Maximum characters for diff output sent to the LLM. */\nconst MAX_DIFF_CHARS = 4000;\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file (unless replaceAll is true) and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t\treplaceAll: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription:\n\t\t\t\t\t\"When true, replace every occurrence of oldText instead of requiring it to be unique. Use for renaming a symbol/string throughout the file. Default false.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\nexport interface EditToolDetails {\n\t/** Unified diff of the changes made */\n\tdiff: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) args.edits = parsed;\n\t\t} catch {}\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(\"Edit tool input is invalid. edits must contain at least one replacement.\");\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 0, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst invalidArg = invalidArgText(theme);\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file, unless that edit sets replaceAll: true to replace all of its occurrences. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet:\n\t\t\t\"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\t\t// Only the batching preference lives here. The matching semantics\n\t\t// (original-file matching, no overlaps, replaceAll) are already stated in the\n\t\t// `edits`/`oldText`/`replaceAll` schema descriptions, which ship on every turn\n\t\t// alongside these — repeating them spent the tokens twice.\n\t\tpromptGuidelines: [\n\t\t\t\"When changing several separate locations in one file, send one edit call with multiple entries in edits[] rather than several edit calls. Keep each oldText as small as it can be while staying unique.\",\n\t\t],\n\t\tparameters: editSchema,\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tabsolutePath,\n\t\t\t\t() =>\n\t\t\t\t\tnew Promise<{\n\t\t\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t\t\t}>((resolve, reject) => {\n\t\t\t\t\t\t// Check if already aborted.\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet aborted = false;\n\n\t\t\t\t\t\t// Set up abort handler.\n\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Perform the edit operation.\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Check if file exists.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treject(new Error(`Could not edit file: ${path}. ${errorMessage}.`));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if aborted before reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Read the file.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t\t\t// Check if aborted after reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(\n\t\t\t\t\t\t\t\t\tnormalizedContent,\n\t\t\t\t\t\t\t\t\tedits,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Check if aborted before writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\n\t\t\t\t\t\t\t\t// Check if aborted after writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\t\t\t\t\t// Truncate large diffs to save tokens\n\t\t\t\t\t\t\t\tconst diff =\n\t\t\t\t\t\t\t\t\tdiffResult.diff.length > MAX_DIFF_CHARS\n\t\t\t\t\t\t\t\t\t\t? `${diffResult.diff.slice(0, MAX_DIFF_CHARS)}\\n\\n[diff truncated for brevity]`\n\t\t\t\t\t\t\t\t\t\t: diffResult.diff;\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tdetails: { diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\t\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(callComponent, context.args as RenderableEditArgs | undefined, theme);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
@@ -170,12 +170,12 @@ export function createEditToolDefinition(cwd, options) {
170
170
  label: "edit",
171
171
  description: "Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file, unless that edit sets replaceAll: true to replace all of its occurrences. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.",
172
172
  promptSnippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
173
+ // Only the batching preference lives here. The matching semantics
174
+ // (original-file matching, no overlaps, replaceAll) are already stated in the
175
+ // `edits`/`oldText`/`replaceAll` schema descriptions, which ship on every turn
176
+ // alongside these — repeating them spent the tokens twice.
173
177
  promptGuidelines: [
174
- "Use edit for precise changes (edits[].oldText must match exactly)",
175
- "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
176
- "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
177
- "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
178
- "To rename or replace every occurrence of a string in the file, set edits[].replaceAll: true on that edit instead of requiring oldText to be unique.",
178
+ "When changing several separate locations in one file, send one edit call with multiple entries in edits[] rather than several edit calls. Keep each oldText as small as it can be while staying unique.",
179
179
  ],
180
180
  parameters: editSchema,
181
181
  renderShell: "self",
@@ -1 +1 @@
1
- {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Box, Container, Spacer, Text } from \"@kolisachint/hoocode-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n/** Maximum characters for diff output sent to the LLM. */\nconst MAX_DIFF_CHARS = 4000;\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file (unless replaceAll is true) and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t\treplaceAll: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription:\n\t\t\t\t\t\"When true, replace every occurrence of oldText instead of requiring it to be unique. Use for renaming a symbol/string throughout the file. Default false.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\nexport interface EditToolDetails {\n\t/** Unified diff of the changes made */\n\tdiff: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) args.edits = parsed;\n\t\t} catch {}\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(\"Edit tool input is invalid. edits must contain at least one replacement.\");\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 0, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst invalidArg = invalidArgText(theme);\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file, unless that edit sets replaceAll: true to replace all of its occurrences. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet:\n\t\t\t\"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use edit for precise changes (edits[].oldText must match exactly)\",\n\t\t\t\"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\",\n\t\t\t\"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\",\n\t\t\t\"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\",\n\t\t\t\"To rename or replace every occurrence of a string in the file, set edits[].replaceAll: true on that edit instead of requiring oldText to be unique.\",\n\t\t],\n\t\tparameters: editSchema,\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tabsolutePath,\n\t\t\t\t() =>\n\t\t\t\t\tnew Promise<{\n\t\t\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t\t\t}>((resolve, reject) => {\n\t\t\t\t\t\t// Check if already aborted.\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet aborted = false;\n\n\t\t\t\t\t\t// Set up abort handler.\n\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Perform the edit operation.\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Check if file exists.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treject(new Error(`Could not edit file: ${path}. ${errorMessage}.`));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if aborted before reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Read the file.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t\t\t// Check if aborted after reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(\n\t\t\t\t\t\t\t\t\tnormalizedContent,\n\t\t\t\t\t\t\t\t\tedits,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Check if aborted before writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\n\t\t\t\t\t\t\t\t// Check if aborted after writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\t\t\t\t\t// Truncate large diffs to save tokens\n\t\t\t\t\t\t\t\tconst diff =\n\t\t\t\t\t\t\t\t\tdiffResult.diff.length > MAX_DIFF_CHARS\n\t\t\t\t\t\t\t\t\t\t? `${diffResult.diff.slice(0, MAX_DIFF_CHARS)}\\n\\n[diff truncated for brevity]`\n\t\t\t\t\t\t\t\t\t\t: diffResult.diff;\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tdetails: { diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\t\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(callComponent, context.args as RenderableEditArgs | undefined, theme);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Box, Container, Spacer, Text } from \"@kolisachint/hoocode-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.js\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n/** Maximum characters for diff output sent to the LLM. */\nconst MAX_DIFF_CHARS = 4000;\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file (unless replaceAll is true) and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t\treplaceAll: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription:\n\t\t\t\t\t\"When true, replace every occurrence of oldText instead of requiring it to be unique. Use for renaming a symbol/string throughout the file. Default false.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\nexport interface EditToolDetails {\n\t/** Unified diff of the changes made */\n\tdiff: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) args.edits = parsed;\n\t\t} catch {}\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(\"Edit tool input is invalid. edits must contain at least one replacement.\");\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 0, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst invalidArg = invalidArgText(theme);\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file, unless that edit sets replaceAll: true to replace all of its occurrences. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet:\n\t\t\t\"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\t\t// Only the batching preference lives here. The matching semantics\n\t\t// (original-file matching, no overlaps, replaceAll) are already stated in the\n\t\t// `edits`/`oldText`/`replaceAll` schema descriptions, which ship on every turn\n\t\t// alongside these — repeating them spent the tokens twice.\n\t\tpromptGuidelines: [\n\t\t\t\"When changing several separate locations in one file, send one edit call with multiple entries in edits[] rather than several edit calls. Keep each oldText as small as it can be while staying unique.\",\n\t\t],\n\t\tparameters: editSchema,\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tabsolutePath,\n\t\t\t\t() =>\n\t\t\t\t\tnew Promise<{\n\t\t\t\t\t\tcontent: Array<{ type: \"text\"; text: string }>;\n\t\t\t\t\t\tdetails: EditToolDetails | undefined;\n\t\t\t\t\t}>((resolve, reject) => {\n\t\t\t\t\t\t// Check if already aborted.\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet aborted = false;\n\n\t\t\t\t\t\t// Set up abort handler.\n\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Perform the edit operation.\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Check if file exists.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treject(new Error(`Could not edit file: ${path}. ${errorMessage}.`));\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if aborted before reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Read the file.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t\t\t// Check if aborted after reading.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\t\t\t\t\tconst { bom, text: content } = stripBom(rawContent);\n\t\t\t\t\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\t\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\t\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(\n\t\t\t\t\t\t\t\t\tnormalizedContent,\n\t\t\t\t\t\t\t\t\tedits,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t// Check if aborted before writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\n\t\t\t\t\t\t\t\t// Check if aborted after writing.\n\t\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\t\t\t\t\t// Truncate large diffs to save tokens\n\t\t\t\t\t\t\t\tconst diff =\n\t\t\t\t\t\t\t\t\tdiffResult.diff.length > MAX_DIFF_CHARS\n\t\t\t\t\t\t\t\t\t\t? `${diffResult.diff.slice(0, MAX_DIFF_CHARS)}\\n\\n[diff truncated for brevity]`\n\t\t\t\t\t\t\t\t\t\t: diffResult.diff;\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\tdetails: { diff, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t\t\t// Clean up abort handler.\n\t\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})();\n\t\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(callComponent, context.args as RenderableEditArgs | undefined, theme);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAKjE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAMtF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,UAAU;;;;;;;;EAqBd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAKtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,yFAAyF;IACzF,IAAI,EAAE,CACL,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACvD,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAClC;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAmPhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { applyFdGlobPattern, relativizeFdLine, toPosixPath } from \"./fd-utils.js\";\nimport { isNativeSearchForced, nativeFind } from \"./native-search.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.Union([Type.String(), Type.Array(Type.String())], {\n\t\tdescription:\n\t\t\t\"Glob pattern(s) to match files. Pass one pattern or an array for OR logic, e.g. '*.ts', 'src/**/*.spec.ts', or ['src/**/*.ts', 'test/**/*.ts'].\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: current directory)\" })),\n\texclude: Type.Optional(\n\t\tType.Union([Type.String(), Type.Array(Type.String())], {\n\t\t\tdescription: \"Additional exclusion glob(s), e.g. '**/*.test.ts' or ['**/dist/**', '**/build/**'].\",\n\t\t}),\n\t),\n\ttype: Type.Optional(\n\t\tType.Union([Type.Literal(\"f\"), Type.Literal(\"d\"), Type.Literal(\"l\")], {\n\t\t\tdescription: \"Filter by entry type: 'f' files, 'd' directories, 'l' symlinks (default: 'f').\",\n\t\t}),\n\t),\n\tdepth: Type.Optional(Type.Number({ description: \"Maximum directory depth to search.\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results (default: 1000).\" })),\n\tcompress: Type.Optional(\n\t\tType.Boolean({ description: \"Group files in the same directory to shorten output (default: false).\" }),\n\t),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\nconst NO_RESULTS_MESSAGE = \"No files found matching pattern\";\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching one or more glob patterns. Returns relative or absolute paths. */\n\tglob: (\n\t\tpatterns: string[],\n\t\tcwd: string,\n\t\toptions: { ignore: string[]; limit: number; type?: string },\n\t) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: existsSync,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus fd */\n\toperations?: FindOperations;\n}\n\n/**\n * Compress a list of file paths by grouping files in the same directory.\n *\n * Compression rules:\n * - If a directory has >=3 entries all sharing the same extension: fold into `dir/{stem1,stem2,stem3}.ext`\n * - If >=3 entries with mixed extensions: sub-group by extension; fold sub-groups >=3\n * - If directory has >6 total files: summarize verbatim after sub-grouping\n * - 1-2 entries: emit verbatim\n */\nfunction compressPaths(paths: string[]): string {\n\tif (paths.length === 0) return \"\";\n\n\tconst sorted = [...paths].sort();\n\n\tconst dirMap = new Map<string, string[]>();\n\tfor (const p of sorted) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tif (!dirMap.has(dir)) dirMap.set(dir, []);\n\t\tdirMap.get(dir)!.push(base);\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (const [dir, files] of dirMap) {\n\t\tif (files.length <= 2) {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst extMap = new Map<string, string[]>();\n\t\tfor (const f of files) {\n\t\t\tconst ext = path.extname(f);\n\t\t\tconst stem = path.basename(f, ext);\n\t\t\tif (!extMap.has(ext)) extMap.set(ext, []);\n\t\t\textMap.get(ext)!.push(stem);\n\t\t}\n\n\t\tif (extMap.size === 1 && files.length >= 3) {\n\t\t\tconst [ext, stems] = extMap.entries().next().value!;\n\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\tresult.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet anyFolded = false;\n\t\tconst subResults: string[] = [];\n\t\tfor (const [ext, stems] of extMap) {\n\t\t\tif (stems.length >= 3) {\n\t\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\t\tsubResults.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\t\tanyFolded = true;\n\t\t\t} else {\n\t\t\t\tfor (const stem of stems) subResults.push(dir === \".\" ? `${stem}${ext}` : `${dir}/${stem}${ext}`);\n\t\t\t}\n\t\t}\n\n\t\tif (anyFolded || files.length > 6) {\n\t\t\tresult.push(...subResults);\n\t\t} else {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t}\n\t}\n\n\treturn result.join(\"\\n\");\n}\n\nfunction formatFindCall(\n\targs: { pattern?: string | string[]; path?: string; type?: string; depth?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst patterns = args?.pattern;\n\tconst patternStr = Array.isArray(patterns) ? patterns.join(\", \") : str(patterns);\n\tconst rawPath = str(args?.path);\n\tconst displayPath = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst type = args?.type;\n\tconst depth = args?.depth;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(patternStr === null || patternStr === \"\" ? invalidArg : theme.fg(\"accent\", patternStr)) +\n\t\ttheme.fg(\"toolOutput\", ` in ${displayPath === null ? invalidArg : displayPath}`);\n\tif (type && type !== \"f\") {\n\t\tconst typeLabel = type === \"d\" ? \"dirs\" : type === \"l\" ? \"symlinks\" : type;\n\t\ttext += theme.fg(\"toolOutput\", ` (${typeLabel})`);\n\t}\n\tif (depth !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (depth ${depth})`);\n\t}\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Search for files by one or more glob patterns (OR logic across an array). Optionally filter by entry type (files/dirs/symlinks), directory depth, and extra exclusions. Returns matching paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,\n\t\tpromptSnippet: \"Find files by glob pattern (supports multiple patterns, respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{\n\t\t\t\tpattern: rawPattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\texclude: rawExclude,\n\t\t\t\ttype,\n\t\t\t\tdepth,\n\t\t\t\tlimit,\n\t\t\t\tcompress = false,\n\t\t\t}: {\n\t\t\t\tpattern: string | string[];\n\t\t\t\tpath?: string;\n\t\t\t\texclude?: string | string[];\n\t\t\t\ttype?: \"f\" | \"d\" | \"l\";\n\t\t\t\tdepth?: number;\n\t\t\t\tlimit?: number;\n\t\t\t\tcompress?: boolean;\n\t\t\t},\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChildren: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChildren = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChildren?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT);\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tconst patterns = Array.isArray(rawPattern) ? rawPattern : [rawPattern];\n\t\t\t\t\t\tconst excludePatterns = rawExclude ? (Array.isArray(rawExclude) ? rawExclude : [rawExclude]) : [];\n\t\t\t\t\t\t// Always exclude node_modules and .git, plus any caller exclusions.\n\t\t\t\t\t\tconst allIgnore = [\"**/node_modules/**\", \"**/.git/**\", ...excludePatterns];\n\t\t\t\t\t\tconst typeFilter = type === \"d\" ? \"d\" : type === \"l\" ? \"l\" : \"f\";\n\n\t\t\t\t\t\tconst emit = (relativized: string[]) => {\n\t\t\t\t\t\t\tif (relativized.length === 0) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: NO_RESULTS_MESSAGE }], details: undefined }),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Sources over-fetch by one so exactly-limit result sets are not\n\t\t\t\t\t\t\t// misreported as truncated.\n\t\t\t\t\t\t\tconst unique = [...new Set(relativized)].sort();\n\t\t\t\t\t\t\tconst resultLimitReached = unique.length > effectiveLimit;\n\t\t\t\t\t\t\tconst truncated = unique.slice(0, effectiveLimit);\n\t\t\t\t\t\t\tconst rawOutput = compress ? compressPaths(truncated) : truncated.join(\"\\n\");\n\t\t\t\t\t\t\tconst truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\t\t\tlet resultOutput = truncation.content;\n\t\t\t\t\t\t\tconst details: FindToolDetails = {};\n\t\t\t\t\t\t\tconst notices: string[] = [];\n\t\t\t\t\t\t\tif (resultLimitReached) {\n\t\t\t\t\t\t\t\tnotices.push(\n\t\t\t\t\t\t\t\t\t`${effectiveLimit} result${effectiveLimit === 1 ? \"\" : \"s\"} limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tdetails.resultLimitReached = effectiveLimit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t\t\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (notices.length > 0) resultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: resultOutput }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(details).length > 0 ? details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(patterns, searchPath, {\n\t\t\t\t\t\t\t\tignore: allIgnore,\n\t\t\t\t\t\t\t\tlimit: effectiveLimit + 1,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\temit(\n\t\t\t\t\t\t\t\tresults.map((p) => {\n\t\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd, with a pure-JS fallback when fd is\n\t\t\t\t\t\t// unavailable (restricted environments) or explicitly forced.\n\t\t\t\t\t\tconst fdPath = isNativeSearchForced() ? undefined : await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst nativeResults = nativeFind(searchPath, {\n\t\t\t\t\t\t\t\tpatterns,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t\texcludeGlobs: allIgnore,\n\t\t\t\t\t\t\t\tmaxDepth: depth,\n\t\t\t\t\t\t\t\t// find always excludes node_modules/.git; the exclude globs above\n\t\t\t\t\t\t\t\t// cover them too, but skipping the dirs avoids descending them.\n\t\t\t\t\t\t\t\talwaysSkipDirs: new Set([\".git\", \"node_modules\"]),\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\temit(nativeResults);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst children: ChildProcess[] = [];\n\t\t\t\t\t\tstopChildren = () => {\n\t\t\t\t\t\t\tfor (const child of children) if (!child.killed) child.kill();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Run each pattern with fd. fd errors (e.g. an invalid glob) surface as a\n\t\t\t\t\t\t// rejection when the pattern produced no output, matching the shell's\n\t\t\t\t\t\t// behavior; a non-zero exit that still produced matches is tolerated.\n\t\t\t\t\t\tconst runPattern = (pattern: string): Promise<string[]> =>\n\t\t\t\t\t\t\tnew Promise<string[]>((res, rej) => {\n\t\t\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\t\t\"--type\",\n\t\t\t\t\t\t\t\t\ttypeFilter,\n\t\t\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\t\t\tString(effectiveLimit + 1),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\tfor (const excl of allIgnore) args.push(\"--exclude\", excl);\n\t\t\t\t\t\t\t\tif (depth !== undefined) args.push(\"--max-depth\", String(depth));\n\t\t\t\t\t\t\t\tconst effectivePattern = applyFdGlobPattern(args, pattern);\n\t\t\t\t\t\t\t\targs.push(\"--\", effectivePattern, searchPath);\n\n\t\t\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\t\t\tchildren.push(child);\n\t\t\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout! });\n\t\t\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\t\t\tconst lines: string[] = [];\n\t\t\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\trl.on(\"line\", (line) => lines.push(line));\n\t\t\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\trej(new Error(`Failed to run fd: ${error.message}`));\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\tif (code !== 0 && lines.length === 0) {\n\t\t\t\t\t\t\t\t\t\trej(new Error(stderr.trim() || `fd exited with code ${code}`));\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tres(lines);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst patternResults = await Promise.all(patterns.map(runPattern));\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\tfor (const results of patternResults) {\n\t\t\t\t\t\t\tfor (const rawLine of results) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\trelativized.push(relativizeFdLine(line, searchPath));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\temit(relativized);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/core/tools/find.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAKjE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAMtF,OAAO,EAAiC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAEnG,QAAA,MAAM,UAAU;;;;;;;;EAkBd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAKtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7D,yFAAyF;IACzF,IAAI,EAAE,CACL,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACvD,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CAClC;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAmPhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["import { createInterface } from \"node:readline\";\nimport type { AgentTool } from \"@kolisachint/hoocode-agent-core\";\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { ensureTool } from \"../../utils/tools-manager.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { applyFdGlobPattern, relativizeFdLine, toPosixPath } from \"./fd-utils.js\";\nimport { isNativeSearchForced, nativeFind } from \"./native-search.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst findSchema = Type.Object({\n\tpattern: Type.Union([Type.String(), Type.Array(Type.String())], {\n\t\tdescription: \"Glob(s) to match, e.g. 'src/**/*.spec.ts'. An array is OR'd.\",\n\t}),\n\tpath: Type.Optional(Type.String({ description: \"Directory to search in (default: cwd)\" })),\n\texclude: Type.Optional(\n\t\tType.Union([Type.String(), Type.Array(Type.String())], { description: \"Extra exclusion glob(s).\" }),\n\t),\n\ttype: Type.Optional(\n\t\tType.Union([Type.Literal(\"f\"), Type.Literal(\"d\"), Type.Literal(\"l\")], {\n\t\t\tdescription: \"f=files, d=directories, l=symlinks (default: f).\",\n\t\t}),\n\t),\n\tdepth: Type.Optional(Type.Number({ description: \"Maximum directory depth.\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum results (default: 1000).\" })),\n\tcompress: Type.Optional(\n\t\tType.Boolean({ description: \"Group files by directory to shorten output (default: false).\" }),\n\t),\n});\n\nexport type FindToolInput = Static<typeof findSchema>;\n\nconst DEFAULT_LIMIT = 1000;\nconst NO_RESULTS_MESSAGE = \"No files found matching pattern\";\n\nexport interface FindToolDetails {\n\ttruncation?: TruncationResult;\n\tresultLimitReached?: number;\n}\n\n/**\n * Pluggable operations for the find tool.\n * Override these to delegate file search to remote systems (for example SSH).\n */\nexport interface FindOperations {\n\t/** Check if path exists */\n\texists: (absolutePath: string) => Promise<boolean> | boolean;\n\t/** Find files matching one or more glob patterns. Returns relative or absolute paths. */\n\tglob: (\n\t\tpatterns: string[],\n\t\tcwd: string,\n\t\toptions: { ignore: string[]; limit: number; type?: string },\n\t) => Promise<string[]> | string[];\n}\n\nconst defaultFindOperations: FindOperations = {\n\texists: existsSync,\n\t// This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.\n\tglob: () => [],\n};\n\nexport interface FindToolOptions {\n\t/** Custom operations for find. Default: local filesystem plus fd */\n\toperations?: FindOperations;\n}\n\n/**\n * Compress a list of file paths by grouping files in the same directory.\n *\n * Compression rules:\n * - If a directory has >=3 entries all sharing the same extension: fold into `dir/{stem1,stem2,stem3}.ext`\n * - If >=3 entries with mixed extensions: sub-group by extension; fold sub-groups >=3\n * - If directory has >6 total files: summarize verbatim after sub-grouping\n * - 1-2 entries: emit verbatim\n */\nfunction compressPaths(paths: string[]): string {\n\tif (paths.length === 0) return \"\";\n\n\tconst sorted = [...paths].sort();\n\n\tconst dirMap = new Map<string, string[]>();\n\tfor (const p of sorted) {\n\t\tconst dir = path.dirname(p);\n\t\tconst base = path.basename(p);\n\t\tif (!dirMap.has(dir)) dirMap.set(dir, []);\n\t\tdirMap.get(dir)!.push(base);\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (const [dir, files] of dirMap) {\n\t\tif (files.length <= 2) {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst extMap = new Map<string, string[]>();\n\t\tfor (const f of files) {\n\t\t\tconst ext = path.extname(f);\n\t\t\tconst stem = path.basename(f, ext);\n\t\t\tif (!extMap.has(ext)) extMap.set(ext, []);\n\t\t\textMap.get(ext)!.push(stem);\n\t\t}\n\n\t\tif (extMap.size === 1 && files.length >= 3) {\n\t\t\tconst [ext, stems] = extMap.entries().next().value!;\n\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\tresult.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet anyFolded = false;\n\t\tconst subResults: string[] = [];\n\t\tfor (const [ext, stems] of extMap) {\n\t\t\tif (stems.length >= 3) {\n\t\t\t\tconst displayDir = dir === \".\" ? \"\" : `${dir}/`;\n\t\t\t\tsubResults.push(`${displayDir}{${stems.join(\",\")}}${ext}`);\n\t\t\t\tanyFolded = true;\n\t\t\t} else {\n\t\t\t\tfor (const stem of stems) subResults.push(dir === \".\" ? `${stem}${ext}` : `${dir}/${stem}${ext}`);\n\t\t\t}\n\t\t}\n\n\t\tif (anyFolded || files.length > 6) {\n\t\t\tresult.push(...subResults);\n\t\t} else {\n\t\t\tfor (const f of files) result.push(dir === \".\" ? f : `${dir}/${f}`);\n\t\t}\n\t}\n\n\treturn result.join(\"\\n\");\n}\n\nfunction formatFindCall(\n\targs: { pattern?: string | string[]; path?: string; type?: string; depth?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst patterns = args?.pattern;\n\tconst patternStr = Array.isArray(patterns) ? patterns.join(\", \") : str(patterns);\n\tconst rawPath = str(args?.path);\n\tconst displayPath = rawPath !== null ? shortenPath(rawPath || \".\") : null;\n\tconst type = args?.type;\n\tconst depth = args?.depth;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text =\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"find\")) +\n\t\t\" \" +\n\t\t(patternStr === null || patternStr === \"\" ? invalidArg : theme.fg(\"accent\", patternStr)) +\n\t\ttheme.fg(\"toolOutput\", ` in ${displayPath === null ? invalidArg : displayPath}`);\n\tif (type && type !== \"f\") {\n\t\tconst typeLabel = type === \"d\" ? \"dirs\" : type === \"l\" ? \"symlinks\" : type;\n\t\ttext += theme.fg(\"toolOutput\", ` (${typeLabel})`);\n\t}\n\tif (depth !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (depth ${depth})`);\n\t}\n\tif (limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` (limit ${limit})`);\n\t}\n\treturn text;\n}\n\nfunction formatFindResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: FindToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tshowImages: boolean,\n): string {\n\tconst output = getTextOutput(result, showImages).trim();\n\tlet text = \"\";\n\tif (output) {\n\t\tconst lines = output.split(\"\\n\");\n\t\tconst maxLines = options.expanded ? lines.length : 20;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\tconst resultLimit = result.details?.resultLimitReached;\n\tconst truncation = result.details?.truncation;\n\tif (resultLimit || truncation?.truncated) {\n\t\tconst warnings: string[] = [];\n\t\tif (resultLimit) warnings.push(`${resultLimit} results limit`);\n\t\tif (truncation?.truncated) warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);\n\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${warnings.join(\", \")}]`)}`;\n\t}\n\treturn text;\n}\n\nexport function createFindToolDefinition(\n\tcwd: string,\n\toptions?: FindToolOptions,\n): ToolDefinition<typeof findSchema, FindToolDetails | undefined> {\n\tconst customOps = options?.operations;\n\treturn {\n\t\tname: \"find\",\n\t\tlabel: \"find\",\n\t\tdescription: `Locate files by glob. Returns paths relative to the search directory. Respects .gitignore. Truncates at ${DEFAULT_LIMIT} results / ${DEFAULT_MAX_BYTES / 1024}KB.`,\n\t\tpromptSnippet: \"Find files by glob pattern (supports multiple patterns, respects .gitignore)\",\n\t\tparameters: findSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{\n\t\t\t\tpattern: rawPattern,\n\t\t\t\tpath: searchDir,\n\t\t\t\texclude: rawExclude,\n\t\t\t\ttype,\n\t\t\t\tdepth,\n\t\t\t\tlimit,\n\t\t\t\tcompress = false,\n\t\t\t}: {\n\t\t\t\tpattern: string | string[];\n\t\t\t\tpath?: string;\n\t\t\t\texclude?: string | string[];\n\t\t\t\ttype?: \"f\" | \"d\" | \"l\";\n\t\t\t\tdepth?: number;\n\t\t\t\tlimit?: number;\n\t\t\t\tcompress?: boolean;\n\t\t\t},\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet settled = false;\n\t\t\t\tlet stopChildren: (() => void) | undefined;\n\t\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tstopChildren = undefined;\n\t\t\t\t\tfn();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tstopChildren?.();\n\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t};\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst searchPath = resolveToCwd(searchDir || \".\", cwd);\n\t\t\t\t\t\tconst effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT);\n\t\t\t\t\t\tconst ops = customOps ?? defaultFindOperations;\n\n\t\t\t\t\t\tconst patterns = Array.isArray(rawPattern) ? rawPattern : [rawPattern];\n\t\t\t\t\t\tconst excludePatterns = rawExclude ? (Array.isArray(rawExclude) ? rawExclude : [rawExclude]) : [];\n\t\t\t\t\t\t// Always exclude node_modules and .git, plus any caller exclusions.\n\t\t\t\t\t\tconst allIgnore = [\"**/node_modules/**\", \"**/.git/**\", ...excludePatterns];\n\t\t\t\t\t\tconst typeFilter = type === \"d\" ? \"d\" : type === \"l\" ? \"l\" : \"f\";\n\n\t\t\t\t\t\tconst emit = (relativized: string[]) => {\n\t\t\t\t\t\t\tif (relativized.length === 0) {\n\t\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: NO_RESULTS_MESSAGE }], details: undefined }),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Sources over-fetch by one so exactly-limit result sets are not\n\t\t\t\t\t\t\t// misreported as truncated.\n\t\t\t\t\t\t\tconst unique = [...new Set(relativized)].sort();\n\t\t\t\t\t\t\tconst resultLimitReached = unique.length > effectiveLimit;\n\t\t\t\t\t\t\tconst truncated = unique.slice(0, effectiveLimit);\n\t\t\t\t\t\t\tconst rawOutput = compress ? compressPaths(truncated) : truncated.join(\"\\n\");\n\t\t\t\t\t\t\tconst truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });\n\t\t\t\t\t\t\tlet resultOutput = truncation.content;\n\t\t\t\t\t\t\tconst details: FindToolDetails = {};\n\t\t\t\t\t\t\tconst notices: string[] = [];\n\t\t\t\t\t\t\tif (resultLimitReached) {\n\t\t\t\t\t\t\t\tnotices.push(\n\t\t\t\t\t\t\t\t\t`${effectiveLimit} result${effectiveLimit === 1 ? \"\" : \"s\"} limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tdetails.resultLimitReached = effectiveLimit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\t\tnotices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);\n\t\t\t\t\t\t\t\tdetails.truncation = truncation;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (notices.length > 0) resultOutput += `\\n\\n[${notices.join(\". \")}]`;\n\t\t\t\t\t\t\tsettle(() =>\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: resultOutput }],\n\t\t\t\t\t\t\t\t\tdetails: Object.keys(details).length > 0 ? details : undefined,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// If custom operations provide glob(), use that instead of fd.\n\t\t\t\t\t\tif (customOps?.glob) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst results = await ops.glob(patterns, searchPath, {\n\t\t\t\t\t\t\t\tignore: allIgnore,\n\t\t\t\t\t\t\t\tlimit: effectiveLimit + 1,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\temit(\n\t\t\t\t\t\t\t\tresults.map((p) => {\n\t\t\t\t\t\t\t\t\tif (p.startsWith(searchPath)) return toPosixPath(p.slice(searchPath.length + 1));\n\t\t\t\t\t\t\t\t\treturn toPosixPath(path.relative(searchPath, p));\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default implementation uses fd, with a pure-JS fallback when fd is\n\t\t\t\t\t\t// unavailable (restricted environments) or explicitly forced.\n\t\t\t\t\t\tconst fdPath = isNativeSearchForced() ? undefined : await ensureTool(\"fd\", true);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!fdPath) {\n\t\t\t\t\t\t\tif (!(await ops.exists(searchPath))) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(`Path not found: ${searchPath}`)));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst nativeResults = nativeFind(searchPath, {\n\t\t\t\t\t\t\t\tpatterns,\n\t\t\t\t\t\t\t\ttype: typeFilter,\n\t\t\t\t\t\t\t\texcludeGlobs: allIgnore,\n\t\t\t\t\t\t\t\tmaxDepth: depth,\n\t\t\t\t\t\t\t\t// find always excludes node_modules/.git; the exclude globs above\n\t\t\t\t\t\t\t\t// cover them too, but skipping the dirs avoids descending them.\n\t\t\t\t\t\t\t\talwaysSkipDirs: new Set([\".git\", \"node_modules\"]),\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\temit(nativeResults);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst children: ChildProcess[] = [];\n\t\t\t\t\t\tstopChildren = () => {\n\t\t\t\t\t\t\tfor (const child of children) if (!child.killed) child.kill();\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Run each pattern with fd. fd errors (e.g. an invalid glob) surface as a\n\t\t\t\t\t\t// rejection when the pattern produced no output, matching the shell's\n\t\t\t\t\t\t// behavior; a non-zero exit that still produced matches is tolerated.\n\t\t\t\t\t\tconst runPattern = (pattern: string): Promise<string[]> =>\n\t\t\t\t\t\t\tnew Promise<string[]>((res, rej) => {\n\t\t\t\t\t\t\t\tconst args: string[] = [\n\t\t\t\t\t\t\t\t\t\"--glob\",\n\t\t\t\t\t\t\t\t\t\"--color=never\",\n\t\t\t\t\t\t\t\t\t\"--hidden\",\n\t\t\t\t\t\t\t\t\t\"--no-require-git\",\n\t\t\t\t\t\t\t\t\t\"--type\",\n\t\t\t\t\t\t\t\t\ttypeFilter,\n\t\t\t\t\t\t\t\t\t\"--max-results\",\n\t\t\t\t\t\t\t\t\tString(effectiveLimit + 1),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\tfor (const excl of allIgnore) args.push(\"--exclude\", excl);\n\t\t\t\t\t\t\t\tif (depth !== undefined) args.push(\"--max-depth\", String(depth));\n\t\t\t\t\t\t\t\tconst effectivePattern = applyFdGlobPattern(args, pattern);\n\t\t\t\t\t\t\t\targs.push(\"--\", effectivePattern, searchPath);\n\n\t\t\t\t\t\t\t\tconst child = spawn(fdPath, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\t\t\t\t\t\t\tchildren.push(child);\n\t\t\t\t\t\t\t\tconst rl = createInterface({ input: child.stdout! });\n\t\t\t\t\t\t\t\tlet stderr = \"\";\n\t\t\t\t\t\t\t\tconst lines: string[] = [];\n\t\t\t\t\t\t\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\t\t\t\t\t\t\tstderr += chunk.toString();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\trl.on(\"line\", (line) => lines.push(line));\n\t\t\t\t\t\t\t\tchild.on(\"error\", (error) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\trej(new Error(`Failed to run fd: ${error.message}`));\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\t\t\t\t\t\trl.close();\n\t\t\t\t\t\t\t\t\tif (code !== 0 && lines.length === 0) {\n\t\t\t\t\t\t\t\t\t\trej(new Error(stderr.trim() || `fd exited with code ${code}`));\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tres(lines);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst patternResults = await Promise.all(patterns.map(runPattern));\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst relativized: string[] = [];\n\t\t\t\t\t\tfor (const results of patternResults) {\n\t\t\t\t\t\t\tfor (const rawLine of results) {\n\t\t\t\t\t\t\t\tconst line = rawLine.replace(/\\r$/, \"\").trim();\n\t\t\t\t\t\t\t\tif (!line) continue;\n\t\t\t\t\t\t\t\trelativized.push(relativizeFdLine(line, searchPath));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\temit(relativized);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\tsettle(() => reject(new Error(\"Operation aborted\")));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst error = e instanceof Error ? e : new Error(String(e));\n\t\t\t\t\t\tsettle(() => reject(error));\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatFindResult(result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createFindTool(cwd: string, options?: FindToolOptions): AgentTool<typeof findSchema> {\n\treturn wrapToolDefinition(createFindToolDefinition(cwd, options));\n}\n"]}
@@ -14,18 +14,16 @@ import { wrapToolDefinition } from "./tool-definition-wrapper.js";
14
14
  import { DEFAULT_MAX_BYTES, formatSize, truncateHead } from "./truncate.js";
15
15
  const findSchema = Type.Object({
16
16
  pattern: Type.Union([Type.String(), Type.Array(Type.String())], {
17
- description: "Glob pattern(s) to match files. Pass one pattern or an array for OR logic, e.g. '*.ts', 'src/**/*.spec.ts', or ['src/**/*.ts', 'test/**/*.ts'].",
17
+ description: "Glob(s) to match, e.g. 'src/**/*.spec.ts'. An array is OR'd.",
18
18
  }),
19
- path: Type.Optional(Type.String({ description: "Directory to search in (default: current directory)" })),
20
- exclude: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())], {
21
- description: "Additional exclusion glob(s), e.g. '**/*.test.ts' or ['**/dist/**', '**/build/**'].",
22
- })),
19
+ path: Type.Optional(Type.String({ description: "Directory to search in (default: cwd)" })),
20
+ exclude: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())], { description: "Extra exclusion glob(s)." })),
23
21
  type: Type.Optional(Type.Union([Type.Literal("f"), Type.Literal("d"), Type.Literal("l")], {
24
- description: "Filter by entry type: 'f' files, 'd' directories, 'l' symlinks (default: 'f').",
22
+ description: "f=files, d=directories, l=symlinks (default: f).",
25
23
  })),
26
- depth: Type.Optional(Type.Number({ description: "Maximum directory depth to search." })),
27
- limit: Type.Optional(Type.Number({ description: "Maximum number of results (default: 1000)." })),
28
- compress: Type.Optional(Type.Boolean({ description: "Group files in the same directory to shorten output (default: false)." })),
24
+ depth: Type.Optional(Type.Number({ description: "Maximum directory depth." })),
25
+ limit: Type.Optional(Type.Number({ description: "Maximum results (default: 1000)." })),
26
+ compress: Type.Optional(Type.Boolean({ description: "Group files by directory to shorten output (default: false)." })),
29
27
  });
30
28
  const DEFAULT_LIMIT = 1000;
31
29
  const NO_RESULTS_MESSAGE = "No files found matching pattern";
@@ -154,7 +152,7 @@ export function createFindToolDefinition(cwd, options) {
154
152
  return {
155
153
  name: "find",
156
154
  label: "find",
157
- description: `Search for files by one or more glob patterns (OR logic across an array). Optionally filter by entry type (files/dirs/symlinks), directory depth, and extra exclusions. Returns matching paths relative to the search directory. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,
155
+ description: `Locate files by glob. Returns paths relative to the search directory. Respects .gitignore. Truncates at ${DEFAULT_LIMIT} results / ${DEFAULT_MAX_BYTES / 1024}KB.`,
158
156
  promptSnippet: "Find files by glob pattern (supports multiple patterns, respects .gitignore)",
159
157
  parameters: findSchema,
160
158
  async execute(_toolCallId, { pattern: rawPattern, path: searchDir, exclude: rawExclude, type, depth, limit, compress = false, }, signal, _onUpdate, _ctx) {