@gguf/coder 0.2.9 → 0.3.1

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 (439) hide show
  1. package/.editorconfig +16 -0
  2. package/.env.example +63 -0
  3. package/.gitattributes +1 -0
  4. package/.semgrepignore +19 -0
  5. package/coder-dummy-file.ts +52 -0
  6. package/coder.config.example.json +59 -0
  7. package/coder.config.json +13 -0
  8. package/color_picker.html +36 -0
  9. package/dist/tools/execute-bash.js +3 -3
  10. package/dist/tools/execute-bash.js.map +1 -1
  11. package/dist/tools/fetch-url.js +3 -3
  12. package/dist/tools/fetch-url.js.map +1 -1
  13. package/dist/tools/find-files.d.ts.map +1 -1
  14. package/dist/tools/find-files.js +1 -1
  15. package/dist/tools/find-files.js.map +1 -1
  16. package/dist/tools/lsp-get-diagnostics.js +1 -1
  17. package/dist/tools/lsp-get-diagnostics.js.map +1 -1
  18. package/dist/tools/read-file.d.ts.map +1 -1
  19. package/dist/tools/read-file.js +6 -6
  20. package/dist/tools/read-file.js.map +1 -1
  21. package/dist/tools/search-file-contents.d.ts.map +1 -1
  22. package/dist/tools/search-file-contents.js +1 -1
  23. package/dist/tools/search-file-contents.js.map +1 -1
  24. package/dist/tools/string-replace.js +11 -11
  25. package/dist/tools/string-replace.js.map +1 -1
  26. package/dist/tools/web-search.d.ts.map +1 -1
  27. package/dist/tools/web-search.js +3 -3
  28. package/dist/tools/web-search.js.map +1 -1
  29. package/dist/tools/write-file.js +4 -4
  30. package/dist/tools/write-file.js.map +1 -1
  31. package/dist/utils/tool-result-display.d.ts.map +1 -1
  32. package/dist/utils/tool-result-display.js +3 -3
  33. package/dist/utils/tool-result-display.js.map +1 -1
  34. package/package.json +2 -14
  35. package/scripts/extract-changelog.js +73 -0
  36. package/scripts/fetch-models.js +143 -0
  37. package/scripts/test.sh +40 -0
  38. package/scripts/update-homebrew-formula.sh +125 -0
  39. package/scripts/update-nix-version.sh +157 -0
  40. package/source/ai-sdk-client/AISDKClient.spec.ts +117 -0
  41. package/source/ai-sdk-client/AISDKClient.ts +155 -0
  42. package/source/ai-sdk-client/chat/chat-handler.spec.ts +121 -0
  43. package/source/ai-sdk-client/chat/chat-handler.ts +276 -0
  44. package/source/ai-sdk-client/chat/streaming-handler.spec.ts +173 -0
  45. package/source/ai-sdk-client/chat/streaming-handler.ts +110 -0
  46. package/source/ai-sdk-client/chat/tool-processor.spec.ts +92 -0
  47. package/source/ai-sdk-client/chat/tool-processor.ts +70 -0
  48. package/source/ai-sdk-client/converters/message-converter.spec.ts +220 -0
  49. package/source/ai-sdk-client/converters/message-converter.ts +113 -0
  50. package/source/ai-sdk-client/converters/tool-converter.spec.ts +90 -0
  51. package/source/ai-sdk-client/converters/tool-converter.ts +46 -0
  52. package/source/ai-sdk-client/error-handling/error-extractor.spec.ts +55 -0
  53. package/source/ai-sdk-client/error-handling/error-extractor.ts +15 -0
  54. package/source/ai-sdk-client/error-handling/error-parser.spec.ts +169 -0
  55. package/source/ai-sdk-client/error-handling/error-parser.ts +161 -0
  56. package/source/ai-sdk-client/index.ts +7 -0
  57. package/source/ai-sdk-client/providers/provider-factory.spec.ts +71 -0
  58. package/source/ai-sdk-client/providers/provider-factory.ts +41 -0
  59. package/source/ai-sdk-client/types.ts +9 -0
  60. package/source/ai-sdk-client-empty-message.spec.ts +141 -0
  61. package/source/ai-sdk-client-error-handling.spec.ts +186 -0
  62. package/source/ai-sdk-client-maxretries.spec.ts +114 -0
  63. package/source/ai-sdk-client-preparestep.spec.ts +279 -0
  64. package/source/app/App.spec.tsx +32 -0
  65. package/source/app/App.tsx +480 -0
  66. package/source/app/components/AppContainer.spec.tsx +96 -0
  67. package/source/app/components/AppContainer.tsx +56 -0
  68. package/source/app/components/ChatInterface.spec.tsx +163 -0
  69. package/source/app/components/ChatInterface.tsx +144 -0
  70. package/source/app/components/ModalSelectors.spec.tsx +141 -0
  71. package/source/app/components/ModalSelectors.tsx +135 -0
  72. package/source/app/helpers.spec.ts +97 -0
  73. package/source/app/helpers.ts +63 -0
  74. package/source/app/index.ts +4 -0
  75. package/source/app/types.ts +39 -0
  76. package/source/app/utils/appUtils.ts +294 -0
  77. package/source/app/utils/conversationState.ts +310 -0
  78. package/source/app.spec.tsx +244 -0
  79. package/source/cli.spec.ts +73 -0
  80. package/source/cli.tsx +51 -0
  81. package/source/client-factory.spec.ts +48 -0
  82. package/source/client-factory.ts +178 -0
  83. package/source/command-parser.spec.ts +127 -0
  84. package/source/command-parser.ts +36 -0
  85. package/source/commands/checkpoint.spec.tsx +277 -0
  86. package/source/commands/checkpoint.tsx +366 -0
  87. package/source/commands/clear.tsx +22 -0
  88. package/source/commands/custom-commands.tsx +121 -0
  89. package/source/commands/exit.ts +21 -0
  90. package/source/commands/export.spec.tsx +131 -0
  91. package/source/commands/export.tsx +79 -0
  92. package/source/commands/help.tsx +120 -0
  93. package/source/commands/index.ts +17 -0
  94. package/source/commands/init.tsx +339 -0
  95. package/source/commands/lsp-command.spec.tsx +281 -0
  96. package/source/commands/lsp.tsx +120 -0
  97. package/source/commands/mcp-command.spec.tsx +313 -0
  98. package/source/commands/mcp.tsx +162 -0
  99. package/source/commands/model-database.spec.tsx +758 -0
  100. package/source/commands/model-database.tsx +418 -0
  101. package/source/commands/model.ts +12 -0
  102. package/source/commands/provider.ts +12 -0
  103. package/source/commands/setup-config.tsx +16 -0
  104. package/source/commands/simple-commands.spec.tsx +175 -0
  105. package/source/commands/status.ts +12 -0
  106. package/source/commands/theme.ts +12 -0
  107. package/source/commands/update.spec.tsx +261 -0
  108. package/source/commands/update.tsx +201 -0
  109. package/source/commands/usage.spec.tsx +495 -0
  110. package/source/commands/usage.tsx +100 -0
  111. package/source/commands.spec.ts +436 -0
  112. package/source/commands.ts +83 -0
  113. package/source/components/assistant-message.spec.tsx +796 -0
  114. package/source/components/assistant-message.tsx +34 -0
  115. package/source/components/bash-execution-indicator.tsx +21 -0
  116. package/source/components/cancelling-indicator.tsx +16 -0
  117. package/source/components/chat-queue.spec.tsx +83 -0
  118. package/source/components/chat-queue.tsx +36 -0
  119. package/source/components/checkpoint-display.spec.tsx +219 -0
  120. package/source/components/checkpoint-display.tsx +126 -0
  121. package/source/components/checkpoint-selector.spec.tsx +173 -0
  122. package/source/components/checkpoint-selector.tsx +173 -0
  123. package/source/components/development-mode-indicator.spec.tsx +268 -0
  124. package/source/components/development-mode-indicator.tsx +38 -0
  125. package/source/components/message-box.spec.tsx +427 -0
  126. package/source/components/message-box.tsx +87 -0
  127. package/source/components/model-selector.tsx +132 -0
  128. package/source/components/provider-selector.tsx +75 -0
  129. package/source/components/random-spinner.tsx +19 -0
  130. package/source/components/security-disclaimer.tsx +73 -0
  131. package/source/components/status-connection-display.spec.tsx +133 -0
  132. package/source/components/status.tsx +267 -0
  133. package/source/components/theme-selector.tsx +126 -0
  134. package/source/components/tool-confirmation.tsx +190 -0
  135. package/source/components/tool-execution-indicator.tsx +33 -0
  136. package/source/components/tool-message.tsx +85 -0
  137. package/source/components/ui/titled-box.spec.tsx +207 -0
  138. package/source/components/ui/titled-box.tsx +57 -0
  139. package/source/components/usage/progress-bar.spec.tsx +398 -0
  140. package/source/components/usage/progress-bar.tsx +30 -0
  141. package/source/components/usage/usage-display.spec.tsx +780 -0
  142. package/source/components/usage/usage-display.tsx +291 -0
  143. package/source/components/user-input.spec.tsx +327 -0
  144. package/source/components/user-input.tsx +533 -0
  145. package/source/components/user-message.spec.tsx +230 -0
  146. package/source/components/user-message.tsx +84 -0
  147. package/source/components/welcome-message.tsx +76 -0
  148. package/source/config/env-substitution.ts +65 -0
  149. package/source/config/index.spec.ts +171 -0
  150. package/source/config/index.ts +154 -0
  151. package/source/config/paths.spec.ts +241 -0
  152. package/source/config/paths.ts +55 -0
  153. package/source/config/preferences.ts +51 -0
  154. package/source/config/themes.ts +315 -0
  155. package/source/constants.ts +130 -0
  156. package/source/context/mode-context.spec.ts +79 -0
  157. package/source/context/mode-context.ts +24 -0
  158. package/source/custom-commands/executor.spec.ts +142 -0
  159. package/source/custom-commands/executor.ts +64 -0
  160. package/source/custom-commands/loader.spec.ts +314 -0
  161. package/source/custom-commands/loader.ts +153 -0
  162. package/source/custom-commands/parser.ts +196 -0
  163. package/source/hooks/chat-handler/conversation/conversation-loop.spec.ts +39 -0
  164. package/source/hooks/chat-handler/conversation/conversation-loop.tsx +511 -0
  165. package/source/hooks/chat-handler/conversation/tool-executor.spec.ts +50 -0
  166. package/source/hooks/chat-handler/conversation/tool-executor.tsx +109 -0
  167. package/source/hooks/chat-handler/index.ts +12 -0
  168. package/source/hooks/chat-handler/state/streaming-state.spec.ts +26 -0
  169. package/source/hooks/chat-handler/state/streaming-state.ts +19 -0
  170. package/source/hooks/chat-handler/types.ts +38 -0
  171. package/source/hooks/chat-handler/useChatHandler.spec.tsx +321 -0
  172. package/source/hooks/chat-handler/useChatHandler.tsx +194 -0
  173. package/source/hooks/chat-handler/utils/context-checker.spec.ts +60 -0
  174. package/source/hooks/chat-handler/utils/context-checker.tsx +73 -0
  175. package/source/hooks/chat-handler/utils/message-helpers.spec.ts +42 -0
  176. package/source/hooks/chat-handler/utils/message-helpers.tsx +36 -0
  177. package/source/hooks/chat-handler/utils/tool-filters.spec.ts +109 -0
  178. package/source/hooks/chat-handler/utils/tool-filters.ts +64 -0
  179. package/source/hooks/useAppHandlers.tsx +291 -0
  180. package/source/hooks/useAppInitialization.tsx +422 -0
  181. package/source/hooks/useAppState.tsx +311 -0
  182. package/source/hooks/useDirectoryTrust.tsx +98 -0
  183. package/source/hooks/useInputState.ts +414 -0
  184. package/source/hooks/useModeHandlers.tsx +302 -0
  185. package/source/hooks/useNonInteractiveMode.ts +140 -0
  186. package/source/hooks/useTerminalWidth.tsx +81 -0
  187. package/source/hooks/useTheme.ts +18 -0
  188. package/source/hooks/useToolHandler.tsx +349 -0
  189. package/source/hooks/useUIState.ts +61 -0
  190. package/source/init/agents-template-generator.ts +421 -0
  191. package/source/init/existing-rules-extractor.ts +319 -0
  192. package/source/init/file-scanner.spec.ts +227 -0
  193. package/source/init/file-scanner.ts +238 -0
  194. package/source/init/framework-detector.ts +382 -0
  195. package/source/init/language-detector.ts +269 -0
  196. package/source/init/project-analyzer.spec.ts +231 -0
  197. package/source/init/project-analyzer.ts +458 -0
  198. package/source/lsp/index.ts +31 -0
  199. package/source/lsp/lsp-client.spec.ts +508 -0
  200. package/source/lsp/lsp-client.ts +487 -0
  201. package/source/lsp/lsp-manager.spec.ts +477 -0
  202. package/source/lsp/lsp-manager.ts +419 -0
  203. package/source/lsp/protocol.spec.ts +502 -0
  204. package/source/lsp/protocol.ts +360 -0
  205. package/source/lsp/server-discovery.spec.ts +654 -0
  206. package/source/lsp/server-discovery.ts +515 -0
  207. package/source/markdown-parser/html-entities.spec.ts +88 -0
  208. package/source/markdown-parser/html-entities.ts +45 -0
  209. package/source/markdown-parser/index.spec.ts +281 -0
  210. package/source/markdown-parser/index.ts +126 -0
  211. package/source/markdown-parser/table-parser.spec.ts +133 -0
  212. package/source/markdown-parser/table-parser.ts +114 -0
  213. package/source/markdown-parser/utils.spec.ts +70 -0
  214. package/source/markdown-parser/utils.ts +13 -0
  215. package/source/mcp/mcp-client.spec.ts +81 -0
  216. package/source/mcp/mcp-client.ts +625 -0
  217. package/source/mcp/transport-factory.spec.ts +406 -0
  218. package/source/mcp/transport-factory.ts +312 -0
  219. package/source/message-handler.ts +67 -0
  220. package/source/model-database/database-engine.spec.ts +494 -0
  221. package/source/model-database/database-engine.ts +50 -0
  222. package/source/model-database/model-database.spec.ts +363 -0
  223. package/source/model-database/model-database.ts +91 -0
  224. package/source/model-database/model-engine.spec.ts +447 -0
  225. package/source/model-database/model-engine.ts +65 -0
  226. package/source/model-database/model-fetcher.spec.ts +583 -0
  227. package/source/model-database/model-fetcher.ts +330 -0
  228. package/source/models/index.ts +1 -0
  229. package/source/models/models-cache.spec.ts +214 -0
  230. package/source/models/models-cache.ts +78 -0
  231. package/source/models/models-dev-client.spec.ts +379 -0
  232. package/source/models/models-dev-client.ts +329 -0
  233. package/source/models/models-types.ts +68 -0
  234. package/source/prompt-history.ts +155 -0
  235. package/source/security/command-injection.spec.ts +240 -0
  236. package/source/services/checkpoint-manager.spec.ts +523 -0
  237. package/source/services/checkpoint-manager.ts +466 -0
  238. package/source/services/file-snapshot.spec.ts +569 -0
  239. package/source/services/file-snapshot.ts +220 -0
  240. package/source/test-utils/render-with-theme.tsx +48 -0
  241. package/source/tokenization/index.ts +1 -0
  242. package/source/tokenization/tokenizer-factory.spec.ts +170 -0
  243. package/source/tokenization/tokenizer-factory.ts +125 -0
  244. package/source/tokenization/tokenizers/anthropic-tokenizer.spec.ts +200 -0
  245. package/source/tokenization/tokenizers/anthropic-tokenizer.ts +43 -0
  246. package/source/tokenization/tokenizers/fallback-tokenizer.spec.ts +236 -0
  247. package/source/tokenization/tokenizers/fallback-tokenizer.ts +26 -0
  248. package/source/tokenization/tokenizers/llama-tokenizer.spec.ts +224 -0
  249. package/source/tokenization/tokenizers/llama-tokenizer.ts +41 -0
  250. package/source/tokenization/tokenizers/openai-tokenizer.spec.ts +184 -0
  251. package/source/tokenization/tokenizers/openai-tokenizer.ts +57 -0
  252. package/source/tool-calling/index.ts +5 -0
  253. package/source/tool-calling/json-parser.spec.ts +639 -0
  254. package/source/tool-calling/json-parser.ts +247 -0
  255. package/source/tool-calling/tool-parser.spec.ts +395 -0
  256. package/source/tool-calling/tool-parser.ts +120 -0
  257. package/source/tool-calling/xml-parser.spec.ts +662 -0
  258. package/source/tool-calling/xml-parser.ts +289 -0
  259. package/source/tools/execute-bash.spec.tsx +353 -0
  260. package/source/tools/execute-bash.tsx +219 -0
  261. package/source/tools/execute-function.spec.ts +130 -0
  262. package/source/tools/fetch-url.spec.tsx +342 -0
  263. package/source/tools/fetch-url.tsx +172 -0
  264. package/source/tools/find-files.spec.tsx +924 -0
  265. package/source/tools/find-files.tsx +293 -0
  266. package/source/tools/index.ts +102 -0
  267. package/source/tools/lsp-get-diagnostics.tsx +192 -0
  268. package/source/tools/needs-approval.spec.ts +282 -0
  269. package/source/tools/read-file.spec.tsx +801 -0
  270. package/source/tools/read-file.tsx +387 -0
  271. package/source/tools/search-file-contents.spec.tsx +1273 -0
  272. package/source/tools/search-file-contents.tsx +293 -0
  273. package/source/tools/string-replace.spec.tsx +730 -0
  274. package/source/tools/string-replace.tsx +548 -0
  275. package/source/tools/tool-manager.ts +210 -0
  276. package/source/tools/tool-registry.spec.ts +415 -0
  277. package/source/tools/tool-registry.ts +228 -0
  278. package/source/tools/web-search.tsx +223 -0
  279. package/source/tools/write-file.spec.tsx +559 -0
  280. package/source/tools/write-file.tsx +228 -0
  281. package/source/types/app.ts +37 -0
  282. package/source/types/checkpoint.ts +48 -0
  283. package/source/types/commands.ts +46 -0
  284. package/source/types/components.ts +27 -0
  285. package/source/types/config.ts +103 -0
  286. package/source/types/core-connection-status.spec.ts +67 -0
  287. package/source/types/core.ts +181 -0
  288. package/source/types/hooks.ts +50 -0
  289. package/source/types/index.ts +12 -0
  290. package/source/types/markdown-parser.ts +11 -0
  291. package/source/types/mcp.ts +52 -0
  292. package/source/types/system.ts +16 -0
  293. package/source/types/tokenization.ts +41 -0
  294. package/source/types/ui.ts +40 -0
  295. package/source/types/usage.ts +58 -0
  296. package/source/types/utils.ts +16 -0
  297. package/source/usage/calculator.spec.ts +385 -0
  298. package/source/usage/calculator.ts +104 -0
  299. package/source/usage/storage.spec.ts +703 -0
  300. package/source/usage/storage.ts +238 -0
  301. package/source/usage/tracker.spec.ts +456 -0
  302. package/source/usage/tracker.ts +102 -0
  303. package/source/utils/atomic-deletion.spec.ts +194 -0
  304. package/source/utils/atomic-deletion.ts +127 -0
  305. package/source/utils/bounded-map.spec.ts +300 -0
  306. package/source/utils/bounded-map.ts +193 -0
  307. package/source/utils/checkpoint-utils.spec.ts +222 -0
  308. package/source/utils/checkpoint-utils.ts +92 -0
  309. package/source/utils/error-formatter.spec.ts +169 -0
  310. package/source/utils/error-formatter.ts +194 -0
  311. package/source/utils/file-autocomplete.spec.ts +173 -0
  312. package/source/utils/file-autocomplete.ts +196 -0
  313. package/source/utils/file-cache.spec.ts +309 -0
  314. package/source/utils/file-cache.ts +195 -0
  315. package/source/utils/file-content-loader.spec.ts +180 -0
  316. package/source/utils/file-content-loader.ts +179 -0
  317. package/source/utils/file-mention-handler.spec.ts +261 -0
  318. package/source/utils/file-mention-handler.ts +84 -0
  319. package/source/utils/file-mention-parser.spec.ts +182 -0
  320. package/source/utils/file-mention-parser.ts +170 -0
  321. package/source/utils/fuzzy-matching.spec.ts +149 -0
  322. package/source/utils/fuzzy-matching.ts +146 -0
  323. package/source/utils/indentation-normalizer.spec.ts +216 -0
  324. package/source/utils/indentation-normalizer.ts +76 -0
  325. package/source/utils/installation-detector.spec.ts +178 -0
  326. package/source/utils/installation-detector.ts +153 -0
  327. package/source/utils/logging/config.spec.ts +311 -0
  328. package/source/utils/logging/config.ts +210 -0
  329. package/source/utils/logging/console-facade.spec.ts +184 -0
  330. package/source/utils/logging/console-facade.ts +384 -0
  331. package/source/utils/logging/correlation.spec.ts +679 -0
  332. package/source/utils/logging/correlation.ts +474 -0
  333. package/source/utils/logging/formatters.spec.ts +464 -0
  334. package/source/utils/logging/formatters.ts +207 -0
  335. package/source/utils/logging/health-monitor/alerts/alert-manager.spec.ts +93 -0
  336. package/source/utils/logging/health-monitor/alerts/alert-manager.ts +79 -0
  337. package/source/utils/logging/health-monitor/checks/configuration-check.spec.ts +56 -0
  338. package/source/utils/logging/health-monitor/checks/configuration-check.ts +43 -0
  339. package/source/utils/logging/health-monitor/checks/logging-check.spec.ts +56 -0
  340. package/source/utils/logging/health-monitor/checks/logging-check.ts +58 -0
  341. package/source/utils/logging/health-monitor/checks/memory-check.spec.ts +100 -0
  342. package/source/utils/logging/health-monitor/checks/memory-check.ts +78 -0
  343. package/source/utils/logging/health-monitor/checks/performance-check.spec.ts +56 -0
  344. package/source/utils/logging/health-monitor/checks/performance-check.ts +56 -0
  345. package/source/utils/logging/health-monitor/checks/request-check.spec.ts +56 -0
  346. package/source/utils/logging/health-monitor/checks/request-check.ts +76 -0
  347. package/source/utils/logging/health-monitor/core/health-check-runner.spec.ts +70 -0
  348. package/source/utils/logging/health-monitor/core/health-check-runner.ts +138 -0
  349. package/source/utils/logging/health-monitor/core/health-monitor.spec.ts +58 -0
  350. package/source/utils/logging/health-monitor/core/health-monitor.ts +344 -0
  351. package/source/utils/logging/health-monitor/core/scoring.spec.ts +65 -0
  352. package/source/utils/logging/health-monitor/core/scoring.ts +91 -0
  353. package/source/utils/logging/health-monitor/index.ts +15 -0
  354. package/source/utils/logging/health-monitor/instances.ts +48 -0
  355. package/source/utils/logging/health-monitor/middleware/http-middleware.spec.ts +141 -0
  356. package/source/utils/logging/health-monitor/middleware/http-middleware.ts +75 -0
  357. package/source/utils/logging/health-monitor/types.ts +126 -0
  358. package/source/utils/logging/index.spec.ts +284 -0
  359. package/source/utils/logging/index.ts +236 -0
  360. package/source/utils/logging/integration.spec.ts +441 -0
  361. package/source/utils/logging/log-method-factory.spec.ts +573 -0
  362. package/source/utils/logging/log-method-factory.ts +233 -0
  363. package/source/utils/logging/log-query/aggregation/aggregator.spec.ts +277 -0
  364. package/source/utils/logging/log-query/aggregation/aggregator.ts +159 -0
  365. package/source/utils/logging/log-query/aggregation/facet-generator.spec.ts +159 -0
  366. package/source/utils/logging/log-query/aggregation/facet-generator.ts +47 -0
  367. package/source/utils/logging/log-query/index.ts +23 -0
  368. package/source/utils/logging/log-query/query/filter-predicates.spec.ts +247 -0
  369. package/source/utils/logging/log-query/query/filter-predicates.ts +154 -0
  370. package/source/utils/logging/log-query/query/query-builder.spec.ts +182 -0
  371. package/source/utils/logging/log-query/query/query-builder.ts +151 -0
  372. package/source/utils/logging/log-query/query/query-engine.spec.ts +214 -0
  373. package/source/utils/logging/log-query/query/query-engine.ts +45 -0
  374. package/source/utils/logging/log-query/storage/circular-buffer.spec.ts +143 -0
  375. package/source/utils/logging/log-query/storage/circular-buffer.ts +75 -0
  376. package/source/utils/logging/log-query/storage/index-manager.spec.ts +150 -0
  377. package/source/utils/logging/log-query/storage/index-manager.ts +71 -0
  378. package/source/utils/logging/log-query/storage/log-storage.spec.ts +257 -0
  379. package/source/utils/logging/log-query/storage/log-storage.ts +80 -0
  380. package/source/utils/logging/log-query/types.ts +163 -0
  381. package/source/utils/logging/log-query/utils/helpers.spec.ts +263 -0
  382. package/source/utils/logging/log-query/utils/helpers.ts +72 -0
  383. package/source/utils/logging/log-query/utils/sorting.spec.ts +182 -0
  384. package/source/utils/logging/log-query/utils/sorting.ts +61 -0
  385. package/source/utils/logging/logger-provider.spec.ts +262 -0
  386. package/source/utils/logging/logger-provider.ts +362 -0
  387. package/source/utils/logging/performance.spec.ts +209 -0
  388. package/source/utils/logging/performance.ts +757 -0
  389. package/source/utils/logging/pino-logger.spec.ts +425 -0
  390. package/source/utils/logging/pino-logger.ts +514 -0
  391. package/source/utils/logging/redaction.spec.ts +490 -0
  392. package/source/utils/logging/redaction.ts +267 -0
  393. package/source/utils/logging/request-tracker.spec.ts +1198 -0
  394. package/source/utils/logging/request-tracker.ts +803 -0
  395. package/source/utils/logging/transports.spec.ts +505 -0
  396. package/source/utils/logging/transports.ts +305 -0
  397. package/source/utils/logging/types.ts +216 -0
  398. package/source/utils/message-builder.spec.ts +179 -0
  399. package/source/utils/message-builder.ts +101 -0
  400. package/source/utils/message-queue.tsx +486 -0
  401. package/source/utils/paste-detection.spec.ts +69 -0
  402. package/source/utils/paste-detection.ts +124 -0
  403. package/source/utils/paste-roundtrip.spec.ts +442 -0
  404. package/source/utils/paste-utils.spec.ts +128 -0
  405. package/source/utils/paste-utils.ts +52 -0
  406. package/source/utils/programming-language-helper.spec.ts +74 -0
  407. package/source/utils/programming-language-helper.ts +32 -0
  408. package/source/utils/prompt-assembly.spec.ts +221 -0
  409. package/source/utils/prompt-processor.ts +173 -0
  410. package/source/utils/tool-args-parser.spec.ts +136 -0
  411. package/source/utils/tool-args-parser.ts +54 -0
  412. package/source/utils/tool-cancellation.spec.ts +230 -0
  413. package/source/utils/tool-cancellation.ts +28 -0
  414. package/source/utils/tool-result-display.spec.tsx +469 -0
  415. package/source/utils/tool-result-display.tsx +90 -0
  416. package/source/utils/update-checker.spec.ts +383 -0
  417. package/source/utils/update-checker.ts +183 -0
  418. package/source/wizard/config-wizard.spec.tsx +103 -0
  419. package/source/wizard/config-wizard.tsx +382 -0
  420. package/source/wizard/steps/location-step.spec.tsx +186 -0
  421. package/source/wizard/steps/location-step.tsx +147 -0
  422. package/source/wizard/steps/mcp-step.spec.tsx +607 -0
  423. package/source/wizard/steps/mcp-step.tsx +632 -0
  424. package/source/wizard/steps/provider-step.spec.tsx +342 -0
  425. package/source/wizard/steps/provider-step.tsx +957 -0
  426. package/source/wizard/steps/summary-step.spec.tsx +749 -0
  427. package/source/wizard/steps/summary-step.tsx +228 -0
  428. package/source/wizard/templates/mcp-templates.spec.ts +613 -0
  429. package/source/wizard/templates/mcp-templates.ts +570 -0
  430. package/source/wizard/templates/provider-templates.spec.ts +152 -0
  431. package/source/wizard/templates/provider-templates.ts +485 -0
  432. package/source/wizard/utils/fetch-cloud-models.spec.ts +428 -0
  433. package/source/wizard/utils/fetch-cloud-models.ts +223 -0
  434. package/source/wizard/utils/fetch-local-models.spec.ts +297 -0
  435. package/source/wizard/utils/fetch-local-models.ts +192 -0
  436. package/source/wizard/validation-array.spec.ts +264 -0
  437. package/source/wizard/validation.spec.ts +373 -0
  438. package/source/wizard/validation.ts +232 -0
  439. package/source/app/prompts/main-prompt.md +0 -122
@@ -0,0 +1,219 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { platform } from 'node:os';
4
+ import { highlight } from 'cli-highlight';
5
+ import { Box, Text } from 'ink';
6
+ import React from 'react';
7
+
8
+ import ToolMessage from '@/components/tool-message';
9
+ import { TRUNCATION_OUTPUT_LIMIT } from '@/constants';
10
+ import { ThemeContext } from '@/hooks/useTheme';
11
+ import { jsonSchema, tool } from '@/types/core';
12
+ import type { CoderToolExport } from '@/types/core';
13
+
14
+ const runShellCommand = (
15
+ shell: string,
16
+ shellArgs: string[],
17
+ script: string,
18
+ ): Promise<string> => {
19
+ return new Promise((resolve, reject) => {
20
+ const proc = spawn(shell, [...shellArgs, script]);
21
+ let stdout = '';
22
+ let stderr = '';
23
+
24
+ proc.stdout.on('data', (data: Buffer) => {
25
+ stdout += data.toString();
26
+ });
27
+
28
+ proc.stderr.on('data', (data: Buffer) => {
29
+ stderr += data.toString();
30
+ });
31
+
32
+ proc.on('close', (code: number | null) => {
33
+ let fullOutput = '';
34
+
35
+ // Include exit code information
36
+ const exitCodeInfo = code !== null ? `EXIT_CODE: ${code}\n` : '';
37
+
38
+ if (stderr) {
39
+ fullOutput = `${exitCodeInfo}STDERR:
40
+ ${stderr}
41
+ STDOUT:
42
+ ${stdout}`;
43
+ } else {
44
+ fullOutput = `${exitCodeInfo}${stdout}`;
45
+ }
46
+
47
+ // Limit the context for LLM to first TRUNCATION_OUTPUT_LIMIT characters to prevent overwhelming the model
48
+ const llmContext =
49
+ fullOutput.length > TRUNCATION_OUTPUT_LIMIT
50
+ ? fullOutput.substring(0, TRUNCATION_OUTPUT_LIMIT) +
51
+ '\n... [Output truncated. Use more specific commands to see full output]'
52
+ : fullOutput;
53
+
54
+ // Return ONLY the llmContext to avoid sending massive outputs to the model
55
+ // The formatter will need to be updated to handle plain strings
56
+ resolve(llmContext);
57
+ });
58
+
59
+ proc.on('error', error => {
60
+ reject(error);
61
+ });
62
+ });
63
+ };
64
+
65
+ const executeExecuteBash = async (args: { command: string }): Promise<string> => {
66
+ try {
67
+ return await runShellCommand('sh', ['-c'], args.command);
68
+ } catch (error: any) {
69
+ if (error.code === 'ENOENT' && platform() === 'win32') {
70
+ // Try to find Git Bash in common locations
71
+ const gitBashPaths = [
72
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
73
+ 'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
74
+ `C:\\Users\\${process.env['USERNAME'] || 'User'
75
+ }\\AppData\\Local\\Programs\\Git\\bin\\bash.exe`,
76
+ ];
77
+
78
+ for (const bashPath of gitBashPaths) {
79
+ if (existsSync(bashPath)) {
80
+ try {
81
+ return await runShellCommand(bashPath, ['-c'], args.command);
82
+ } catch {
83
+ // Continue to next fallback
84
+ }
85
+ }
86
+ }
87
+
88
+ // Fallback to PowerShell
89
+ try {
90
+ return await runShellCommand('powershell', ['-Command'], args.command);
91
+ } catch {
92
+ // If PowerShell also fails, throw the original error
93
+ throw new Error(`Error executing command: ${error.message}`);
94
+ }
95
+ }
96
+
97
+ throw new Error(`Error executing command: ${error.message}`);
98
+ }
99
+ };
100
+
101
+ const executeBashCoreTool = tool({
102
+ description:
103
+ 'Execute a bash command and return the output (use for running commands)',
104
+ inputSchema: jsonSchema<{ command: string }>({
105
+ type: 'object',
106
+ properties: {
107
+ command: {
108
+ type: 'string',
109
+ description: 'The bash command to execute.',
110
+ },
111
+ },
112
+ required: ['command'],
113
+ }),
114
+ // High risk: bash commands always require approval in all modes
115
+ needsApproval: true,
116
+ execute: async (args, _options) => {
117
+ return await executeExecuteBash(args);
118
+ },
119
+ });
120
+
121
+ // Create a component that will re-render when theme changes
122
+ const ExecuteBashFormatter = React.memo(
123
+ ({ args, result }: { args: { command: string }; result?: string }) => {
124
+ const themeContext = React.useContext(ThemeContext);
125
+ if (!themeContext) {
126
+ throw new Error('ThemeContext is required');
127
+ }
128
+ const { colors } = themeContext;
129
+ const command = args.command || 'unknown';
130
+
131
+ try {
132
+ highlight(command, {
133
+ language: 'bash',
134
+ theme: 'default',
135
+ });
136
+ } catch {
137
+ // Syntax highlighting failed, will use plain command
138
+ }
139
+
140
+ // Result is now a plain string (truncated output)
141
+ let outputSize = 0;
142
+ let estimatedTokens = 0;
143
+ if (result) {
144
+ outputSize = result.length;
145
+ estimatedTokens = Math.ceil(outputSize / 4); // ~4 characters per token
146
+ }
147
+
148
+ const messageContent = (
149
+ <Box flexDirection="column">
150
+ <Text color={colors.tool}>☕︎ execute_bash</Text>
151
+
152
+ <Box>
153
+ <Text color={colors.secondary}>Command: </Text>
154
+ <Text color={colors.primary}>{command}</Text>
155
+ </Box>
156
+
157
+ {result && (
158
+ <Box>
159
+ <Text color={colors.secondary}>Output: </Text>
160
+ <Text color={colors.white}>
161
+ {outputSize} characters (~{estimatedTokens} tokens sent to LLM)
162
+ </Text>
163
+ </Box>
164
+ )}
165
+ </Box>
166
+ );
167
+
168
+ return <ToolMessage message={messageContent} hideBox={true} />;
169
+ },
170
+ );
171
+
172
+ const executeBashFormatter = (
173
+ args: { command: string },
174
+ result?: string,
175
+ ): React.ReactElement => {
176
+ return <ExecuteBashFormatter args={args} result={result} />;
177
+ };
178
+
179
+ const executeBashValidator = (args: {
180
+ command: string;
181
+ }): Promise<{ valid: true } | { valid: false; error: string }> => {
182
+ const command = args.command?.trim();
183
+
184
+ // Check if command is empty
185
+ if (!command) {
186
+ return Promise.resolve({
187
+ valid: false,
188
+ error: '☕︎ Command cannot be empty',
189
+ });
190
+ }
191
+
192
+ // Check for extremely dangerous commands
193
+ const dangerousPatterns = [
194
+ /rm\s+-rf\s+\/(?!\w)/i, // rm -rf / (but allow /path)
195
+ /mkfs/i, // Format filesystem
196
+ /dd\s+if=/i, // Direct disk write
197
+ /:(){:|:&};:/i, // Fork bomb
198
+ />\s*\/dev\/sd[a-z]/i, // Writing to raw disk devices
199
+ /chmod\s+-R\s+000/i, // Remove all permissions recursively
200
+ ];
201
+
202
+ for (const pattern of dangerousPatterns) {
203
+ if (pattern.test(command)) {
204
+ return Promise.resolve({
205
+ valid: false,
206
+ error: `☕︎ Command contains potentially destructive operation: "${command}". This command is blocked for safety.`,
207
+ });
208
+ }
209
+ }
210
+
211
+ return Promise.resolve({ valid: true });
212
+ };
213
+
214
+ export const executeBashTool: CoderToolExport = {
215
+ name: 'execute_bash' as const,
216
+ tool: executeBashCoreTool,
217
+ formatter: executeBashFormatter,
218
+ validator: executeBashValidator,
219
+ };
@@ -0,0 +1,130 @@
1
+ import {tmpdir} from 'os';
2
+ import {resolve} from 'path';
3
+ import test from 'ava';
4
+ import {unlink, writeFile} from 'fs/promises';
5
+ import {executeBashTool} from './execute-bash.js';
6
+ import {readFileTool} from './read-file.js';
7
+
8
+ // ============================================================================
9
+ // Tests for AI SDK v6 Native Tools
10
+ // ============================================================================
11
+ // These tests validate that the native AI SDK tools work correctly with their
12
+ // execute functions. Since tools now only export the native AI SDK tool,
13
+ // there's no separate handler to compare against.
14
+
15
+ // ============================================================================
16
+ // Bash Execute Function
17
+ // ============================================================================
18
+
19
+ test('execute_bash tool works correctly', async t => {
20
+ const result = (await executeBashTool.tool.execute!(
21
+ {command: 'echo "test"'},
22
+ {
23
+ toolCallId: 'test-1',
24
+ messages: [],
25
+ },
26
+ )) as string;
27
+
28
+ t.truthy(result);
29
+ t.regex(result, /test/);
30
+ });
31
+
32
+ test('execute_bash includes exit code', async t => {
33
+ const result = (await executeBashTool.tool.execute!(
34
+ {command: 'echo "success"'},
35
+ {
36
+ toolCallId: 'test-2',
37
+ messages: [],
38
+ },
39
+ )) as string;
40
+
41
+ t.truthy(result);
42
+ t.regex(result, /EXIT_CODE: 0/);
43
+ });
44
+
45
+ test('execute_bash captures stderr', async t => {
46
+ const result = (await executeBashTool.tool.execute!(
47
+ {command: 'echo "error" >&2'},
48
+ {
49
+ toolCallId: 'test-3',
50
+ messages: [],
51
+ },
52
+ )) as string;
53
+
54
+ t.truthy(result);
55
+ t.regex(result, /STDERR:/);
56
+ t.regex(result, /error/);
57
+ });
58
+
59
+ test('execute_bash tool name constant is correct', t => {
60
+ t.is(executeBashTool.name, 'execute_bash');
61
+ });
62
+
63
+ // ============================================================================
64
+ // Read File Execute Function
65
+ // ============================================================================
66
+
67
+ test('read_file reads existing file', async t => {
68
+ const testFile = resolve(tmpdir(), `coder-test-${Date.now()}.txt`);
69
+ await writeFile(testFile, 'test content', 'utf-8');
70
+
71
+ try {
72
+ const result = (await readFileTool.tool.execute!(
73
+ {path: testFile},
74
+ {
75
+ toolCallId: 'test-4',
76
+ messages: [],
77
+ },
78
+ )) as string;
79
+
80
+ t.truthy(result);
81
+ t.regex(result, /test content/);
82
+ } finally {
83
+ await unlink(testFile).catch(() => {});
84
+ }
85
+ });
86
+
87
+ test('read_file handles non-existent file', async t => {
88
+ const nonExistentFile = resolve(tmpdir(), 'non-existent-file.txt');
89
+
90
+ await t.throwsAsync(
91
+ async () => {
92
+ await readFileTool.tool.execute!(
93
+ {path: nonExistentFile},
94
+ {
95
+ toolCallId: 'test-5',
96
+ messages: [],
97
+ },
98
+ );
99
+ },
100
+ {message: /does not exist|no such file or directory|ENOENT/i},
101
+ );
102
+ });
103
+
104
+ test('read_file reads with line ranges', async t => {
105
+ const testFile = resolve(tmpdir(), `coder-test-${Date.now()}.txt`);
106
+ await writeFile(testFile, 'line1\nline2\nline3\nline4\nline5', 'utf-8');
107
+
108
+ try {
109
+ const result = (await readFileTool.tool.execute!(
110
+ {path: testFile, start_line: 2, end_line: 4},
111
+ {
112
+ toolCallId: 'test-6',
113
+ messages: [],
114
+ },
115
+ )) as string;
116
+
117
+ t.truthy(result);
118
+ t.regex(result, /line2/);
119
+ t.regex(result, /line3/);
120
+ t.regex(result, /line4/);
121
+ t.notRegex(result, /line1/);
122
+ t.notRegex(result, /line5/);
123
+ } finally {
124
+ await unlink(testFile).catch(() => {});
125
+ }
126
+ });
127
+
128
+ test('read_file tool name constant is correct', t => {
129
+ t.is(readFileTool.name, 'read_file');
130
+ });
@@ -0,0 +1,342 @@
1
+ import test from 'ava';
2
+ import {render} from 'ink-testing-library';
3
+ import React from 'react';
4
+ import {themes} from '../config/themes';
5
+ import {ThemeContext} from '../hooks/useTheme';
6
+
7
+ console.log(`\nfetch-url.spec.tsx – ${React.version}`);
8
+
9
+ // Polyfill File for undici in Node.js test environment
10
+ if (typeof File === 'undefined') {
11
+ (global as any).File = class File {
12
+ constructor(
13
+ public parts: any[],
14
+ public name: string,
15
+ public options?: any,
16
+ ) {}
17
+ };
18
+ }
19
+
20
+ // Dynamically import to avoid loading undici in test environment
21
+ let fetchUrlTool: any;
22
+
23
+ test.before(async () => {
24
+ // Only import when we need it, and handle the case where undici might not work
25
+ try {
26
+ const module = await import('./fetch-url.js');
27
+ fetchUrlTool = module.fetchUrlTool;
28
+ } catch (error) {
29
+ // If undici fails to load (e.g., in CI), we'll skip handler tests
30
+ console.warn('Failed to load fetch-url module:', error);
31
+ }
32
+ });
33
+
34
+ // Mock ThemeProvider for testing
35
+ const MockThemeProvider = ({children}: {children: React.ReactNode}) => {
36
+ const mockTheme = {
37
+ currentTheme: 'default' as const,
38
+ colors: themes['tokyo-night'].colors, // Use tokyo-night theme colors
39
+ setCurrentTheme: () => {},
40
+ };
41
+
42
+ return (
43
+ <ThemeContext.Provider value={mockTheme}>{children}</ThemeContext.Provider>
44
+ );
45
+ };
46
+
47
+ // Note: These tests validate the tool configuration and validator logic.
48
+ // Handler tests that make actual network requests are skipped to avoid flakiness.
49
+ // For handler testing, consider using integration tests with known stable URLs
50
+ // or mocking the convertToMarkdown function at the module level.
51
+
52
+ test('handler validates URL format', async t => {
53
+ if (!fetchUrlTool) {
54
+ t.pass('Skipping test - fetch-url module not available');
55
+ return;
56
+ }
57
+
58
+ await t.throwsAsync(
59
+ async () => {
60
+ await fetchUrlTool.tool.execute!(
61
+ {url: 'not-a-valid-url'},
62
+ {toolCallId: 'test', messages: []},
63
+ );
64
+ },
65
+ {message: /Invalid URL/},
66
+ );
67
+ });
68
+
69
+ test('validator accepts valid HTTP URLs', async t => {
70
+ if (!fetchUrlTool) {
71
+ t.pass('Skipping test - fetch-url module not available');
72
+ return;
73
+ }
74
+ const result = await fetchUrlTool.validator!({url: 'https://example.com'});
75
+
76
+ t.true(result.valid);
77
+ });
78
+
79
+ test('validator accepts valid HTTPS URLs', async t => {
80
+ if (!fetchUrlTool) {
81
+ t.pass('Skipping test - fetch-url module not available');
82
+ return;
83
+ }
84
+ const result = await fetchUrlTool.validator!({url: 'http://example.com'});
85
+
86
+ t.true(result.valid);
87
+ });
88
+
89
+ test('validator rejects invalid URL formats', async t => {
90
+ if (!fetchUrlTool) {
91
+ t.pass('Skipping test - fetch-url module not available');
92
+ return;
93
+ }
94
+ const result = await fetchUrlTool.validator!({url: 'not a url'});
95
+
96
+ t.false(result.valid);
97
+ if (!result.valid) {
98
+ t.true(result.error.includes('Invalid URL format'));
99
+ }
100
+ });
101
+
102
+ test('validator rejects non-HTTP/HTTPS protocols', async t => {
103
+ if (!fetchUrlTool) {
104
+ t.pass('Skipping test - fetch-url module not available');
105
+ return;
106
+ }
107
+ const result = await fetchUrlTool.validator!({url: 'ftp://example.com'});
108
+
109
+ t.false(result.valid);
110
+ if (!result.valid) {
111
+ t.true(result.error.includes('Invalid URL protocol'));
112
+ t.true(result.error.includes('ftp:'));
113
+ }
114
+ });
115
+
116
+ test('validator rejects localhost URLs', async t => {
117
+ if (!fetchUrlTool) {
118
+ t.pass('Skipping test - fetch-url module not available');
119
+ return;
120
+ }
121
+ const result = await fetchUrlTool.validator!({url: 'http://localhost:3000'});
122
+
123
+ t.false(result.valid);
124
+ if (!result.valid) {
125
+ t.true(result.error.includes('internal/private network'));
126
+ }
127
+ });
128
+
129
+ test('validator rejects 127.0.0.1 URLs', async t => {
130
+ if (!fetchUrlTool) {
131
+ t.pass('Skipping test - fetch-url module not available');
132
+ return;
133
+ }
134
+ const result = await fetchUrlTool.validator!({
135
+ url: 'http://127.0.0.1:8080',
136
+ });
137
+
138
+ t.false(result.valid);
139
+ if (!result.valid) {
140
+ t.true(result.error.includes('internal/private network'));
141
+ }
142
+ });
143
+
144
+ test('validator rejects 192.168.x.x URLs', async t => {
145
+ if (!fetchUrlTool) {
146
+ t.pass('Skipping test - fetch-url module not available');
147
+ return;
148
+ }
149
+ const result = await fetchUrlTool.validator!({
150
+ url: 'http://192.168.1.1',
151
+ });
152
+
153
+ t.false(result.valid);
154
+ if (!result.valid) {
155
+ t.true(result.error.includes('internal/private network'));
156
+ }
157
+ });
158
+
159
+ test('validator rejects 10.x.x.x URLs', async t => {
160
+ if (!fetchUrlTool) {
161
+ t.pass('Skipping test - fetch-url module not available');
162
+ return;
163
+ }
164
+ const result = await fetchUrlTool.validator!({url: 'http://10.0.0.1'});
165
+
166
+ t.false(result.valid);
167
+ if (!result.valid) {
168
+ t.true(result.error.includes('internal/private network'));
169
+ }
170
+ });
171
+
172
+ test('validator rejects 172.16-31.x.x URLs', async t => {
173
+ if (!fetchUrlTool) {
174
+ t.pass('Skipping test - fetch-url module not available');
175
+ return;
176
+ }
177
+ const result = await fetchUrlTool.validator!({url: 'http://172.16.0.1'});
178
+
179
+ t.false(result.valid);
180
+ if (!result.valid) {
181
+ t.true(result.error.includes('internal/private network'));
182
+ }
183
+ });
184
+
185
+ test('validator accepts external IP addresses', async t => {
186
+ if (!fetchUrlTool) {
187
+ t.pass('Skipping test - fetch-url module not available');
188
+ return;
189
+ }
190
+ const result = await fetchUrlTool.validator!({url: 'http://8.8.8.8'});
191
+
192
+ t.true(result.valid);
193
+ });
194
+
195
+ test('tool has correct name', t => {
196
+ if (!fetchUrlTool) {
197
+ t.pass('Skipping test - fetch-url module not available');
198
+ return;
199
+ }
200
+ t.is(fetchUrlTool.name, 'fetch_url');
201
+ });
202
+
203
+ test('tool does not require confirmation', t => {
204
+ if (!fetchUrlTool) {
205
+ t.pass('Skipping test - fetch-url module not available');
206
+ return;
207
+ }
208
+ t.false(fetchUrlTool.tool.needsApproval);
209
+ });
210
+
211
+ test('formatter is a function', t => {
212
+ if (!fetchUrlTool) {
213
+ t.pass('Skipping test - fetch-url module not available');
214
+ return;
215
+ }
216
+ t.is(typeof fetchUrlTool.formatter, 'function');
217
+ });
218
+
219
+ test('formatter returns a Promise', async t => {
220
+ if (!fetchUrlTool) {
221
+ t.pass('Skipping test - fetch-url module not available');
222
+ return;
223
+ }
224
+ const result = fetchUrlTool.formatter!({url: 'https://example.com'});
225
+
226
+ t.true(result instanceof Promise);
227
+ });
228
+
229
+ // ============================================================================
230
+ // Component Rendering Tests
231
+ // ============================================================================
232
+
233
+ test('formatter renders component with URL', async t => {
234
+ if (!fetchUrlTool) {
235
+ t.pass('Skipping test - fetch-url module not available');
236
+ return;
237
+ }
238
+ const component = await fetchUrlTool.formatter!({
239
+ url: 'https://example.com',
240
+ });
241
+
242
+ const {lastFrame} = render(
243
+ <MockThemeProvider>{component}</MockThemeProvider>,
244
+ );
245
+
246
+ const output = lastFrame();
247
+ t.truthy(output);
248
+ t.regex(output!, /fetch_url/);
249
+ t.regex(output!, /https:\/\/example\.com/);
250
+ });
251
+
252
+ test('formatter renders component with result stats', async t => {
253
+ if (!fetchUrlTool) {
254
+ t.pass('Skipping test - fetch-url module not available');
255
+ return;
256
+ }
257
+ const mockResult = 'Test content with some markdown';
258
+
259
+ const component = await fetchUrlTool.formatter!(
260
+ {url: 'https://example.com'},
261
+ mockResult,
262
+ );
263
+
264
+ const {lastFrame} = render(
265
+ <MockThemeProvider>{component}</MockThemeProvider>,
266
+ );
267
+
268
+ const output = lastFrame();
269
+ t.truthy(output);
270
+ t.regex(output!, /fetch_url/);
271
+ t.regex(output!, /https:\/\/example\.com/);
272
+ t.regex(output!, /Content:/);
273
+ t.regex(output!, /characters/);
274
+ t.regex(output!, /tokens/);
275
+ });
276
+
277
+ test('formatter shows truncation warning when content is truncated', async t => {
278
+ if (!fetchUrlTool) {
279
+ t.pass('Skipping test - fetch-url module not available');
280
+ return;
281
+ }
282
+ const truncatedResult =
283
+ 'x'.repeat(100000) +
284
+ '\n\n[Content truncated - original size was 150000 characters]';
285
+
286
+ const component = await fetchUrlTool.formatter!(
287
+ {url: 'https://large-content.com'},
288
+ truncatedResult,
289
+ );
290
+
291
+ const {lastFrame} = render(
292
+ <MockThemeProvider>{component}</MockThemeProvider>,
293
+ );
294
+
295
+ const output = lastFrame();
296
+ t.truthy(output);
297
+ t.regex(output!, /Content was truncated to 100KB/);
298
+ });
299
+
300
+ test('formatter renders without result (before execution)', async t => {
301
+ if (!fetchUrlTool) {
302
+ t.pass('Skipping test - fetch-url module not available');
303
+ return;
304
+ }
305
+ const component = await fetchUrlTool.formatter!({
306
+ url: 'https://example.com',
307
+ });
308
+
309
+ const {lastFrame} = render(
310
+ <MockThemeProvider>{component}</MockThemeProvider>,
311
+ );
312
+
313
+ const output = lastFrame();
314
+ t.truthy(output);
315
+ t.regex(output!, /fetch_url/);
316
+ t.regex(output!, /https:\/\/example\.com/);
317
+ // Should not show content stats before execution
318
+ t.notRegex(output!, /Content:/);
319
+ });
320
+
321
+ test('formatter calculates token estimate correctly', async t => {
322
+ if (!fetchUrlTool) {
323
+ t.pass('Skipping test - fetch-url module not available');
324
+ return;
325
+ }
326
+ // 100 characters should estimate ~25 tokens (divide by 4)
327
+ const mockResult = 'a'.repeat(100);
328
+
329
+ const component = await fetchUrlTool.formatter!(
330
+ {url: 'https://example.com'},
331
+ mockResult,
332
+ );
333
+
334
+ const {lastFrame} = render(
335
+ <MockThemeProvider>{component}</MockThemeProvider>,
336
+ );
337
+
338
+ const output = lastFrame();
339
+ t.truthy(output);
340
+ t.regex(output!, /100.*characters/);
341
+ t.regex(output!, /25.*tokens/);
342
+ });