@cdoing/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/dist/agents/coordinator.d.ts +114 -0
  2. package/dist/agents/coordinator.d.ts.map +1 -0
  3. package/dist/agents/coordinator.js +158 -0
  4. package/dist/agents/coordinator.js.map +1 -0
  5. package/dist/context-providers/clipboard.d.ts +13 -0
  6. package/dist/context-providers/clipboard.d.ts.map +1 -0
  7. package/dist/context-providers/clipboard.js +53 -0
  8. package/dist/context-providers/clipboard.js.map +1 -0
  9. package/dist/context-providers/codebase.d.ts +46 -0
  10. package/dist/context-providers/codebase.d.ts.map +1 -0
  11. package/dist/context-providers/codebase.js +273 -0
  12. package/dist/context-providers/codebase.js.map +1 -0
  13. package/dist/context-providers/diff.d.ts +18 -0
  14. package/dist/context-providers/diff.d.ts.map +1 -0
  15. package/dist/context-providers/diff.js +63 -0
  16. package/dist/context-providers/diff.js.map +1 -0
  17. package/dist/context-providers/docs.d.ts +21 -0
  18. package/dist/context-providers/docs.d.ts.map +1 -0
  19. package/dist/context-providers/docs.js +180 -0
  20. package/dist/context-providers/docs.js.map +1 -0
  21. package/dist/context-providers/file-include.d.ts +13 -0
  22. package/dist/context-providers/file-include.d.ts.map +1 -0
  23. package/dist/context-providers/file-include.js +82 -0
  24. package/dist/context-providers/file-include.js.map +1 -0
  25. package/dist/context-providers/folder.d.ts +19 -0
  26. package/dist/context-providers/folder.d.ts.map +1 -0
  27. package/dist/context-providers/folder.js +130 -0
  28. package/dist/context-providers/folder.js.map +1 -0
  29. package/dist/context-providers/git.d.ts +19 -0
  30. package/dist/context-providers/git.d.ts.map +1 -0
  31. package/dist/context-providers/git.js +74 -0
  32. package/dist/context-providers/git.js.map +1 -0
  33. package/dist/context-providers/index.d.ts +26 -0
  34. package/dist/context-providers/index.d.ts.map +1 -0
  35. package/dist/context-providers/index.js +37 -0
  36. package/dist/context-providers/index.js.map +1 -0
  37. package/dist/context-providers/open-files.d.ts +25 -0
  38. package/dist/context-providers/open-files.d.ts.map +1 -0
  39. package/dist/context-providers/open-files.js +134 -0
  40. package/dist/context-providers/open-files.js.map +1 -0
  41. package/dist/context-providers/problems.d.ts +24 -0
  42. package/dist/context-providers/problems.d.ts.map +1 -0
  43. package/dist/context-providers/problems.js +97 -0
  44. package/dist/context-providers/problems.js.map +1 -0
  45. package/dist/context-providers/registry.d.ts +61 -0
  46. package/dist/context-providers/registry.d.ts.map +1 -0
  47. package/dist/context-providers/registry.js +92 -0
  48. package/dist/context-providers/registry.js.map +1 -0
  49. package/dist/context-providers/terminal.d.ts +25 -0
  50. package/dist/context-providers/terminal.d.ts.map +1 -0
  51. package/dist/context-providers/terminal.js +55 -0
  52. package/dist/context-providers/terminal.js.map +1 -0
  53. package/dist/context-providers/tree.d.ts +29 -0
  54. package/dist/context-providers/tree.d.ts.map +1 -0
  55. package/dist/context-providers/tree.js +172 -0
  56. package/dist/context-providers/tree.js.map +1 -0
  57. package/dist/context-providers/types.d.ts +72 -0
  58. package/dist/context-providers/types.d.ts.map +1 -0
  59. package/dist/context-providers/types.js +10 -0
  60. package/dist/context-providers/types.js.map +1 -0
  61. package/dist/context-providers/url.d.ts +27 -0
  62. package/dist/context-providers/url.d.ts.map +1 -0
  63. package/dist/context-providers/url.js +131 -0
  64. package/dist/context-providers/url.js.map +1 -0
  65. package/dist/effort/index.d.ts +78 -0
  66. package/dist/effort/index.d.ts.map +1 -0
  67. package/dist/effort/index.js +146 -0
  68. package/dist/effort/index.js.map +1 -0
  69. package/dist/hooks/index.d.ts +47 -0
  70. package/dist/hooks/index.d.ts.map +1 -0
  71. package/dist/hooks/index.js +151 -0
  72. package/dist/hooks/index.js.map +1 -0
  73. package/dist/index.d.ts +75 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +152 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/indexing/chunker.d.ts +25 -0
  78. package/dist/indexing/chunker.d.ts.map +1 -0
  79. package/dist/indexing/chunker.js +217 -0
  80. package/dist/indexing/chunker.js.map +1 -0
  81. package/dist/indexing/database.d.ts +49 -0
  82. package/dist/indexing/database.d.ts.map +1 -0
  83. package/dist/indexing/database.js +287 -0
  84. package/dist/indexing/database.js.map +1 -0
  85. package/dist/indexing/index.d.ts +9 -0
  86. package/dist/indexing/index.d.ts.map +1 -0
  87. package/dist/indexing/index.js +13 -0
  88. package/dist/indexing/index.js.map +1 -0
  89. package/dist/indexing/indexer.d.ts +63 -0
  90. package/dist/indexing/indexer.d.ts.map +1 -0
  91. package/dist/indexing/indexer.js +352 -0
  92. package/dist/indexing/indexer.js.map +1 -0
  93. package/dist/indexing/recent-edits-cache.d.ts +77 -0
  94. package/dist/indexing/recent-edits-cache.d.ts.map +1 -0
  95. package/dist/indexing/recent-edits-cache.js +123 -0
  96. package/dist/indexing/recent-edits-cache.js.map +1 -0
  97. package/dist/indexing/types.d.ts +39 -0
  98. package/dist/indexing/types.d.ts.map +1 -0
  99. package/dist/indexing/types.js +6 -0
  100. package/dist/indexing/types.js.map +1 -0
  101. package/dist/mcp/index.d.ts +33 -0
  102. package/dist/mcp/index.d.ts.map +1 -0
  103. package/dist/mcp/index.js +37 -0
  104. package/dist/mcp/index.js.map +1 -0
  105. package/dist/mcp/manager.d.ts +123 -0
  106. package/dist/mcp/manager.d.ts.map +1 -0
  107. package/dist/mcp/manager.js +331 -0
  108. package/dist/mcp/manager.js.map +1 -0
  109. package/dist/oauth.d.ts +33 -0
  110. package/dist/oauth.d.ts.map +1 -0
  111. package/dist/oauth.js +312 -0
  112. package/dist/oauth.js.map +1 -0
  113. package/dist/permissions/index.d.ts +216 -0
  114. package/dist/permissions/index.d.ts.map +1 -0
  115. package/dist/permissions/index.js +938 -0
  116. package/dist/permissions/index.js.map +1 -0
  117. package/dist/plan/index.d.ts +20 -0
  118. package/dist/plan/index.d.ts.map +1 -0
  119. package/dist/plan/index.js +24 -0
  120. package/dist/plan/index.js.map +1 -0
  121. package/dist/plan/manager.d.ts +101 -0
  122. package/dist/plan/manager.d.ts.map +1 -0
  123. package/dist/plan/manager.js +170 -0
  124. package/dist/plan/manager.js.map +1 -0
  125. package/dist/rules/index.d.ts +28 -0
  126. package/dist/rules/index.d.ts.map +1 -0
  127. package/dist/rules/index.js +31 -0
  128. package/dist/rules/index.js.map +1 -0
  129. package/dist/rules/manager.d.ts +77 -0
  130. package/dist/rules/manager.d.ts.map +1 -0
  131. package/dist/rules/manager.js +279 -0
  132. package/dist/rules/manager.js.map +1 -0
  133. package/dist/rules/types.d.ts +34 -0
  134. package/dist/rules/types.d.ts.map +1 -0
  135. package/dist/rules/types.js +9 -0
  136. package/dist/rules/types.js.map +1 -0
  137. package/dist/sandbox/filesystem.d.ts +20 -0
  138. package/dist/sandbox/filesystem.d.ts.map +1 -0
  139. package/dist/sandbox/filesystem.js +141 -0
  140. package/dist/sandbox/filesystem.js.map +1 -0
  141. package/dist/sandbox/index.d.ts +4 -0
  142. package/dist/sandbox/index.d.ts.map +1 -0
  143. package/dist/sandbox/index.js +8 -0
  144. package/dist/sandbox/index.js.map +1 -0
  145. package/dist/sandbox/manager.d.ts +47 -0
  146. package/dist/sandbox/manager.d.ts.map +1 -0
  147. package/dist/sandbox/manager.js +220 -0
  148. package/dist/sandbox/manager.js.map +1 -0
  149. package/dist/sandbox/network.d.ts +14 -0
  150. package/dist/sandbox/network.d.ts.map +1 -0
  151. package/dist/sandbox/network.js +87 -0
  152. package/dist/sandbox/network.js.map +1 -0
  153. package/dist/sandbox/types.d.ts +42 -0
  154. package/dist/sandbox/types.d.ts.map +1 -0
  155. package/dist/sandbox/types.js +25 -0
  156. package/dist/sandbox/types.js.map +1 -0
  157. package/dist/tools/ast-edit.d.ts +57 -0
  158. package/dist/tools/ast-edit.d.ts.map +1 -0
  159. package/dist/tools/ast-edit.js +443 -0
  160. package/dist/tools/ast-edit.js.map +1 -0
  161. package/dist/tools/code-verify.d.ts +8 -0
  162. package/dist/tools/code-verify.d.ts.map +1 -0
  163. package/dist/tools/code-verify.js +159 -0
  164. package/dist/tools/code-verify.js.map +1 -0
  165. package/dist/tools/codebase-search.d.ts +17 -0
  166. package/dist/tools/codebase-search.d.ts.map +1 -0
  167. package/dist/tools/codebase-search.js +104 -0
  168. package/dist/tools/codebase-search.js.map +1 -0
  169. package/dist/tools/file-delete.d.ts +26 -0
  170. package/dist/tools/file-delete.d.ts.map +1 -0
  171. package/dist/tools/file-delete.js +179 -0
  172. package/dist/tools/file-delete.js.map +1 -0
  173. package/dist/tools/file-edit.d.ts +10 -0
  174. package/dist/tools/file-edit.d.ts.map +1 -0
  175. package/dist/tools/file-edit.js +138 -0
  176. package/dist/tools/file-edit.js.map +1 -0
  177. package/dist/tools/file-read.d.ts +12 -0
  178. package/dist/tools/file-read.d.ts.map +1 -0
  179. package/dist/tools/file-read.js +211 -0
  180. package/dist/tools/file-read.js.map +1 -0
  181. package/dist/tools/file-run.d.ts +10 -0
  182. package/dist/tools/file-run.d.ts.map +1 -0
  183. package/dist/tools/file-run.js +179 -0
  184. package/dist/tools/file-run.js.map +1 -0
  185. package/dist/tools/file-write.d.ts +10 -0
  186. package/dist/tools/file-write.d.ts.map +1 -0
  187. package/dist/tools/file-write.js +134 -0
  188. package/dist/tools/file-write.js.map +1 -0
  189. package/dist/tools/glob-search.d.ts +8 -0
  190. package/dist/tools/glob-search.d.ts.map +1 -0
  191. package/dist/tools/glob-search.js +108 -0
  192. package/dist/tools/glob-search.js.map +1 -0
  193. package/dist/tools/grep-search.d.ts +8 -0
  194. package/dist/tools/grep-search.d.ts.map +1 -0
  195. package/dist/tools/grep-search.js +139 -0
  196. package/dist/tools/grep-search.js.map +1 -0
  197. package/dist/tools/list-dir.d.ts +16 -0
  198. package/dist/tools/list-dir.d.ts.map +1 -0
  199. package/dist/tools/list-dir.js +183 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/multi-edit.d.ts +16 -0
  202. package/dist/tools/multi-edit.d.ts.map +1 -0
  203. package/dist/tools/multi-edit.js +163 -0
  204. package/dist/tools/multi-edit.js.map +1 -0
  205. package/dist/tools/notebook-edit.d.ts +31 -0
  206. package/dist/tools/notebook-edit.d.ts.map +1 -0
  207. package/dist/tools/notebook-edit.js +321 -0
  208. package/dist/tools/notebook-edit.js.map +1 -0
  209. package/dist/tools/registry.d.ts +16 -0
  210. package/dist/tools/registry.d.ts.map +1 -0
  211. package/dist/tools/registry.js +41 -0
  212. package/dist/tools/registry.js.map +1 -0
  213. package/dist/tools/shell-exec.d.ts +12 -0
  214. package/dist/tools/shell-exec.d.ts.map +1 -0
  215. package/dist/tools/shell-exec.js +261 -0
  216. package/dist/tools/shell-exec.js.map +1 -0
  217. package/dist/tools/sub-agent-manager.d.ts +57 -0
  218. package/dist/tools/sub-agent-manager.d.ts.map +1 -0
  219. package/dist/tools/sub-agent-manager.js +153 -0
  220. package/dist/tools/sub-agent-manager.js.map +1 -0
  221. package/dist/tools/sub-agent-status.d.ts +12 -0
  222. package/dist/tools/sub-agent-status.d.ts.map +1 -0
  223. package/dist/tools/sub-agent-status.js +59 -0
  224. package/dist/tools/sub-agent-status.js.map +1 -0
  225. package/dist/tools/sub-agent-terminate.d.ts +12 -0
  226. package/dist/tools/sub-agent-terminate.d.ts.map +1 -0
  227. package/dist/tools/sub-agent-terminate.js +55 -0
  228. package/dist/tools/sub-agent-terminate.js.map +1 -0
  229. package/dist/tools/sub-agent.d.ts +34 -0
  230. package/dist/tools/sub-agent.d.ts.map +1 -0
  231. package/dist/tools/sub-agent.js +140 -0
  232. package/dist/tools/sub-agent.js.map +1 -0
  233. package/dist/tools/system-info.d.ts +24 -0
  234. package/dist/tools/system-info.d.ts.map +1 -0
  235. package/dist/tools/system-info.js +220 -0
  236. package/dist/tools/system-info.js.map +1 -0
  237. package/dist/tools/todo.d.ts +16 -0
  238. package/dist/tools/todo.d.ts.map +1 -0
  239. package/dist/tools/todo.js +144 -0
  240. package/dist/tools/todo.js.map +1 -0
  241. package/dist/tools/types.d.ts +20 -0
  242. package/dist/tools/types.d.ts.map +1 -0
  243. package/dist/tools/types.js +3 -0
  244. package/dist/tools/types.js.map +1 -0
  245. package/dist/tools/view-diff.d.ts +11 -0
  246. package/dist/tools/view-diff.d.ts.map +1 -0
  247. package/dist/tools/view-diff.js +88 -0
  248. package/dist/tools/view-diff.js.map +1 -0
  249. package/dist/tools/view-repo-map.d.ts +18 -0
  250. package/dist/tools/view-repo-map.d.ts.map +1 -0
  251. package/dist/tools/view-repo-map.js +245 -0
  252. package/dist/tools/view-repo-map.js.map +1 -0
  253. package/dist/tools/web-fetch.d.ts +13 -0
  254. package/dist/tools/web-fetch.d.ts.map +1 -0
  255. package/dist/tools/web-fetch.js +106 -0
  256. package/dist/tools/web-fetch.js.map +1 -0
  257. package/dist/tools/web-search.d.ts +10 -0
  258. package/dist/tools/web-search.d.ts.map +1 -0
  259. package/dist/tools/web-search.js +106 -0
  260. package/dist/tools/web-search.js.map +1 -0
  261. package/dist/utils/gitignore.d.ts +10 -0
  262. package/dist/utils/gitignore.d.ts.map +1 -0
  263. package/dist/utils/gitignore.js +104 -0
  264. package/dist/utils/gitignore.js.map +1 -0
  265. package/dist/utils/lazy-apply.d.ts +45 -0
  266. package/dist/utils/lazy-apply.d.ts.map +1 -0
  267. package/dist/utils/lazy-apply.js +164 -0
  268. package/dist/utils/lazy-apply.js.map +1 -0
  269. package/dist/utils/memory.d.ts +36 -0
  270. package/dist/utils/memory.d.ts.map +1 -0
  271. package/dist/utils/memory.js +136 -0
  272. package/dist/utils/memory.js.map +1 -0
  273. package/dist/utils/path-matching.d.ts +24 -0
  274. package/dist/utils/path-matching.d.ts.map +1 -0
  275. package/dist/utils/path-matching.js +116 -0
  276. package/dist/utils/path-matching.js.map +1 -0
  277. package/dist/utils/path-safety.d.ts +13 -0
  278. package/dist/utils/path-safety.d.ts.map +1 -0
  279. package/dist/utils/path-safety.js +54 -0
  280. package/dist/utils/path-safety.js.map +1 -0
  281. package/dist/utils/project-config.d.ts +18 -0
  282. package/dist/utils/project-config.d.ts.map +1 -0
  283. package/dist/utils/project-config.js +76 -0
  284. package/dist/utils/project-config.js.map +1 -0
  285. package/dist/utils/search-match.d.ts +63 -0
  286. package/dist/utils/search-match.d.ts.map +1 -0
  287. package/dist/utils/search-match.js +426 -0
  288. package/dist/utils/search-match.js.map +1 -0
  289. package/dist/utils/shell-paths.d.ts +17 -0
  290. package/dist/utils/shell-paths.d.ts.map +1 -0
  291. package/dist/utils/shell-paths.js +107 -0
  292. package/dist/utils/shell-paths.js.map +1 -0
  293. package/dist/utils/streaming-diff.d.ts +45 -0
  294. package/dist/utils/streaming-diff.d.ts.map +1 -0
  295. package/dist/utils/streaming-diff.js +230 -0
  296. package/dist/utils/streaming-diff.js.map +1 -0
  297. package/dist/utils/todo.d.ts +47 -0
  298. package/dist/utils/todo.d.ts.map +1 -0
  299. package/dist/utils/todo.js +102 -0
  300. package/dist/utils/todo.js.map +1 -0
  301. package/package.json +23 -0
  302. package/src/agents/coordinator.ts +240 -0
  303. package/src/context-providers/clipboard.ts +48 -0
  304. package/src/context-providers/codebase.ts +274 -0
  305. package/src/context-providers/diff.ts +66 -0
  306. package/src/context-providers/docs.ts +160 -0
  307. package/src/context-providers/file-include.ts +54 -0
  308. package/src/context-providers/folder.ts +106 -0
  309. package/src/context-providers/git.ts +72 -0
  310. package/src/context-providers/index.ts +26 -0
  311. package/src/context-providers/open-files.ts +113 -0
  312. package/src/context-providers/problems.ts +100 -0
  313. package/src/context-providers/registry.ts +99 -0
  314. package/src/context-providers/terminal.ts +58 -0
  315. package/src/context-providers/tree.ts +161 -0
  316. package/src/context-providers/types.ts +84 -0
  317. package/src/context-providers/url.ts +138 -0
  318. package/src/effort/index.ts +177 -0
  319. package/src/hooks/index.ts +148 -0
  320. package/src/index.ts +114 -0
  321. package/src/indexing/README.md +267 -0
  322. package/src/indexing/chunker.ts +206 -0
  323. package/src/indexing/database.ts +299 -0
  324. package/src/indexing/index.ts +15 -0
  325. package/src/indexing/indexer.ts +383 -0
  326. package/src/indexing/recent-edits-cache.ts +150 -0
  327. package/src/indexing/types.ts +44 -0
  328. package/src/mcp/index.ts +33 -0
  329. package/src/mcp/manager.ts +385 -0
  330. package/src/oauth.ts +330 -0
  331. package/src/permissions/index.ts +1011 -0
  332. package/src/plan/index.ts +20 -0
  333. package/src/plan/manager.ts +233 -0
  334. package/src/rules/index.ts +28 -0
  335. package/src/rules/manager.ts +276 -0
  336. package/src/rules/types.ts +40 -0
  337. package/src/sandbox/filesystem.ts +135 -0
  338. package/src/sandbox/index.ts +9 -0
  339. package/src/sandbox/manager.ts +213 -0
  340. package/src/sandbox/network.ts +101 -0
  341. package/src/sandbox/types.ts +63 -0
  342. package/src/tools/ast-edit.ts +493 -0
  343. package/src/tools/code-verify.ts +143 -0
  344. package/src/tools/codebase-search.ts +117 -0
  345. package/src/tools/file-delete.ts +155 -0
  346. package/src/tools/file-edit.ts +115 -0
  347. package/src/tools/file-read.ts +195 -0
  348. package/src/tools/file-run.ts +158 -0
  349. package/src/tools/file-write.ts +104 -0
  350. package/src/tools/glob-search.ts +80 -0
  351. package/src/tools/grep-search.ts +120 -0
  352. package/src/tools/list-dir.ts +172 -0
  353. package/src/tools/multi-edit.ts +138 -0
  354. package/src/tools/notebook-edit.ts +342 -0
  355. package/src/tools/registry.ts +43 -0
  356. package/src/tools/shell-exec.ts +251 -0
  357. package/src/tools/sub-agent-manager.ts +183 -0
  358. package/src/tools/sub-agent-status.ts +67 -0
  359. package/src/tools/sub-agent-terminate.ts +62 -0
  360. package/src/tools/sub-agent.ts +162 -0
  361. package/src/tools/system-info.ts +248 -0
  362. package/src/tools/todo.ts +149 -0
  363. package/src/tools/types.ts +21 -0
  364. package/src/tools/view-diff.ts +99 -0
  365. package/src/tools/view-repo-map.ts +249 -0
  366. package/src/tools/web-fetch.ts +118 -0
  367. package/src/tools/web-search.ts +129 -0
  368. package/src/utils/gitignore.ts +73 -0
  369. package/src/utils/lazy-apply.ts +189 -0
  370. package/src/utils/memory.ts +124 -0
  371. package/src/utils/path-matching.ts +84 -0
  372. package/src/utils/path-safety.ts +19 -0
  373. package/src/utils/project-config.ts +41 -0
  374. package/src/utils/search-match.ts +495 -0
  375. package/src/utils/shell-paths.ts +79 -0
  376. package/src/utils/streaming-diff.ts +260 -0
  377. package/src/utils/todo.ts +115 -0
  378. package/tsconfig.json +18 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Codebase Search Tool — semantic + full-text search across the indexed codebase.
3
+ *
4
+ * Uses the indexing system (SQLite FTS5 + optional embeddings) for fast,
5
+ * ranked code retrieval. Falls back to runtime grep if index not available.
6
+ */
7
+
8
+ import type { BaseTool, ToolDefinition, ToolResult } from "./types";
9
+ import { CodebaseIndexer } from "../indexing";
10
+
11
+ export class CodebaseSearchTool implements BaseTool {
12
+ definition: ToolDefinition = {
13
+ name: "codebase_search",
14
+ description:
15
+ `Search the entire codebase using natural language or keywords. Uses indexed full-text search (BM25) and optional semantic search (embeddings) for fast, ranked results.
16
+
17
+ The codebase is automatically indexed on first use. Results include file paths, line numbers, and matching code snippets ranked by relevance.
18
+
19
+ Use this for:
20
+ - Finding where a concept is implemented ("authentication middleware")
21
+ - Locating function/class definitions ("UserService class")
22
+ - Understanding how something works ("how errors are handled")
23
+ - Finding usage patterns ("all places that call sendEmail")`,
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ query: {
28
+ type: "string",
29
+ description: "Natural language query or keywords to search for",
30
+ },
31
+ directory: {
32
+ type: "string",
33
+ description: "Optional: limit search to a subdirectory (e.g. 'src/api')",
34
+ },
35
+ limit: {
36
+ type: "number",
37
+ description: "Max results to return. Default: 20.",
38
+ },
39
+ },
40
+ required: ["query"],
41
+ },
42
+ requiresPermission: false,
43
+ };
44
+
45
+ private indexer: CodebaseIndexer | null = null;
46
+ private workingDir: string;
47
+ private indexPromise: Promise<void> | null = null;
48
+
49
+ constructor(workingDir: string) {
50
+ this.workingDir = workingDir;
51
+ }
52
+
53
+ async execute(input: Record<string, unknown>): Promise<ToolResult> {
54
+ const query = input.query as string;
55
+ const directory = input.directory as string | undefined;
56
+ const limit = (input.limit as number) || 20;
57
+
58
+ if (!query.trim()) {
59
+ return { success: false, output: "", error: "Query cannot be empty" };
60
+ }
61
+
62
+ // Ensure index exists (lazy initialization)
63
+ try {
64
+ await this.ensureIndex();
65
+ } catch (err) {
66
+ return { success: false, output: "", error: `Indexing failed: ${(err as Error).message}` };
67
+ }
68
+
69
+ if (!this.indexer) {
70
+ return { success: false, output: "", error: "Indexer not available" };
71
+ }
72
+
73
+ // Search
74
+ const results = await this.indexer.search(query, limit, directory);
75
+
76
+ if (results.length === 0) {
77
+ return { success: true, output: `No results found for: "${query}"` };
78
+ }
79
+
80
+ // Format results
81
+ const formatted = results.map((r, i) => {
82
+ const header = `### ${i + 1}. ${r.path}:${r.startLine}-${r.endLine} (${r.source}, score: ${r.score.toFixed(3)})`;
83
+ const content = r.content.length > 500
84
+ ? r.content.substring(0, 500) + "\n... [truncated]"
85
+ : r.content;
86
+ return `${header}\n\`\`\`\n${content}\n\`\`\``;
87
+ });
88
+
89
+ const stats = this.indexer.getStats();
90
+ const header = `Found ${results.length} results for "${query}" (searched ${stats.totalChunks} chunks across ${stats.totalFiles} files)`;
91
+
92
+ return { success: true, output: `${header}\n\n${formatted.join("\n\n")}` };
93
+ }
94
+
95
+ private async ensureIndex(): Promise<void> {
96
+ if (this.indexer) return;
97
+
98
+ // Prevent concurrent indexing
99
+ if (this.indexPromise) {
100
+ await this.indexPromise;
101
+ return;
102
+ }
103
+
104
+ this.indexPromise = (async () => {
105
+ this.indexer = new CodebaseIndexer(this.workingDir);
106
+ const stats = this.indexer.getStats();
107
+
108
+ // If index is empty or stale (>1 hour), re-index
109
+ const oneHour = 60 * 60 * 1000;
110
+ if (stats.totalFiles === 0 || Date.now() - stats.lastIndexed > oneHour) {
111
+ await this.indexer.index();
112
+ }
113
+ })();
114
+
115
+ await this.indexPromise;
116
+ }
117
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * File Delete Tool — permission-controlled file/directory deletion.
3
+ *
4
+ * Unlike raw `rm` via shell_exec, this tool:
5
+ * - Goes through the permission system (Delete category — can be allow/deny/ask via settings)
6
+ * - Checks sandbox write rules before deletion
7
+ * - Reports what will be deleted (file count for directories)
8
+ * - Requires explicit recursive flag for directories
9
+ * - Requires force flag for large directories (>50 items)
10
+ *
11
+ * Users can control deletion via settings rules:
12
+ * - Delete → allow/deny all file deletions
13
+ * - Delete(path) → allow/deny deletion of specific paths
14
+ * - Delete(*.log) → allow/deny deletion by pattern
15
+ */
16
+
17
+ import * as fs from "fs";
18
+ import * as path from "path";
19
+ import type { BaseTool, ToolDefinition, ToolResult } from "./types";
20
+ import { safePath } from "../utils/path-safety";
21
+ import type { SandboxManager } from "../sandbox";
22
+
23
+ const MAX_DIR_FILES = 50;
24
+
25
+ export class FileDeleteTool implements BaseTool {
26
+ definition: ToolDefinition = {
27
+ name: "file_delete",
28
+ description:
29
+ `Delete a file or directory. You can also use shell_exec with rm/del for simple deletions if preferred.`,
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ file_path: {
34
+ type: "string",
35
+ description: "Path to the file or directory to delete",
36
+ },
37
+ recursive: {
38
+ type: "boolean",
39
+ description: "Required for directory deletion. Deletes all contents. Default: false.",
40
+ },
41
+ force: {
42
+ type: "boolean",
43
+ description: "Required for directories with more than 50 items. Default: false.",
44
+ },
45
+ },
46
+ required: ["file_path"],
47
+ },
48
+ requiresPermission: true,
49
+ permissionMessage: (input) => {
50
+ const recursive = input.recursive ? " (recursive)" : "";
51
+ return `Delete${recursive}: ${input.file_path}`;
52
+ },
53
+ };
54
+
55
+ private workingDir: string;
56
+ private sandboxManager?: SandboxManager;
57
+
58
+ constructor(workingDir: string, sandboxManager?: SandboxManager) {
59
+ this.workingDir = workingDir;
60
+ this.sandboxManager = sandboxManager;
61
+ }
62
+
63
+ async execute(input: Record<string, unknown>): Promise<ToolResult> {
64
+ let filePath: string;
65
+ try {
66
+ filePath = safePath(input.file_path as string, this.workingDir);
67
+ } catch (err) {
68
+ return { success: false, output: "", error: (err as Error).message };
69
+ }
70
+
71
+ const recursive = (input.recursive as boolean) || false;
72
+ const force = (input.force as boolean) || false;
73
+
74
+ // Sandbox write check (deletion requires write access to parent)
75
+ if (this.sandboxManager) {
76
+ const check = this.sandboxManager.checkFileWrite(filePath);
77
+ if (!check.allowed) {
78
+ return { success: false, output: "", error: check.reason || "Sandbox: delete access denied" };
79
+ }
80
+ }
81
+
82
+ // Existence check
83
+ if (!fs.existsSync(filePath)) {
84
+ return { success: false, output: "", error: `Path not found: ${filePath}` };
85
+ }
86
+
87
+ const stat = fs.statSync(filePath);
88
+
89
+ // ── File deletion ─────────────────────────────────────────────────────────
90
+ if (stat.isFile() || stat.isSymbolicLink()) {
91
+ try {
92
+ fs.unlinkSync(filePath);
93
+ return { success: true, output: `Deleted file: ${filePath}` };
94
+ } catch (err) {
95
+ return { success: false, output: "", error: `Failed to delete: ${(err as Error).message}` };
96
+ }
97
+ }
98
+
99
+ // ── Directory deletion ────────────────────────────────────────────────────
100
+ if (!stat.isDirectory()) {
101
+ return { success: false, output: "", error: `Unsupported file type at: ${filePath}` };
102
+ }
103
+
104
+ if (!recursive) {
105
+ // Show what's inside so the user can make an informed decision
106
+ const entries = fs.readdirSync(filePath);
107
+ const preview = entries.slice(0, 10).join(", ");
108
+ const more = entries.length > 10 ? ` ... and ${entries.length - 10} more` : "";
109
+ return {
110
+ success: false,
111
+ output: "",
112
+ error: `"${filePath}" is a directory with ${entries.length} items (${preview}${more}). Set recursive=true to delete it and all its contents.`,
113
+ };
114
+ }
115
+
116
+ // Count files before deleting
117
+ const fileCount = this.countItems(filePath);
118
+
119
+ if (fileCount > MAX_DIR_FILES && !force) {
120
+ return {
121
+ success: false,
122
+ output: "",
123
+ error: `Directory contains ${fileCount} items. Set force=true to confirm bulk deletion of "${filePath}".`,
124
+ };
125
+ }
126
+
127
+ try {
128
+ fs.rmSync(filePath, { recursive: true, force: true });
129
+ return {
130
+ success: true,
131
+ output: `Deleted directory: ${filePath} (${fileCount} items removed)`,
132
+ };
133
+ } catch (err) {
134
+ return { success: false, output: "", error: `Failed to delete directory: ${(err as Error).message}` };
135
+ }
136
+ }
137
+
138
+ private countItems(dirPath: string, limit = 10000): number {
139
+ let count = 0;
140
+ try {
141
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ count++;
144
+ if (count >= limit) return count;
145
+ if (entry.isDirectory()) {
146
+ count += this.countItems(path.join(dirPath, entry.name), limit - count);
147
+ if (count >= limit) return count;
148
+ }
149
+ }
150
+ } catch {
151
+ // Permission denied — count what we can
152
+ }
153
+ return count;
154
+ }
155
+ }
@@ -0,0 +1,115 @@
1
+ import * as fs from "fs";
2
+ import type { BaseTool, ToolDefinition, ToolResult } from "./types";
3
+ import { safePath } from "../utils/path-safety";
4
+ import { executeFindAndReplace, isUnifiedDiff, applyUnifiedDiff } from "../utils/search-match";
5
+ import { hasPlaceholders, expandPlaceholders } from "../utils/lazy-apply";
6
+ import type { SandboxManager } from "../sandbox";
7
+
8
+ export class FileEditTool implements BaseTool {
9
+ definition: ToolDefinition = {
10
+ name: "file_edit",
11
+ description:
12
+ `Performs string replacement in a file using multi-strategy matching (exact → trimmed → case-insensitive → whitespace-ignored). Also supports unified diff patches. Requires user permission. Access controlled by sandbox/permission rules.
13
+
14
+ IMPORTANT:
15
+ - Always read the file first before editing to see its current contents.
16
+ - old_string should match the file content — the system tolerates minor whitespace/case differences.
17
+ - old_string and new_string MUST be different.
18
+ - When not using replace_all, old_string must be unique in the file. Add more surrounding context if ambiguous.
19
+ - Use replace_all for renaming variables or strings across the file.
20
+ - You can pass a unified diff (with @@ hunk headers) as old_string and leave new_string empty to apply the diff patch directly.
21
+ - This tool CANNOT be called in parallel with itself on the SAME file.`,
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ file_path: { type: "string", description: "Path to the file to edit" },
26
+ old_string: { type: "string", description: "The text to replace — must match file content (exact match preferred, but tolerates whitespace/case differences)" },
27
+ new_string: { type: "string", description: "The replacement text (MUST be different from old_string)" },
28
+ replace_all: { type: "boolean", description: "Replace all occurrences (default: false)" },
29
+ },
30
+ required: ["file_path", "old_string", "new_string"],
31
+ },
32
+ requiresPermission: true,
33
+ permissionMessage: (input) => `Edit file: ${input.file_path}`,
34
+ };
35
+
36
+ private workingDir: string;
37
+ private sandboxManager?: SandboxManager;
38
+
39
+ constructor(workingDir: string, sandboxManager?: SandboxManager) {
40
+ this.workingDir = workingDir;
41
+ this.sandboxManager = sandboxManager;
42
+ }
43
+
44
+ async execute(input: Record<string, unknown>): Promise<ToolResult> {
45
+ let filePath: string;
46
+ try {
47
+ filePath = safePath(input.file_path as string, this.workingDir);
48
+ } catch (err) {
49
+ return { success: false, output: "", error: (err as Error).message };
50
+ }
51
+
52
+ // Sandbox write check
53
+ if (this.sandboxManager) {
54
+ const check = this.sandboxManager.checkFileWrite(filePath);
55
+ if (!check.allowed) {
56
+ return { success: false, output: "", error: check.reason || "Sandbox: write access denied" };
57
+ }
58
+ }
59
+
60
+ const oldStr = input.old_string as string;
61
+ const newStr = input.new_string as string;
62
+ const replaceAll = (input.replace_all as boolean) || false;
63
+
64
+ if (!fs.existsSync(filePath))
65
+ return { success: false, output: "", error: `File not found: ${filePath}` };
66
+
67
+ const content = fs.readFileSync(filePath, "utf-8");
68
+
69
+ try {
70
+ // Unified diff mode: detect @@ hunk headers in old_string
71
+ if (isUnifiedDiff(oldStr)) {
72
+ const result = applyUnifiedDiff(content, oldStr);
73
+ fs.writeFileSync(filePath, result, "utf-8");
74
+ return { success: true, output: `Applied unified diff patch to ${filePath}\n\n${oldStr}` };
75
+ }
76
+
77
+ // Lazy apply: expand placeholder markers in new_string before replacing
78
+ let expandedNewStr = newStr;
79
+ let lazyNote = "";
80
+ if (hasPlaceholders(newStr)) {
81
+ const { content: expanded, placeholdersExpanded, strategy: lazyStrategy } = expandPlaceholders(content, newStr);
82
+ if (placeholdersExpanded > 0) {
83
+ expandedNewStr = expanded;
84
+ lazyNote = ` (lazy apply: ${placeholdersExpanded} placeholder(s) expanded via ${lazyStrategy})`;
85
+ }
86
+ }
87
+
88
+ const { result, count, strategy } = executeFindAndReplace(content, oldStr, expandedNewStr, replaceAll);
89
+ fs.writeFileSync(filePath, result, "utf-8");
90
+
91
+ const diff = generateDiff(filePath, oldStr, expandedNewStr, count);
92
+ const strategyNote = strategy !== "exact" ? ` (matched via ${strategy} strategy)` : "";
93
+ return { success: true, output: `Edited ${filePath}: replaced ${count} occurrence(s)${strategyNote}${lazyNote}\n\n${diff}` };
94
+ } catch (err) {
95
+ return { success: false, output: "", error: (err as Error).message };
96
+ }
97
+ }
98
+ }
99
+
100
+ /** Generate a unified-style diff showing what changed */
101
+ function generateDiff(filePath: string, oldStr: string, newStr: string, count: number): string {
102
+ const oldLines = oldStr.split("\n");
103
+ const newLines = newStr.split("\n");
104
+
105
+ const parts: string[] = [
106
+ `--- a/${filePath}`,
107
+ `+++ b/${filePath}`,
108
+ `@@ -1,${oldLines.length} +1,${newLines.length} @@ (${count} replacement${count > 1 ? "s" : ""})`,
109
+ ];
110
+
111
+ for (const line of oldLines) parts.push(`- ${line}`);
112
+ for (const line of newLines) parts.push(`+ ${line}`);
113
+
114
+ return parts.join("\n");
115
+ }
@@ -0,0 +1,195 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import type { BaseTool, ToolDefinition, ToolResult } from "./types";
4
+ import { safePath } from "../utils/path-safety";
5
+ import type { SandboxManager } from "../sandbox";
6
+
7
+ /** Binary/image extensions we can describe but not read as text */
8
+ const IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".svg", ".ico"]);
9
+ const PDF_EXTENSION = ".pdf";
10
+
11
+ export class FileReadTool implements BaseTool {
12
+ definition: ToolDefinition = {
13
+ name: "file_read",
14
+ description:
15
+ "Read the contents of a file. Returns content with line numbers. Supports text files, images (returns metadata), and PDFs (extracts text). Always read before editing. Access is governed by the permission system — certain paths may be denied by sandbox or permission rules.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ file_path: {
20
+ type: "string",
21
+ description: "Path to the file to read",
22
+ },
23
+ offset: {
24
+ type: "number",
25
+ description: "Line number to start from (1-based). Optional.",
26
+ },
27
+ limit: {
28
+ type: "number",
29
+ description: "Max lines to read. Default: 2000.",
30
+ },
31
+ },
32
+ required: ["file_path"],
33
+ },
34
+ requiresPermission: false,
35
+ };
36
+
37
+ private workingDir: string;
38
+ private sandboxManager?: SandboxManager;
39
+
40
+ constructor(workingDir: string, sandboxManager?: SandboxManager) {
41
+ this.workingDir = workingDir;
42
+ this.sandboxManager = sandboxManager;
43
+ }
44
+
45
+ async execute(input: Record<string, unknown>): Promise<ToolResult> {
46
+ let filePath: string;
47
+ try {
48
+ filePath = safePath(input.file_path as string, this.workingDir);
49
+ } catch (err) {
50
+ return { success: false, output: "", error: (err as Error).message };
51
+ }
52
+
53
+ // Sandbox read check
54
+ if (this.sandboxManager) {
55
+ const check = this.sandboxManager.checkFileRead(filePath);
56
+ if (!check.allowed) {
57
+ return { success: false, output: "", error: check.reason || "Sandbox: read access denied" };
58
+ }
59
+ }
60
+
61
+ if (!fs.existsSync(filePath))
62
+ return { success: false, output: "", error: `File not found: ${filePath}` };
63
+ if (fs.statSync(filePath).isDirectory())
64
+ return { success: false, output: "", error: `Path is a directory: ${filePath}` };
65
+
66
+ const ext = path.extname(filePath).toLowerCase();
67
+
68
+ // Handle image files
69
+ if (IMAGE_EXTENSIONS.has(ext)) {
70
+ return this.readImage(filePath, ext);
71
+ }
72
+
73
+ // Handle PDF files
74
+ if (ext === PDF_EXTENSION) {
75
+ return this.readPdf(filePath);
76
+ }
77
+
78
+ // Regular text file
79
+ const offset = (input.offset as number) || 1;
80
+ const limit = (input.limit as number) || 2000;
81
+
82
+ try {
83
+ const content = fs.readFileSync(filePath, "utf-8");
84
+ const lines = content.split("\n");
85
+ const selected = lines.slice(offset - 1, offset - 1 + limit);
86
+ const numbered = selected
87
+ .map((line, i) => `${String(offset + i).padStart(5)} ${line}`)
88
+ .join("\n");
89
+
90
+ const totalLines = lines.length;
91
+ const showing = selected.length;
92
+ const info = totalLines > showing
93
+ ? `\n\n[Showing lines ${offset}-${offset + showing - 1} of ${totalLines}]`
94
+ : "";
95
+
96
+ return { success: true, output: (numbered || "(empty file)") + info };
97
+ } catch {
98
+ // Might be a binary file
99
+ const stat = fs.statSync(filePath);
100
+ return {
101
+ success: true,
102
+ output: `Binary file: ${filePath} (${formatBytes(stat.size)})`,
103
+ };
104
+ }
105
+ }
106
+
107
+ private readImage(filePath: string, ext: string): ToolResult {
108
+ const stat = fs.statSync(filePath);
109
+ const info = [
110
+ `Image file: ${filePath}`,
111
+ `Format: ${ext.substring(1).toUpperCase()}`,
112
+ `Size: ${formatBytes(stat.size)}`,
113
+ ];
114
+
115
+ // For SVG, we can actually read the content
116
+ if (ext === ".svg") {
117
+ try {
118
+ const content = fs.readFileSync(filePath, "utf-8");
119
+ info.push(`\nSVG Content:\n${content.substring(0, 5000)}`);
120
+ if (content.length > 5000) info.push(`\n... [truncated at 5000 chars]`);
121
+ } catch {
122
+ // skip
123
+ }
124
+ }
125
+
126
+ return { success: true, output: info.join("\n") };
127
+ }
128
+
129
+ private readPdf(filePath: string): ToolResult {
130
+ const stat = fs.statSync(filePath);
131
+
132
+ // Basic PDF text extraction — reads raw text strings from PDF
133
+ try {
134
+ const buffer = fs.readFileSync(filePath);
135
+ const text = extractPdfText(buffer);
136
+
137
+ if (text.trim()) {
138
+ const truncated = text.length > 10000
139
+ ? text.substring(0, 10000) + "\n\n... [truncated at 10000 chars]"
140
+ : text;
141
+ return {
142
+ success: true,
143
+ output: `PDF: ${filePath} (${formatBytes(stat.size)})\n\n${truncated}`,
144
+ };
145
+ }
146
+
147
+ return {
148
+ success: true,
149
+ output: `PDF: ${filePath} (${formatBytes(stat.size)})\n(Could not extract text — may be image-based or encrypted)`,
150
+ };
151
+ } catch {
152
+ return {
153
+ success: true,
154
+ output: `PDF: ${filePath} (${formatBytes(stat.size)})\n(Unable to read PDF content)`,
155
+ };
156
+ }
157
+ }
158
+ }
159
+
160
+ /** Very basic PDF text extraction — finds text between parentheses in PDF stream */
161
+ function extractPdfText(buffer: Buffer): string {
162
+ const content = buffer.toString("latin1");
163
+ const textParts: string[] = [];
164
+
165
+ // Extract text from Tj and TJ operators (basic PDF text rendering)
166
+ const tjRegex = /\(([^)]*)\)\s*Tj/g;
167
+ let match;
168
+ while ((match = tjRegex.exec(content)) !== null) {
169
+ const decoded = match[1]
170
+ .replace(/\\n/g, "\n")
171
+ .replace(/\\r/g, "\r")
172
+ .replace(/\\t/g, "\t")
173
+ .replace(/\\\\/g, "\\")
174
+ .replace(/\\([()])/g, "$1");
175
+ textParts.push(decoded);
176
+ }
177
+
178
+ // Also try TJ arrays
179
+ const tjArrayRegex = /\[((?:\([^)]*\)|[^\]])*)\]\s*TJ/gi;
180
+ while ((match = tjArrayRegex.exec(content)) !== null) {
181
+ const innerRegex = /\(([^)]*)\)/g;
182
+ let inner;
183
+ while ((inner = innerRegex.exec(match[1])) !== null) {
184
+ textParts.push(inner[1]);
185
+ }
186
+ }
187
+
188
+ return textParts.join(" ").replace(/\s+/g, " ").trim();
189
+ }
190
+
191
+ function formatBytes(bytes: number): string {
192
+ if (bytes < 1024) return `${bytes} B`;
193
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
194
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
195
+ }