@ebowwa/coder 0.2.1 → 0.7.64

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 (401) hide show
  1. package/README.md +31 -32
  2. package/dist/core/__tests__/permissions.test.d.ts +12 -0
  3. package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
  4. package/dist/core/__tests__/permissions.test.js +851 -0
  5. package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
  6. package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
  7. package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
  8. package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
  9. package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
  10. package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
  11. package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
  12. package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
  13. package/dist/core/agent-loop/__tests__/index.test.js +121 -0
  14. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
  15. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
  16. package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
  17. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
  18. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
  19. package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
  20. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
  21. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
  22. package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
  23. package/dist/core/agent-loop/compaction.d.ts +39 -0
  24. package/dist/core/agent-loop/compaction.d.ts.map +1 -0
  25. package/dist/core/agent-loop/compaction.js +51 -0
  26. package/dist/core/agent-loop/formatters.d.ts +21 -0
  27. package/dist/core/agent-loop/formatters.d.ts.map +1 -0
  28. package/dist/core/agent-loop/formatters.js +42 -0
  29. package/dist/core/agent-loop/index.d.ts +25 -0
  30. package/dist/core/agent-loop/index.d.ts.map +1 -0
  31. package/dist/core/agent-loop/index.js +83 -0
  32. package/dist/core/agent-loop/loop-state.d.ts +74 -0
  33. package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
  34. package/dist/core/agent-loop/loop-state.js +147 -0
  35. package/dist/core/agent-loop/message-builder.d.ts +13 -0
  36. package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
  37. package/dist/core/agent-loop/message-builder.js +49 -0
  38. package/dist/core/agent-loop/tool-executor.d.ts +23 -0
  39. package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
  40. package/dist/core/agent-loop/tool-executor.js +152 -0
  41. package/dist/core/agent-loop/turn-executor.d.ts +57 -0
  42. package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
  43. package/dist/core/agent-loop/turn-executor.js +124 -0
  44. package/dist/core/agent-loop/types.d.ts +141 -0
  45. package/dist/core/agent-loop/types.d.ts.map +1 -0
  46. package/dist/core/agent-loop/types.js +4 -0
  47. package/dist/core/agent-loop.d.ts +17 -0
  48. package/dist/core/agent-loop.d.ts.map +1 -0
  49. package/dist/core/agent-loop.js +16 -0
  50. package/dist/core/api-client-impl.d.ts +62 -0
  51. package/dist/core/api-client-impl.d.ts.map +1 -0
  52. package/dist/core/api-client-impl.js +479 -0
  53. package/dist/core/api-client.d.ts +6 -0
  54. package/dist/core/api-client.d.ts.map +1 -0
  55. package/dist/core/api-client.js +5 -0
  56. package/dist/core/checkpoints.d.ts +128 -0
  57. package/dist/core/checkpoints.d.ts.map +1 -0
  58. package/dist/core/checkpoints.js +438 -0
  59. package/dist/core/claude-md.d.ts +71 -0
  60. package/dist/core/claude-md.d.ts.map +1 -0
  61. package/dist/core/claude-md.js +198 -0
  62. package/dist/core/cognitive-security/hooks.d.ts +138 -0
  63. package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
  64. package/dist/core/cognitive-security/hooks.js +389 -0
  65. package/dist/core/cognitive-security/index.d.ts +751 -0
  66. package/dist/core/cognitive-security/index.d.ts.map +1 -0
  67. package/dist/core/cognitive-security/index.js +1123 -0
  68. package/dist/core/cognitive-security/middleware.d.ts +136 -0
  69. package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
  70. package/dist/core/cognitive-security/middleware.js +376 -0
  71. package/dist/core/config-loader.d.ts +127 -0
  72. package/dist/core/config-loader.d.ts.map +1 -0
  73. package/dist/core/config-loader.js +219 -0
  74. package/dist/core/context-compaction.d.ts +87 -0
  75. package/dist/core/context-compaction.d.ts.map +1 -0
  76. package/dist/core/context-compaction.js +428 -0
  77. package/dist/core/git-status.d.ts +25 -0
  78. package/dist/core/git-status.d.ts.map +1 -0
  79. package/dist/core/git-status.js +204 -0
  80. package/dist/core/image.d.ts +69 -0
  81. package/dist/core/image.d.ts.map +1 -0
  82. package/dist/core/image.js +290 -0
  83. package/dist/core/image.test.d.ts +2 -0
  84. package/dist/core/image.test.d.ts.map +1 -0
  85. package/dist/core/image.test.js +149 -0
  86. package/dist/core/models.d.ts +123 -0
  87. package/dist/core/models.d.ts.map +1 -0
  88. package/dist/core/models.js +325 -0
  89. package/dist/core/permissions.d.ts +81 -0
  90. package/dist/core/permissions.d.ts.map +1 -0
  91. package/dist/core/permissions.js +327 -0
  92. package/dist/core/retry.d.ts +25 -0
  93. package/dist/core/retry.d.ts.map +1 -0
  94. package/dist/core/retry.js +121 -0
  95. package/dist/core/session-store.d.ts +9 -0
  96. package/dist/core/session-store.d.ts.map +1 -0
  97. package/dist/core/session-store.js +10 -0
  98. package/dist/core/sessions/export.d.ts +47 -0
  99. package/dist/core/sessions/export.d.ts.map +1 -0
  100. package/dist/core/sessions/export.js +256 -0
  101. package/dist/core/sessions/index.d.ts +132 -0
  102. package/dist/core/sessions/index.d.ts.map +1 -0
  103. package/dist/core/sessions/index.js +442 -0
  104. package/dist/core/sessions/metadata.d.ts +77 -0
  105. package/dist/core/sessions/metadata.d.ts.map +1 -0
  106. package/dist/core/sessions/metadata.js +233 -0
  107. package/dist/core/sessions/persistence.d.ts +72 -0
  108. package/dist/core/sessions/persistence.d.ts.map +1 -0
  109. package/dist/core/sessions/persistence.js +201 -0
  110. package/dist/core/sessions/types.d.ts +110 -0
  111. package/dist/core/sessions/types.d.ts.map +1 -0
  112. package/dist/core/sessions/types.js +4 -0
  113. package/dist/core/stream-highlighter.d.ts +18 -0
  114. package/dist/core/stream-highlighter.d.ts.map +1 -0
  115. package/dist/core/stream-highlighter.js +916 -0
  116. package/dist/core/system-reminders.d.ts +89 -0
  117. package/dist/core/system-reminders.d.ts.map +1 -0
  118. package/dist/core/system-reminders.js +285 -0
  119. package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
  120. package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
  121. package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
  122. package/dist/ecosystem/hooks/index.d.ts +59 -0
  123. package/dist/ecosystem/hooks/index.d.ts.map +1 -0
  124. package/dist/ecosystem/hooks/index.js +294 -0
  125. package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
  126. package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
  127. package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
  128. package/dist/ecosystem/skills/index.d.ts +55 -0
  129. package/dist/ecosystem/skills/index.d.ts.map +1 -0
  130. package/dist/ecosystem/skills/index.js +258 -0
  131. package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
  132. package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
  133. package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
  134. package/dist/ecosystem/tools/index.d.ts +24 -0
  135. package/dist/ecosystem/tools/index.d.ts.map +1 -0
  136. package/dist/ecosystem/tools/index.js +1709 -0
  137. package/dist/index.d.ts +24 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +32 -2
  140. package/dist/interfaces/mcp/client.d.ts +40 -0
  141. package/dist/interfaces/mcp/client.d.ts.map +1 -0
  142. package/dist/interfaces/mcp/client.js +309 -0
  143. package/dist/interfaces/ui/index.d.ts +36 -0
  144. package/dist/interfaces/ui/index.d.ts.map +1 -0
  145. package/dist/interfaces/ui/index.js +61 -0
  146. package/dist/interfaces/ui/spinner.d.ts +140 -0
  147. package/dist/interfaces/ui/spinner.d.ts.map +1 -0
  148. package/dist/interfaces/ui/spinner.js +342 -0
  149. package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
  150. package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
  151. package/dist/interfaces/ui/terminal/cli/index.js +167 -0
  152. package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
  153. package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
  154. package/dist/interfaces/ui/terminal/shared/args.js +176 -0
  155. package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
  156. package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
  157. package/dist/interfaces/ui/terminal/shared/index.js +16 -0
  158. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
  159. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
  160. package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
  161. package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
  162. package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
  163. package/dist/interfaces/ui/terminal/shared/query.js +100 -0
  164. package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
  165. package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
  166. package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
  167. package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
  168. package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
  169. package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
  170. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
  171. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
  172. package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
  173. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
  174. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
  175. package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
  176. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
  177. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
  178. package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
  179. package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
  180. package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
  181. package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
  182. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
  183. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
  184. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
  185. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
  186. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
  187. package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
  188. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
  189. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
  190. package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
  191. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
  192. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
  193. package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
  194. package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
  195. package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
  196. package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
  197. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
  198. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
  199. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
  200. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
  201. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
  202. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
  203. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
  204. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
  205. package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
  206. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
  207. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
  208. package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
  209. package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
  210. package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
  211. package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
  212. package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
  213. package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
  214. package/dist/interfaces/ui/terminal/tui/console.js +46 -0
  215. package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
  216. package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
  217. package/dist/interfaces/ui/terminal/tui/index.js +28 -0
  218. package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
  219. package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
  220. package/dist/interfaces/ui/terminal/tui/run.js +31 -0
  221. package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
  222. package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
  223. package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
  224. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
  225. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
  226. package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
  227. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
  228. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
  229. package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
  230. package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
  231. package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
  232. package/dist/interfaces/ui/terminal/tui/types.js +5 -0
  233. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
  234. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
  235. package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
  236. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
  237. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
  238. package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
  239. package/dist/native/index.d.ts +480 -0
  240. package/dist/native/index.d.ts.map +1 -0
  241. package/dist/native/index.js +1625 -0
  242. package/dist/teammates/index.d.ts +161 -0
  243. package/dist/teammates/index.d.ts.map +1 -0
  244. package/dist/teammates/index.js +827 -0
  245. package/dist/types/index.d.ts +482 -0
  246. package/dist/types/index.d.ts.map +1 -0
  247. package/dist/types/index.js +52 -0
  248. package/native/README.md +5 -5
  249. package/native/index.darwin-arm64.node +0 -0
  250. package/native/index.node +0 -0
  251. package/native/package.json +4 -4
  252. package/package.json +33 -16
  253. package/packages/src/core/__tests__/permissions.test.ts +1091 -0
  254. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +280 -0
  255. package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
  256. package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
  257. package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
  258. package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
  259. package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
  260. package/packages/src/core/agent-loop/compaction.ts +88 -0
  261. package/packages/src/core/agent-loop/formatters.ts +50 -0
  262. package/packages/src/core/agent-loop/index.ts +135 -0
  263. package/packages/src/core/agent-loop/loop-state.ts +187 -0
  264. package/packages/src/core/agent-loop/message-builder.ts +62 -0
  265. package/packages/src/core/agent-loop/tool-executor.ts +211 -0
  266. package/packages/src/core/agent-loop/turn-executor.ts +222 -0
  267. package/packages/src/core/agent-loop/types.ts +148 -0
  268. package/packages/src/core/agent-loop.ts +18 -0
  269. package/packages/src/core/api-client-impl.ts +619 -0
  270. package/packages/src/core/api-client.ts +6 -0
  271. package/packages/src/core/checkpoints.ts +606 -0
  272. package/packages/src/core/claude-md.ts +272 -0
  273. package/packages/src/core/cognitive-security/hooks.ts +590 -0
  274. package/packages/src/core/cognitive-security/index.ts +2041 -0
  275. package/packages/src/core/cognitive-security/middleware.ts +536 -0
  276. package/packages/src/core/config-loader.ts +324 -0
  277. package/packages/src/core/context-compaction.ts +578 -0
  278. package/packages/src/core/git-status.ts +262 -0
  279. package/packages/src/core/image.test.ts +180 -0
  280. package/packages/src/core/image.ts +350 -0
  281. package/packages/src/core/lmdb.db +0 -0
  282. package/packages/src/core/lmdb.db-lock +0 -0
  283. package/packages/src/core/models.ts +430 -0
  284. package/packages/src/core/normalizers/todo +4 -0
  285. package/packages/src/core/permissions.ts +431 -0
  286. package/packages/src/core/retry.ts +170 -0
  287. package/packages/src/core/session-store.ts +36 -0
  288. package/packages/src/core/sessions/export.ts +329 -0
  289. package/packages/src/core/sessions/index.ts +587 -0
  290. package/packages/src/core/sessions/metadata.ts +309 -0
  291. package/packages/src/core/sessions/persistence.ts +244 -0
  292. package/packages/src/core/sessions/types.ts +169 -0
  293. package/packages/src/core/stream-highlighter.ts +1123 -0
  294. package/packages/src/core/system-reminders.ts +402 -0
  295. package/packages/src/core/todo +8 -0
  296. package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
  297. package/packages/src/ecosystem/hooks/index.ts +341 -0
  298. package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
  299. package/packages/src/ecosystem/skills/index.ts +295 -0
  300. package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
  301. package/packages/src/ecosystem/tools/index.ts +1877 -0
  302. package/packages/src/index.ts +120 -0
  303. package/packages/src/interfaces/mcp/client.ts +389 -0
  304. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  305. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  306. package/packages/src/interfaces/ui/index.ts +161 -0
  307. package/packages/src/interfaces/ui/lmdb.db +0 -0
  308. package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
  309. package/packages/src/interfaces/ui/spinner.ts +451 -0
  310. package/packages/src/interfaces/ui/terminal/cli/index.ts +228 -0
  311. package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
  312. package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
  313. package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
  314. package/packages/src/interfaces/ui/terminal/shared/index.ts +71 -0
  315. package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
  316. package/packages/src/interfaces/ui/terminal/shared/query.ts +146 -0
  317. package/packages/src/interfaces/ui/terminal/shared/setup.ts +295 -0
  318. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +358 -0
  319. package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
  320. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +262 -0
  321. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +232 -0
  322. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +62 -0
  323. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +537 -0
  324. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +107 -0
  325. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +240 -0
  326. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +54 -0
  327. package/packages/src/interfaces/ui/terminal/tui/commands.ts +438 -0
  328. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +584 -0
  329. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +614 -0
  330. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +333 -0
  331. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +604 -0
  332. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +118 -0
  333. package/packages/src/interfaces/ui/terminal/tui/console.ts +49 -0
  334. package/packages/src/interfaces/ui/terminal/tui/index.ts +90 -0
  335. package/packages/src/interfaces/ui/terminal/tui/run.tsx +42 -0
  336. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +69 -0
  337. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +390 -0
  338. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +422 -0
  339. package/packages/src/interfaces/ui/terminal/tui/types.ts +186 -0
  340. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +104 -0
  341. package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +239 -0
  342. package/packages/src/lmdb.db +0 -0
  343. package/packages/src/lmdb.db-lock +0 -0
  344. package/packages/src/native/index.ts +2345 -0
  345. package/packages/src/teammates/index.ts +982 -0
  346. package/packages/src/types/index.ts +722 -0
  347. package/dist/cli.js +0 -148
  348. package/dist/index-0pkak453.js +0 -136
  349. package/dist/index-0qd0x8b4.js +0 -110
  350. package/dist/index-0x3kprq6.js +0 -240
  351. package/dist/index-1eawy937.js +0 -308
  352. package/dist/index-24m2aygy.js +0 -240
  353. package/dist/index-29xcjnne.js +0 -280
  354. package/dist/index-2avyytn5.js +0 -349
  355. package/dist/index-4ms367ey.js +0 -136
  356. package/dist/index-4w2t3b0m.js +0 -240
  357. package/dist/index-4xfgd8nz.js +0 -261
  358. package/dist/index-5acjp9gc.js +0 -157
  359. package/dist/index-5s15hr56.js +0 -136
  360. package/dist/index-6e4wf341.js +0 -349
  361. package/dist/index-6fvnkedw.js +0 -240
  362. package/dist/index-6rqpmd4g.js +0 -128
  363. package/dist/index-77ckwnbm.js +0 -280
  364. package/dist/index-9knxy49k.js +0 -128
  365. package/dist/index-9zrnw4zx.js +0 -128
  366. package/dist/index-bk21w99v.js +0 -280
  367. package/dist/index-c41n76fv.js +0 -240
  368. package/dist/index-cb4ppjdt.js +0 -255
  369. package/dist/index-cfb2edt6.js +0 -240
  370. package/dist/index-cmfa38hh.js +0 -308
  371. package/dist/index-datjz8q1.js +0 -257
  372. package/dist/index-eadf4wvn.js +0 -240
  373. package/dist/index-em5k0m3z.js +0 -345
  374. package/dist/index-gh8r333a.js +0 -110
  375. package/dist/index-gkx6k2tr.js +0 -261
  376. package/dist/index-h5cabfks.js +0 -155
  377. package/dist/index-hcrpwyy3.js +0 -261
  378. package/dist/index-hk7fwwa8.js +0 -257
  379. package/dist/index-jb8cw7f8.js +0 -136
  380. package/dist/index-kbyw4th1.js +0 -347
  381. package/dist/index-kgj5gqnm.js +0 -345
  382. package/dist/index-mdf6xp1z.js +0 -255
  383. package/dist/index-mrhv8kvc.js +0 -280
  384. package/dist/index-mt4743dd.js +0 -161
  385. package/dist/index-qnwsg97q.js +0 -240
  386. package/dist/index-qwdy6x44.js +0 -261
  387. package/dist/index-rmj77261.js +0 -157
  388. package/dist/index-sbbw1a61.js +0 -349
  389. package/dist/index-svy5bcpn.js +0 -345
  390. package/dist/index-tvmy7tm9.js +0 -261
  391. package/dist/index-tzz4vzkj.js +0 -312
  392. package/dist/index-vz80zmhe.js +0 -110
  393. package/dist/index-wed2fk67.js +0 -240
  394. package/dist/index-wksgzz8e.js +0 -280
  395. package/dist/index-wn2m4wma.js +0 -240
  396. package/dist/index-xha05vjc.js +0 -257
  397. package/dist/index-yc6eh8p8.js +0 -136
  398. package/dist/index-ycjxx9ft.js +0 -240
  399. package/dist/index-z0gzd0fc.js +0 -110
  400. package/dist/index-z8cwtf8j.js +0 -240
  401. package/dist/index-zy5mtt00.js +0 -128
@@ -0,0 +1,1335 @@
1
+ /**
2
+ * Built-in Tools Tests
3
+ *
4
+ * Comprehensive tests for Read, Write, Edit, Bash, Glob, and Grep tools.
5
+ */
6
+
7
+ import { describe, test, it, expect, beforeEach, afterEach } from "bun:test";
8
+ import { mkdtemp, writeFile, mkdir, rm, stat } from "fs/promises";
9
+ import { tmpdir } from "os";
10
+ import { join, dirname } from "path";
11
+ import {
12
+ ReadTool,
13
+ WriteTool,
14
+ EditTool,
15
+ BashTool,
16
+ GlobTool,
17
+ GrepTool,
18
+ getToolByName,
19
+ builtInTools,
20
+ } from "../index.js";
21
+ import type { ToolContext, ToolResult } from "../../../types/index.js";
22
+
23
+ // Helper to create a default tool context
24
+ function createToolContext(workingDirectory: string): ToolContext {
25
+ return {
26
+ workingDirectory,
27
+ permissionMode: "bypassPermissions",
28
+ };
29
+ }
30
+
31
+ describe("Built-in Tools Registry", () => {
32
+ test("builtInTools array contains all expected tools", () => {
33
+ const toolNames = builtInTools.map((t) => t.name);
34
+ expect(toolNames).toContain("Read");
35
+ expect(toolNames).toContain("Write");
36
+ expect(toolNames).toContain("Edit");
37
+ expect(toolNames).toContain("Bash");
38
+ expect(toolNames).toContain("Glob");
39
+ expect(toolNames).toContain("Grep");
40
+ });
41
+
42
+ test("getToolByName returns correct tool", () => {
43
+ expect(getToolByName("Read")?.name).toBe("Read");
44
+ expect(getToolByName("Write")?.name).toBe("Write");
45
+ expect(getToolByName("NonExistent")).toBeUndefined();
46
+ });
47
+ });
48
+
49
+ describe("ReadTool", () => {
50
+ let tempDir: string;
51
+ let context: ToolContext;
52
+
53
+ beforeEach(async () => {
54
+ tempDir = await mkdtemp(join(tmpdir(), "read-tool-test-"));
55
+ context = createToolContext(tempDir);
56
+ });
57
+
58
+ afterEach(async () => {
59
+ await rm(tempDir, { recursive: true, force: true });
60
+ });
61
+
62
+ describe("successful operations", () => {
63
+ test("reads a simple text file", async () => {
64
+ const filePath = join(tempDir, "test.txt");
65
+ await writeFile(filePath, "Hello, World!");
66
+
67
+ const result = await ReadTool.handler({ file_path: filePath }, context);
68
+
69
+ expect(result.is_error).toBeUndefined();
70
+ expect(result.content).toContain("Hello, World!");
71
+ });
72
+
73
+ test("reads a file with multiple lines", async () => {
74
+ const filePath = join(tempDir, "multiline.txt");
75
+ const content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5";
76
+ await writeFile(filePath, content);
77
+
78
+ const result = await ReadTool.handler({ file_path: filePath }, context);
79
+
80
+ expect(result.is_error).toBeUndefined();
81
+ expect(result.content).toContain("Line 1");
82
+ expect(result.content).toContain("Line 5");
83
+ });
84
+
85
+ test("reads file with line numbers (cat -n format)", async () => {
86
+ const filePath = join(tempDir, "numbered.txt");
87
+ await writeFile(filePath, "First\nSecond\nThird");
88
+
89
+ const result = await ReadTool.handler({ file_path: filePath }, context);
90
+
91
+ expect(result.content).toMatch(/1\tFirst/);
92
+ expect(result.content).toMatch(/2\tSecond/);
93
+ expect(result.content).toMatch(/3\tThird/);
94
+ });
95
+
96
+ test("reads file with offset", async () => {
97
+ const filePath = join(tempDir, "offset.txt");
98
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
99
+
100
+ const result = await ReadTool.handler(
101
+ { file_path: filePath, offset: 3 },
102
+ context
103
+ );
104
+
105
+ expect(result.content).toContain("Line 3");
106
+ expect(result.content).not.toContain("Line 1");
107
+ expect(result.content).not.toContain("Line 2");
108
+ });
109
+
110
+ test("reads file with limit", async () => {
111
+ const filePath = join(tempDir, "limit.txt");
112
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
113
+
114
+ const result = await ReadTool.handler(
115
+ { file_path: filePath, limit: 2 },
116
+ context
117
+ );
118
+
119
+ expect(result.content).toContain("Line 1");
120
+ expect(result.content).toContain("Line 2");
121
+ expect(result.content).not.toContain("Line 5");
122
+ });
123
+
124
+ test("reads file with offset and limit", async () => {
125
+ const filePath = join(tempDir, "offset-limit.txt");
126
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5");
127
+
128
+ const result = await ReadTool.handler(
129
+ { file_path: filePath, offset: 2, limit: 2 },
130
+ context
131
+ );
132
+
133
+ expect(result.content).toContain("Line 2");
134
+ expect(result.content).toContain("Line 3");
135
+ expect(result.content).not.toContain("Line 1");
136
+ expect(result.content).not.toContain("Line 5");
137
+ });
138
+
139
+ test("reads an empty file", async () => {
140
+ const filePath = join(tempDir, "empty.txt");
141
+ await writeFile(filePath, "");
142
+
143
+ const result = await ReadTool.handler({ file_path: filePath }, context);
144
+
145
+ expect(result.is_error).toBeUndefined();
146
+ // Empty file returns a single line with line number but no content
147
+ expect(result.content).toBe("1\t");
148
+ });
149
+
150
+ test("reads a file with special characters", async () => {
151
+ const filePath = join(tempDir, "special.txt");
152
+ await writeFile(filePath, "Special: \t<Tab>\nUnicode: \u00e9\u00e8\u00ea");
153
+
154
+ const result = await ReadTool.handler({ file_path: filePath }, context);
155
+
156
+ expect(result.is_error).toBeUndefined();
157
+ expect(result.content).toContain("<Tab>");
158
+ expect(result.content).toContain("\u00e9\u00e8\u00ea");
159
+ });
160
+
161
+ test("reads a JSON file", async () => {
162
+ const filePath = join(tempDir, "data.json");
163
+ await writeFile(filePath, JSON.stringify({ key: "value", num: 42 }));
164
+
165
+ const result = await ReadTool.handler({ file_path: filePath }, context);
166
+
167
+ expect(result.is_error).toBeUndefined();
168
+ expect(result.content).toContain('"key"');
169
+ expect(result.content).toContain('"value"');
170
+ });
171
+
172
+ test("reads a TypeScript file", async () => {
173
+ const filePath = join(tempDir, "code.ts");
174
+ await writeFile(filePath, "const x: number = 42;\nexport { x };");
175
+
176
+ const result = await ReadTool.handler({ file_path: filePath }, context);
177
+
178
+ expect(result.is_error).toBeUndefined();
179
+ expect(result.content).toContain("const x: number = 42");
180
+ });
181
+ });
182
+
183
+ describe("truncation warning", () => {
184
+ test("shows truncation warning when file exceeds limit", async () => {
185
+ const filePath = join(tempDir, "long.txt");
186
+ const lines = Array(3000)
187
+ .fill(null)
188
+ .map((_, i) => `Line ${i + 1}`)
189
+ .join("\n");
190
+ await writeFile(filePath, lines);
191
+
192
+ const result = await ReadTool.handler(
193
+ { file_path: filePath, limit: 100 },
194
+ context
195
+ );
196
+
197
+ expect(result.content).toContain("WARNING");
198
+ expect(result.content).toContain("3000 lines");
199
+ expect(result.content).toContain("limit: 100");
200
+ });
201
+
202
+ test("does not show truncation warning when file is within limit", async () => {
203
+ const filePath = join(tempDir, "short.txt");
204
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3");
205
+
206
+ const result = await ReadTool.handler(
207
+ { file_path: filePath, limit: 100 },
208
+ context
209
+ );
210
+
211
+ expect(result.content).not.toContain("WARNING");
212
+ });
213
+ });
214
+
215
+ describe("error handling", () => {
216
+ test("returns error for non-existent file", async () => {
217
+ const result = await ReadTool.handler(
218
+ { file_path: join(tempDir, "nonexistent.txt") },
219
+ context
220
+ );
221
+
222
+ expect(result.is_error).toBe(true);
223
+ expect(result.content).toContain("Error");
224
+ expect(result.content).toContain("not found");
225
+ });
226
+
227
+ test("returns error for empty file_path", async () => {
228
+ const result = await ReadTool.handler({ file_path: "" }, context);
229
+
230
+ expect(result.is_error).toBe(true);
231
+ expect(result.content).toContain("required");
232
+ });
233
+
234
+ test("returns error for missing file_path", async () => {
235
+ const result = await ReadTool.handler({}, context);
236
+
237
+ expect(result.is_error).toBe(true);
238
+ expect(result.content).toContain("required");
239
+ });
240
+
241
+ test("returns error for whitespace-only file_path", async () => {
242
+ const result = await ReadTool.handler({ file_path: " " }, context);
243
+
244
+ expect(result.is_error).toBe(true);
245
+ expect(result.content).toContain("required");
246
+ });
247
+
248
+ test("returns error for directory path instead of file", async () => {
249
+ const dirPath = join(tempDir, "subdir");
250
+ await mkdir(dirPath);
251
+
252
+ const result = await ReadTool.handler({ file_path: dirPath }, context);
253
+
254
+ // Bun.file can read directories but returns empty or error
255
+ // The behavior depends on the implementation
256
+ expect(result).toBeDefined();
257
+ });
258
+ });
259
+
260
+ describe("binary file handling", () => {
261
+ test("returns error for binary exclusion files", async () => {
262
+ const filePath = join(tempDir, "test.exe");
263
+ // Write a small binary file (fake executable header)
264
+ await writeFile(filePath, Buffer.from([0x4d, 0x5a, 0x90, 0x00]));
265
+
266
+ const result = await ReadTool.handler({ file_path: filePath }, context);
267
+
268
+ expect(result.is_error).toBe(true);
269
+ expect(result.content).toContain("Binary file detected");
270
+ });
271
+
272
+ test("returns error for zip files", async () => {
273
+ const filePath = join(tempDir, "archive.zip");
274
+ // Write a minimal zip header
275
+ await writeFile(filePath, Buffer.from([0x50, 0x4b, 0x03, 0x04]));
276
+
277
+ const result = await ReadTool.handler({ file_path: filePath }, context);
278
+
279
+ expect(result.is_error).toBe(true);
280
+ expect(result.content).toContain("Binary file detected");
281
+ });
282
+ });
283
+ });
284
+
285
+ describe("WriteTool", () => {
286
+ let tempDir: string;
287
+ let context: ToolContext;
288
+
289
+ beforeEach(async () => {
290
+ tempDir = await mkdtemp(join(tmpdir(), "write-tool-test-"));
291
+ context = createToolContext(tempDir);
292
+ });
293
+
294
+ afterEach(async () => {
295
+ await rm(tempDir, { recursive: true, force: true });
296
+ });
297
+
298
+ describe("successful operations", () => {
299
+ test("writes a new file", async () => {
300
+ const filePath = join(tempDir, "new.txt");
301
+ const result = await WriteTool.handler(
302
+ { file_path: filePath, content: "Hello, World!" },
303
+ context
304
+ );
305
+
306
+ expect(result.is_error).toBeUndefined();
307
+ expect(result.content).toContain("Successfully wrote");
308
+
309
+ // Verify file was created
310
+ const file = Bun.file(filePath);
311
+ expect(await file.exists()).toBe(true);
312
+ expect(await file.text()).toBe("Hello, World!");
313
+ });
314
+
315
+ test("overwrites an existing file", async () => {
316
+ const filePath = join(tempDir, "existing.txt");
317
+ await writeFile(filePath, "Original content");
318
+
319
+ const result = await WriteTool.handler(
320
+ { file_path: filePath, content: "New content" },
321
+ context
322
+ );
323
+
324
+ expect(result.is_error).toBeUndefined();
325
+
326
+ const file = Bun.file(filePath);
327
+ expect(await file.text()).toBe("New content");
328
+ });
329
+
330
+ test("writes an empty file", async () => {
331
+ const filePath = join(tempDir, "empty.txt");
332
+ const result = await WriteTool.handler(
333
+ { file_path: filePath, content: "" },
334
+ context
335
+ );
336
+
337
+ expect(result.is_error).toBeUndefined();
338
+
339
+ const file = Bun.file(filePath);
340
+ expect(await file.text()).toBe("");
341
+ });
342
+
343
+ test("writes a file with special characters", async () => {
344
+ const filePath = join(tempDir, "special.txt");
345
+ const content = "Special: \t<Tab>\nUnicode: \u00e9\u00e8\u00ea\nEmoji: \ud83d\ude00";
346
+
347
+ const result = await WriteTool.handler(
348
+ { file_path: filePath, content },
349
+ context
350
+ );
351
+
352
+ expect(result.is_error).toBeUndefined();
353
+
354
+ const file = Bun.file(filePath);
355
+ expect(await file.text()).toBe(content);
356
+ });
357
+
358
+ test("writes a large file", async () => {
359
+ const filePath = join(tempDir, "large.txt");
360
+ const content = "x".repeat(100000);
361
+
362
+ const result = await WriteTool.handler(
363
+ { file_path: filePath, content },
364
+ context
365
+ );
366
+
367
+ expect(result.is_error).toBeUndefined();
368
+
369
+ const file = Bun.file(filePath);
370
+ expect(await file.text()).toBe(content);
371
+ });
372
+
373
+ test("writes JSON content", async () => {
374
+ const filePath = join(tempDir, "data.json");
375
+ const content = JSON.stringify({ key: "value", nested: { a: 1 } });
376
+
377
+ const result = await WriteTool.handler(
378
+ { file_path: filePath, content },
379
+ context
380
+ );
381
+
382
+ expect(result.is_error).toBeUndefined();
383
+
384
+ const file = Bun.file(filePath);
385
+ const parsed = JSON.parse(await file.text());
386
+ expect(parsed.key).toBe("value");
387
+ });
388
+
389
+ test("creates nested directories if needed", async () => {
390
+ const filePath = join(tempDir, "nested", "deep", "file.txt");
391
+
392
+ // Bun.write creates parent directories automatically
393
+ const result = await WriteTool.handler(
394
+ { file_path: filePath, content: "nested content" },
395
+ context
396
+ );
397
+
398
+ expect(result.is_error).toBeUndefined();
399
+
400
+ const file = Bun.file(filePath);
401
+ expect(await file.exists()).toBe(true);
402
+ });
403
+ });
404
+
405
+ describe("error handling", () => {
406
+ test("returns error for empty file_path", async () => {
407
+ const result = await WriteTool.handler(
408
+ { file_path: "", content: "test" },
409
+ context
410
+ );
411
+
412
+ expect(result.is_error).toBe(true);
413
+ expect(result.content).toContain("required");
414
+ });
415
+
416
+ test("returns error for missing file_path", async () => {
417
+ const result = await WriteTool.handler({ content: "test" }, context);
418
+
419
+ expect(result.is_error).toBe(true);
420
+ expect(result.content).toContain("required");
421
+ });
422
+
423
+ test("returns error for missing content", async () => {
424
+ const result = await WriteTool.handler(
425
+ { file_path: join(tempDir, "test.txt") },
426
+ context
427
+ );
428
+
429
+ expect(result.is_error).toBe(true);
430
+ expect(result.content).toContain("required");
431
+ });
432
+
433
+ test("returns error for null content", async () => {
434
+ const result = await WriteTool.handler(
435
+ { file_path: join(tempDir, "test.txt"), content: null },
436
+ context
437
+ );
438
+
439
+ expect(result.is_error).toBe(true);
440
+ expect(result.content).toContain("required");
441
+ });
442
+ });
443
+ });
444
+
445
+ describe("EditTool", () => {
446
+ let tempDir: string;
447
+ let context: ToolContext;
448
+
449
+ beforeEach(async () => {
450
+ tempDir = await mkdtemp(join(tmpdir(), "edit-tool-test-"));
451
+ context = createToolContext(tempDir);
452
+ });
453
+
454
+ afterEach(async () => {
455
+ await rm(tempDir, { recursive: true, force: true });
456
+ });
457
+
458
+ describe("successful operations", () => {
459
+ test("edits a file with exact string replacement", async () => {
460
+ const filePath = join(tempDir, "edit.txt");
461
+ await writeFile(filePath, "Hello, World!");
462
+
463
+ const result = await EditTool.handler(
464
+ {
465
+ file_path: filePath,
466
+ old_string: "World",
467
+ new_string: "Universe",
468
+ },
469
+ context
470
+ );
471
+
472
+ expect(result.is_error).toBeUndefined();
473
+ expect(result.content).toContain("Successfully edited");
474
+
475
+ const file = Bun.file(filePath);
476
+ expect(await file.text()).toBe("Hello, Universe!");
477
+ });
478
+
479
+ test("edits multi-line content", async () => {
480
+ const filePath = join(tempDir, "multiline.txt");
481
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3");
482
+
483
+ const result = await EditTool.handler(
484
+ {
485
+ file_path: filePath,
486
+ old_string: "Line 2",
487
+ new_string: "Modified Line 2",
488
+ },
489
+ context
490
+ );
491
+
492
+ expect(result.is_error).toBeUndefined();
493
+
494
+ const file = Bun.file(filePath);
495
+ const text = await file.text();
496
+ expect(text).toContain("Modified Line 2");
497
+ expect(text).toContain("Line 1");
498
+ expect(text).toContain("Line 3");
499
+ });
500
+
501
+ test("replaces all occurrences with replace_all", async () => {
502
+ const filePath = join(tempDir, "replace-all.txt");
503
+ await writeFile(filePath, "foo bar foo baz foo");
504
+
505
+ const result = await EditTool.handler(
506
+ {
507
+ file_path: filePath,
508
+ old_string: "foo",
509
+ new_string: "qux",
510
+ replace_all: true,
511
+ },
512
+ context
513
+ );
514
+
515
+ expect(result.is_error).toBeUndefined();
516
+ expect(result.content).toContain("3 occurrences");
517
+
518
+ const file = Bun.file(filePath);
519
+ expect(await file.text()).toBe("qux bar qux baz qux");
520
+ });
521
+
522
+ test("replaces with empty string (deletion)", async () => {
523
+ const filePath = join(tempDir, "delete.txt");
524
+ await writeFile(filePath, "Hello, World!");
525
+
526
+ const result = await EditTool.handler(
527
+ {
528
+ file_path: filePath,
529
+ old_string: ", World",
530
+ new_string: "",
531
+ },
532
+ context
533
+ );
534
+
535
+ expect(result.is_error).toBeUndefined();
536
+
537
+ const file = Bun.file(filePath);
538
+ expect(await file.text()).toBe("Hello!");
539
+ });
540
+
541
+ test("replaces with longer string", async () => {
542
+ const filePath = join(tempDir, "expand.txt");
543
+ await writeFile(filePath, "Hi");
544
+
545
+ const result = await EditTool.handler(
546
+ {
547
+ file_path: filePath,
548
+ old_string: "Hi",
549
+ new_string: "Hello, this is a much longer greeting!",
550
+ },
551
+ context
552
+ );
553
+
554
+ expect(result.is_error).toBeUndefined();
555
+
556
+ const file = Bun.file(filePath);
557
+ expect(await file.text()).toBe("Hello, this is a much longer greeting!");
558
+ });
559
+
560
+ test("edits file with special regex characters in old_string", async () => {
561
+ const filePath = join(tempDir, "regex.txt");
562
+ await writeFile(filePath, "Price: $100 (50% off)");
563
+
564
+ const result = await EditTool.handler(
565
+ {
566
+ file_path: filePath,
567
+ old_string: "$100 (50% off)",
568
+ new_string: "$80",
569
+ },
570
+ context
571
+ );
572
+
573
+ expect(result.is_error).toBeUndefined();
574
+
575
+ const file = Bun.file(filePath);
576
+ expect(await file.text()).toBe("Price: $80");
577
+ });
578
+ });
579
+
580
+ describe("error handling", () => {
581
+ test("returns error when old_string not found", async () => {
582
+ const filePath = join(tempDir, "not-found.txt");
583
+ await writeFile(filePath, "Hello, World!");
584
+
585
+ const result = await EditTool.handler(
586
+ {
587
+ file_path: filePath,
588
+ old_string: "NonExistent",
589
+ new_string: "Replacement",
590
+ },
591
+ context
592
+ );
593
+
594
+ expect(result.is_error).toBe(true);
595
+ expect(result.content).toContain("not found");
596
+ });
597
+
598
+ test("returns error when string appears multiple times without replace_all", async () => {
599
+ const filePath = join(tempDir, "multiple.txt");
600
+ await writeFile(filePath, "foo bar foo baz foo");
601
+
602
+ const result = await EditTool.handler(
603
+ {
604
+ file_path: filePath,
605
+ old_string: "foo",
606
+ new_string: "qux",
607
+ },
608
+ context
609
+ );
610
+
611
+ expect(result.is_error).toBe(true);
612
+ expect(result.content).toContain("multiple times");
613
+ expect(result.content).toContain("replace_all");
614
+ });
615
+
616
+ test("returns error for non-existent file", async () => {
617
+ const result = await EditTool.handler(
618
+ {
619
+ file_path: join(tempDir, "nonexistent.txt"),
620
+ old_string: "old",
621
+ new_string: "new",
622
+ },
623
+ context
624
+ );
625
+
626
+ expect(result.is_error).toBe(true);
627
+ });
628
+
629
+ test("returns error for missing file_path", async () => {
630
+ const result = await EditTool.handler(
631
+ { old_string: "old", new_string: "new" },
632
+ context
633
+ );
634
+
635
+ expect(result.is_error).toBe(true);
636
+ expect(result.content).toContain("required");
637
+ });
638
+
639
+ test("returns error for missing old_string", async () => {
640
+ const result = await EditTool.handler(
641
+ { file_path: join(tempDir, "test.txt"), new_string: "new" },
642
+ context
643
+ );
644
+
645
+ expect(result.is_error).toBe(true);
646
+ expect(result.content).toContain("required");
647
+ });
648
+
649
+ test("returns error for missing new_string", async () => {
650
+ const result = await EditTool.handler(
651
+ { file_path: join(tempDir, "test.txt"), old_string: "old" },
652
+ context
653
+ );
654
+
655
+ expect(result.is_error).toBe(true);
656
+ expect(result.content).toContain("required");
657
+ });
658
+
659
+ test("returns error when replace_all finds no matches", async () => {
660
+ const filePath = join(tempDir, "no-match.txt");
661
+ await writeFile(filePath, "Hello, World!");
662
+
663
+ const result = await EditTool.handler(
664
+ {
665
+ file_path: filePath,
666
+ old_string: "NonExistent",
667
+ new_string: "Replacement",
668
+ replace_all: true,
669
+ },
670
+ context
671
+ );
672
+
673
+ expect(result.is_error).toBe(true);
674
+ expect(result.content).toContain("not found");
675
+ });
676
+ });
677
+ });
678
+
679
+ describe("BashTool", () => {
680
+ let tempDir: string;
681
+ let context: ToolContext;
682
+
683
+ beforeEach(async () => {
684
+ tempDir = await mkdtemp(join(tmpdir(), "bash-tool-test-"));
685
+ context = createToolContext(tempDir);
686
+ });
687
+
688
+ afterEach(async () => {
689
+ await rm(tempDir, { recursive: true, force: true });
690
+ });
691
+
692
+ describe("successful operations", () => {
693
+ test("executes echo command", async () => {
694
+ const result = await BashTool.handler({ command: "echo 'Hello'" }, context);
695
+
696
+ expect(result.is_error).toBeUndefined();
697
+ expect(result.content).toContain("Hello");
698
+ });
699
+
700
+ test("executes pwd command", async () => {
701
+ const result = await BashTool.handler({ command: "pwd" }, context);
702
+
703
+ expect(result.is_error).toBeUndefined();
704
+ expect(result.content).toContain(tempDir.split("/").pop()!);
705
+ });
706
+
707
+ test("executes ls command", async () => {
708
+ await writeFile(join(tempDir, "test.txt"), "content");
709
+
710
+ const result = await BashTool.handler({ command: "ls" }, context);
711
+
712
+ expect(result.is_error).toBeUndefined();
713
+ expect(result.content).toContain("test.txt");
714
+ });
715
+
716
+ test("executes command with pipes", async () => {
717
+ const result = await BashTool.handler(
718
+ { command: "echo 'line1\nline2\nline3' | wc -l" },
719
+ context
720
+ );
721
+
722
+ expect(result.is_error).toBeUndefined();
723
+ expect(typeof result.content === "string" ? result.content.trim() : result.content).toBe("3");
724
+ });
725
+
726
+ test("executes command with redirection", async () => {
727
+ const result = await BashTool.handler(
728
+ { command: "echo 'test content' > output.txt" },
729
+ context
730
+ );
731
+
732
+ expect(result.is_error).toBeUndefined();
733
+
734
+ const file = Bun.file(join(tempDir, "output.txt"));
735
+ expect(await file.text()).toContain("test content");
736
+ });
737
+
738
+ test("executes command with environment variables", async () => {
739
+ const result = await BashTool.handler(
740
+ { command: "MY_VAR=hello && echo $MY_VAR" },
741
+ context
742
+ );
743
+
744
+ expect(result.is_error).toBeUndefined();
745
+ expect(result.content).toContain("hello");
746
+ });
747
+
748
+ test("returns (no output) for commands with no stdout", async () => {
749
+ const result = await BashTool.handler(
750
+ { command: "mkdir -p subdir" },
751
+ context
752
+ );
753
+
754
+ expect(result.is_error).toBeUndefined();
755
+ expect(result.content).toContain("no output");
756
+ });
757
+ });
758
+
759
+ describe("error handling", () => {
760
+ test("returns error for non-zero exit code", async () => {
761
+ const result = await BashTool.handler(
762
+ { command: "exit 1" },
763
+ context
764
+ );
765
+
766
+ expect(result.is_error).toBe(true);
767
+ expect(result.content).toContain("Exit code: 1");
768
+ });
769
+
770
+ test("returns error for command not found", async () => {
771
+ const result = await BashTool.handler(
772
+ { command: "nonexistent_command_xyz" },
773
+ context
774
+ );
775
+
776
+ expect(result.is_error).toBe(true);
777
+ });
778
+
779
+ test("returns error for missing command", async () => {
780
+ const result = await BashTool.handler({}, context);
781
+
782
+ expect(result.is_error).toBe(true);
783
+ expect(result.content).toContain("required");
784
+ });
785
+
786
+ test("returns error for empty command", async () => {
787
+ const result = await BashTool.handler({ command: "" }, context);
788
+
789
+ expect(result.is_error).toBe(true);
790
+ expect(result.content).toContain("required");
791
+ });
792
+
793
+ test("returns error for whitespace-only command", async () => {
794
+ const result = await BashTool.handler({ command: " " }, context);
795
+
796
+ expect(result.is_error).toBe(true);
797
+ expect(result.content).toContain("required");
798
+ });
799
+
800
+ test("includes stderr in error output", async () => {
801
+ const result = await BashTool.handler(
802
+ { command: "ls /nonexistent_directory_xyz" },
803
+ context
804
+ );
805
+
806
+ expect(result.is_error).toBe(true);
807
+ // Should include error message about non-existent directory
808
+ });
809
+ });
810
+
811
+ describe("timeout handling", () => {
812
+ test("uses default timeout when not specified", async () => {
813
+ const result = await BashTool.handler(
814
+ { command: "echo 'quick'" },
815
+ context
816
+ );
817
+
818
+ expect(result.is_error).toBeUndefined();
819
+ });
820
+
821
+ test("respects custom timeout", async () => {
822
+ const result = await BashTool.handler(
823
+ { command: "echo 'test'", timeout: 5000 },
824
+ context
825
+ );
826
+
827
+ expect(result.is_error).toBeUndefined();
828
+ });
829
+ });
830
+
831
+ describe("working directory", () => {
832
+ test("executes in specified working directory", async () => {
833
+ const subDir = join(tempDir, "subdir");
834
+ await mkdir(subDir);
835
+ await writeFile(join(subDir, "unique.txt"), "content");
836
+
837
+ const localContext = createToolContext(subDir);
838
+ const result = await BashTool.handler({ command: "ls" }, localContext);
839
+
840
+ expect(result.content).toContain("unique.txt");
841
+ });
842
+ });
843
+ });
844
+
845
+ describe("GlobTool", () => {
846
+ let tempDir: string;
847
+ let context: ToolContext;
848
+
849
+ beforeEach(async () => {
850
+ tempDir = await mkdtemp(join(tmpdir(), "glob-tool-test-"));
851
+ context = createToolContext(tempDir);
852
+
853
+ // Create test file structure
854
+ await writeFile(join(tempDir, "file1.txt"), "content");
855
+ await writeFile(join(tempDir, "file2.txt"), "content");
856
+ await writeFile(join(tempDir, "script.ts"), "content");
857
+ await writeFile(join(tempDir, "config.json"), "content");
858
+ await mkdir(join(tempDir, "subdir"));
859
+ await writeFile(join(tempDir, "subdir", "nested.txt"), "content");
860
+ await writeFile(join(tempDir, "subdir", "nested.ts"), "content");
861
+ });
862
+
863
+ afterEach(async () => {
864
+ await rm(tempDir, { recursive: true, force: true });
865
+ });
866
+
867
+ describe("successful operations", () => {
868
+ test("finds files with *.txt pattern", async () => {
869
+ const result = await GlobTool.handler({ pattern: "*.txt" }, context);
870
+
871
+ expect(result.is_error).toBeUndefined();
872
+ expect(result.content).toContain("file1.txt");
873
+ expect(result.content).toContain("file2.txt");
874
+ });
875
+
876
+ test("finds files with **/*.ts pattern (recursive)", async () => {
877
+ const result = await GlobTool.handler({ pattern: "**/*.ts" }, context);
878
+
879
+ expect(result.is_error).toBeUndefined();
880
+ expect(result.content).toContain("script.ts");
881
+ expect(result.content).toContain("nested.ts");
882
+ });
883
+
884
+ test("finds files with **/* pattern (all files)", async () => {
885
+ const result = await GlobTool.handler({ pattern: "**/*" }, context);
886
+
887
+ expect(result.is_error).toBeUndefined();
888
+ expect(result.content).toContain("file1.txt");
889
+ expect(result.content).toContain("script.ts");
890
+ expect(result.content).toContain("nested.txt");
891
+ });
892
+
893
+ test("finds files in specific directory", async () => {
894
+ const result = await GlobTool.handler(
895
+ { pattern: "*.txt", path: join(tempDir, "subdir") },
896
+ context
897
+ );
898
+
899
+ expect(result.is_error).toBeUndefined();
900
+ expect(result.content).toContain("nested.txt");
901
+ expect(result.content).not.toContain("file1.txt");
902
+ });
903
+
904
+ test("finds JSON files", async () => {
905
+ const result = await GlobTool.handler({ pattern: "*.json" }, context);
906
+
907
+ expect(result.is_error).toBeUndefined();
908
+ expect(result.content).toContain("config.json");
909
+ });
910
+
911
+ test("returns absolute paths", async () => {
912
+ const result = await GlobTool.handler({ pattern: "*.txt" }, context);
913
+
914
+ expect(result.content).toContain(tempDir);
915
+ });
916
+ });
917
+
918
+ describe("no matches", () => {
919
+ test("returns message when no files match", async () => {
920
+ const result = await GlobTool.handler(
921
+ { pattern: "*.nonexistent" },
922
+ context
923
+ );
924
+
925
+ expect(result.is_error).toBeUndefined();
926
+ expect(result.content).toContain("No files found");
927
+ });
928
+ });
929
+
930
+ describe("error handling", () => {
931
+ test("returns error for missing pattern", async () => {
932
+ const result = await GlobTool.handler({}, context);
933
+
934
+ expect(result.is_error).toBe(true);
935
+ expect(result.content).toContain("required");
936
+ });
937
+
938
+ test("returns error for empty pattern", async () => {
939
+ const result = await GlobTool.handler({ pattern: "" }, context);
940
+
941
+ expect(result.is_error).toBe(true);
942
+ expect(result.content).toContain("required");
943
+ });
944
+
945
+ test("returns error for non-existent directory", async () => {
946
+ const result = await GlobTool.handler(
947
+ { pattern: "*.txt", path: "/nonexistent/path" },
948
+ context
949
+ );
950
+
951
+ // Glob may succeed with empty results or fail depending on implementation
952
+ expect(result).toBeDefined();
953
+ });
954
+ });
955
+ });
956
+
957
+ describe("GrepTool", () => {
958
+ let tempDir: string;
959
+ let context: ToolContext;
960
+
961
+ beforeEach(async () => {
962
+ tempDir = await mkdtemp(join(tmpdir(), "grep-tool-test-"));
963
+ context = createToolContext(tempDir);
964
+
965
+ // Create test files
966
+ await writeFile(
967
+ join(tempDir, "file1.txt"),
968
+ "Hello World\nThis is a test\nAnother line"
969
+ );
970
+ await writeFile(
971
+ join(tempDir, "file2.txt"),
972
+ "Hello Universe\nDifferent content\nTest pattern here"
973
+ );
974
+ await writeFile(
975
+ join(tempDir, "code.ts"),
976
+ "const greeting = 'Hello';\nfunction test() { return 'Hello'; }"
977
+ );
978
+ await mkdir(join(tempDir, "subdir"));
979
+ await writeFile(
980
+ join(tempDir, "subdir", "nested.txt"),
981
+ "Nested Hello\nAnother test"
982
+ );
983
+ });
984
+
985
+ afterEach(async () => {
986
+ await rm(tempDir, { recursive: true, force: true });
987
+ });
988
+
989
+ describe("successful operations", () => {
990
+ test("searches for pattern in directory", async () => {
991
+ const result = await GrepTool.handler({ pattern: "Hello" }, context);
992
+
993
+ expect(result.is_error).toBeUndefined();
994
+ expect(result.content).toContain("Hello");
995
+ });
996
+
997
+ test("searches for pattern in specific file", async () => {
998
+ const result = await GrepTool.handler(
999
+ { pattern: "test", path: join(tempDir, "file1.txt") },
1000
+ context
1001
+ );
1002
+
1003
+ // Note: GrepTool uses ripgrep which may require the path to be a directory
1004
+ // when searching with --json flag. Check that the tool returns results.
1005
+ // If path is a file, ripgrep may behave differently.
1006
+ expect(result).toBeDefined();
1007
+ });
1008
+
1009
+ test("searches case-insensitively with -i flag", async () => {
1010
+ await writeFile(join(tempDir, "case.txt"), "HELLO world");
1011
+
1012
+ const result = await GrepTool.handler(
1013
+ { pattern: "hello", "-i:": true },
1014
+ context
1015
+ );
1016
+
1017
+ expect(result.is_error).toBeUndefined();
1018
+ expect(result.content).toContain("HELLO");
1019
+ });
1020
+
1021
+ test("filters by glob pattern", async () => {
1022
+ const result = await GrepTool.handler(
1023
+ { pattern: "Hello", glob: "*.ts" },
1024
+ context
1025
+ );
1026
+
1027
+ expect(result.is_error).toBeUndefined();
1028
+ expect(result.content).toContain("code.ts");
1029
+ expect(result.content).not.toContain("file1.txt");
1030
+ });
1031
+
1032
+ test("returns files_with_matches mode", async () => {
1033
+ const result = await GrepTool.handler(
1034
+ { pattern: "Hello", output_mode: "files_with_matches" },
1035
+ context
1036
+ );
1037
+
1038
+ expect(result.is_error).toBeUndefined();
1039
+ // Should list files, not line content
1040
+ });
1041
+
1042
+ test("returns count mode", async () => {
1043
+ const result = await GrepTool.handler(
1044
+ { pattern: "Hello", output_mode: "count" },
1045
+ context
1046
+ );
1047
+
1048
+ expect(result.is_error).toBeUndefined();
1049
+ // Should return counts
1050
+ });
1051
+
1052
+ test("respects head_limit", async () => {
1053
+ const result = await GrepTool.handler(
1054
+ { pattern: "Hello", head_limit: 1 },
1055
+ context
1056
+ );
1057
+
1058
+ expect(result.is_error).toBeUndefined();
1059
+ // Should limit results
1060
+ });
1061
+
1062
+ test("searches with regex pattern", async () => {
1063
+ const result = await GrepTool.handler(
1064
+ { pattern: "H[a-z]+o" },
1065
+ context
1066
+ );
1067
+
1068
+ expect(result.is_error).toBeUndefined();
1069
+ expect(result.content).toContain("Hello");
1070
+ });
1071
+ });
1072
+
1073
+ describe("no matches", () => {
1074
+ test("returns message when no matches found", async () => {
1075
+ const result = await GrepTool.handler(
1076
+ { pattern: "NonExistentPatternXYZ123" },
1077
+ context
1078
+ );
1079
+
1080
+ expect(result.is_error).toBeUndefined();
1081
+ expect(result.content).toContain("No matches");
1082
+ });
1083
+ });
1084
+
1085
+ describe("error handling", () => {
1086
+ test("returns error for missing pattern", async () => {
1087
+ const result = await GrepTool.handler({}, context);
1088
+
1089
+ expect(result.is_error).toBe(true);
1090
+ expect(result.content).toContain("required");
1091
+ });
1092
+
1093
+ test("returns error for empty pattern", async () => {
1094
+ const result = await GrepTool.handler({ pattern: "" }, context);
1095
+
1096
+ expect(result.is_error).toBe(true);
1097
+ expect(result.content).toContain("required");
1098
+ });
1099
+
1100
+ test("returns error for whitespace-only pattern", async () => {
1101
+ const result = await GrepTool.handler({ pattern: " " }, context);
1102
+
1103
+ expect(result.is_error).toBe(true);
1104
+ expect(result.content).toContain("required");
1105
+ });
1106
+ });
1107
+ });
1108
+
1109
+ describe("Tool Integration", () => {
1110
+ let tempDir: string;
1111
+ let context: ToolContext;
1112
+
1113
+ beforeEach(async () => {
1114
+ tempDir = await mkdtemp(join(tmpdir(), "tool-integration-test-"));
1115
+ context = createToolContext(tempDir);
1116
+ });
1117
+
1118
+ afterEach(async () => {
1119
+ await rm(tempDir, { recursive: true, force: true });
1120
+ });
1121
+
1122
+ test("Write -> Read workflow", async () => {
1123
+ const filePath = join(tempDir, "workflow.txt");
1124
+
1125
+ // Write
1126
+ const writeResult = await WriteTool.handler(
1127
+ { file_path: filePath, content: "Test content" },
1128
+ context
1129
+ );
1130
+ expect(writeResult.is_error).toBeUndefined();
1131
+
1132
+ // Read
1133
+ const readResult = await ReadTool.handler({ file_path: filePath }, context);
1134
+ expect(readResult.is_error).toBeUndefined();
1135
+ expect(readResult.content).toContain("Test content");
1136
+ });
1137
+
1138
+ test("Write -> Edit -> Read workflow", async () => {
1139
+ const filePath = join(tempDir, "edit-workflow.txt");
1140
+
1141
+ // Write
1142
+ await WriteTool.handler(
1143
+ { file_path: filePath, content: "Original content" },
1144
+ context
1145
+ );
1146
+
1147
+ // Edit
1148
+ const editResult = await EditTool.handler(
1149
+ { file_path: filePath, old_string: "Original", new_string: "Modified" },
1150
+ context
1151
+ );
1152
+ expect(editResult.is_error).toBeUndefined();
1153
+
1154
+ // Read
1155
+ const readResult = await ReadTool.handler({ file_path: filePath }, context);
1156
+ expect(readResult.content).toContain("Modified content");
1157
+ });
1158
+
1159
+ test("Bash -> Glob -> Read workflow", async () => {
1160
+ // Create files via bash
1161
+ await BashTool.handler(
1162
+ { command: "echo 'file content' > created.txt" },
1163
+ context
1164
+ );
1165
+
1166
+ // Find file via glob
1167
+ const globResult = await GlobTool.handler({ pattern: "*.txt" }, context);
1168
+ expect(globResult.content).toContain("created.txt");
1169
+
1170
+ // Extract file path and read
1171
+ const filePath = join(tempDir, "created.txt");
1172
+ const readResult = await ReadTool.handler({ file_path: filePath }, context);
1173
+ expect(readResult.content).toContain("file content");
1174
+ });
1175
+
1176
+ test("Glob -> Grep workflow", async () => {
1177
+ // Create multiple files
1178
+ await writeFile(join(tempDir, "a.ts"), "export const X = 1;");
1179
+ await writeFile(join(tempDir, "b.ts"), "export const Y = 2;");
1180
+ await writeFile(join(tempDir, "c.js"), "const Z = 3;");
1181
+
1182
+ // Find TypeScript files
1183
+ const globResult = await GlobTool.handler({ pattern: "*.ts" }, context);
1184
+ expect(globResult.content).toContain("a.ts");
1185
+ expect(globResult.content).toContain("b.ts");
1186
+ expect(globResult.content).not.toContain("c.js");
1187
+
1188
+ // Search for export in TypeScript files only
1189
+ const grepResult = await GrepTool.handler(
1190
+ { pattern: "export", glob: "*.ts" },
1191
+ context
1192
+ );
1193
+ expect(grepResult.content).toContain("export");
1194
+ });
1195
+ });
1196
+
1197
+ describe("Edge Cases", () => {
1198
+ let tempDir: string;
1199
+ let context: ToolContext;
1200
+
1201
+ beforeEach(async () => {
1202
+ tempDir = await mkdtemp(join(tmpdir(), "edge-cases-test-"));
1203
+ context = createToolContext(tempDir);
1204
+ });
1205
+
1206
+ afterEach(async () => {
1207
+ await rm(tempDir, { recursive: true, force: true });
1208
+ });
1209
+
1210
+ describe("Read edge cases", () => {
1211
+ test("handles file with very long lines", async () => {
1212
+ const filePath = join(tempDir, "long-line.txt");
1213
+ await writeFile(filePath, "x".repeat(5000));
1214
+
1215
+ const result = await ReadTool.handler({ file_path: filePath }, context);
1216
+ expect(result.is_error).toBeUndefined();
1217
+ });
1218
+
1219
+ test("handles file with only newlines", async () => {
1220
+ const filePath = join(tempDir, "newlines.txt");
1221
+ await writeFile(filePath, "\n\n\n\n");
1222
+
1223
+ const result = await ReadTool.handler({ file_path: filePath }, context);
1224
+ expect(result.is_error).toBeUndefined();
1225
+ });
1226
+
1227
+ test("handles offset beyond file length", async () => {
1228
+ const filePath = join(tempDir, "short.txt");
1229
+ await writeFile(filePath, "Short file");
1230
+
1231
+ const result = await ReadTool.handler(
1232
+ { file_path: filePath, offset: 1000 },
1233
+ context
1234
+ );
1235
+ expect(result.is_error).toBeUndefined();
1236
+ });
1237
+ });
1238
+
1239
+ describe("Write edge cases", () => {
1240
+ test("handles content with null bytes", async () => {
1241
+ const filePath = join(tempDir, "null.txt");
1242
+ const content = "Before\x00After";
1243
+
1244
+ const result = await WriteTool.handler(
1245
+ { file_path: filePath, content },
1246
+ context
1247
+ );
1248
+ expect(result.is_error).toBeUndefined();
1249
+ });
1250
+
1251
+ test("handles very long file paths", async () => {
1252
+ const longName = "a".repeat(200) + ".txt";
1253
+ const filePath = join(tempDir, longName);
1254
+
1255
+ // This may fail on some filesystems
1256
+ try {
1257
+ const result = await WriteTool.handler(
1258
+ { file_path: filePath, content: "test" },
1259
+ context
1260
+ );
1261
+ // If it succeeds, great
1262
+ expect(result).toBeDefined();
1263
+ } catch {
1264
+ // If it fails due to filesystem limits, that's expected
1265
+ }
1266
+ });
1267
+ });
1268
+
1269
+ describe("Edit edge cases", () => {
1270
+ test("handles old_string at end of file", async () => {
1271
+ const filePath = join(tempDir, "end.txt");
1272
+ await writeFile(filePath, "Start End");
1273
+
1274
+ const result = await EditTool.handler(
1275
+ { file_path: filePath, old_string: "End", new_string: "Finish" },
1276
+ context
1277
+ );
1278
+ expect(result.is_error).toBeUndefined();
1279
+
1280
+ const file = Bun.file(filePath);
1281
+ expect(await file.text()).toBe("Start Finish");
1282
+ });
1283
+
1284
+ test("handles old_string at start of file", async () => {
1285
+ const filePath = join(tempDir, "start.txt");
1286
+ await writeFile(filePath, "Start End");
1287
+
1288
+ const result = await EditTool.handler(
1289
+ { file_path: filePath, old_string: "Start", new_string: "Beginning" },
1290
+ context
1291
+ );
1292
+ expect(result.is_error).toBeUndefined();
1293
+
1294
+ const file = Bun.file(filePath);
1295
+ expect(await file.text()).toBe("Beginning End");
1296
+ });
1297
+
1298
+ test("handles multi-line old_string", async () => {
1299
+ const filePath = join(tempDir, "multi.txt");
1300
+ await writeFile(filePath, "Line 1\nLine 2\nLine 3");
1301
+
1302
+ const result = await EditTool.handler(
1303
+ {
1304
+ file_path: filePath,
1305
+ old_string: "Line 1\nLine 2",
1306
+ new_string: "Replaced",
1307
+ },
1308
+ context
1309
+ );
1310
+ expect(result.is_error).toBeUndefined();
1311
+
1312
+ const file = Bun.file(filePath);
1313
+ expect(await file.text()).toBe("Replaced\nLine 3");
1314
+ });
1315
+ });
1316
+
1317
+ describe("Bash edge cases", () => {
1318
+ test("handles command with quotes", async () => {
1319
+ const result = await BashTool.handler(
1320
+ { command: 'echo "Hello \'World\'"' },
1321
+ context
1322
+ );
1323
+ expect(result.is_error).toBeUndefined();
1324
+ expect(result.content).toContain("Hello");
1325
+ });
1326
+
1327
+ test("handles command with backslashes", async () => {
1328
+ const result = await BashTool.handler(
1329
+ { command: "echo 'test\\nvalue'" },
1330
+ context
1331
+ );
1332
+ expect(result.is_error).toBeUndefined();
1333
+ });
1334
+ });
1335
+ });