@gguf/coder 0.3.0 → 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 (414) 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/package.json +2 -14
  10. package/scripts/extract-changelog.js +73 -0
  11. package/scripts/fetch-models.js +143 -0
  12. package/scripts/test.sh +40 -0
  13. package/scripts/update-homebrew-formula.sh +125 -0
  14. package/scripts/update-nix-version.sh +157 -0
  15. package/source/ai-sdk-client/AISDKClient.spec.ts +117 -0
  16. package/source/ai-sdk-client/AISDKClient.ts +155 -0
  17. package/source/ai-sdk-client/chat/chat-handler.spec.ts +121 -0
  18. package/source/ai-sdk-client/chat/chat-handler.ts +276 -0
  19. package/source/ai-sdk-client/chat/streaming-handler.spec.ts +173 -0
  20. package/source/ai-sdk-client/chat/streaming-handler.ts +110 -0
  21. package/source/ai-sdk-client/chat/tool-processor.spec.ts +92 -0
  22. package/source/ai-sdk-client/chat/tool-processor.ts +70 -0
  23. package/source/ai-sdk-client/converters/message-converter.spec.ts +220 -0
  24. package/source/ai-sdk-client/converters/message-converter.ts +113 -0
  25. package/source/ai-sdk-client/converters/tool-converter.spec.ts +90 -0
  26. package/source/ai-sdk-client/converters/tool-converter.ts +46 -0
  27. package/source/ai-sdk-client/error-handling/error-extractor.spec.ts +55 -0
  28. package/source/ai-sdk-client/error-handling/error-extractor.ts +15 -0
  29. package/source/ai-sdk-client/error-handling/error-parser.spec.ts +169 -0
  30. package/source/ai-sdk-client/error-handling/error-parser.ts +161 -0
  31. package/source/ai-sdk-client/index.ts +7 -0
  32. package/source/ai-sdk-client/providers/provider-factory.spec.ts +71 -0
  33. package/source/ai-sdk-client/providers/provider-factory.ts +41 -0
  34. package/source/ai-sdk-client/types.ts +9 -0
  35. package/source/ai-sdk-client-empty-message.spec.ts +141 -0
  36. package/source/ai-sdk-client-error-handling.spec.ts +186 -0
  37. package/source/ai-sdk-client-maxretries.spec.ts +114 -0
  38. package/source/ai-sdk-client-preparestep.spec.ts +279 -0
  39. package/source/app/App.spec.tsx +32 -0
  40. package/source/app/App.tsx +480 -0
  41. package/source/app/components/AppContainer.spec.tsx +96 -0
  42. package/source/app/components/AppContainer.tsx +56 -0
  43. package/source/app/components/ChatInterface.spec.tsx +163 -0
  44. package/source/app/components/ChatInterface.tsx +144 -0
  45. package/source/app/components/ModalSelectors.spec.tsx +141 -0
  46. package/source/app/components/ModalSelectors.tsx +135 -0
  47. package/source/app/helpers.spec.ts +97 -0
  48. package/source/app/helpers.ts +63 -0
  49. package/source/app/index.ts +4 -0
  50. package/source/app/types.ts +39 -0
  51. package/source/app/utils/appUtils.ts +294 -0
  52. package/source/app/utils/conversationState.ts +310 -0
  53. package/source/app.spec.tsx +244 -0
  54. package/source/cli.spec.ts +73 -0
  55. package/source/cli.tsx +51 -0
  56. package/source/client-factory.spec.ts +48 -0
  57. package/source/client-factory.ts +178 -0
  58. package/source/command-parser.spec.ts +127 -0
  59. package/source/command-parser.ts +36 -0
  60. package/source/commands/checkpoint.spec.tsx +277 -0
  61. package/source/commands/checkpoint.tsx +366 -0
  62. package/source/commands/clear.tsx +22 -0
  63. package/source/commands/custom-commands.tsx +121 -0
  64. package/source/commands/exit.ts +21 -0
  65. package/source/commands/export.spec.tsx +131 -0
  66. package/source/commands/export.tsx +79 -0
  67. package/source/commands/help.tsx +120 -0
  68. package/source/commands/index.ts +17 -0
  69. package/source/commands/init.tsx +339 -0
  70. package/source/commands/lsp-command.spec.tsx +281 -0
  71. package/source/commands/lsp.tsx +120 -0
  72. package/source/commands/mcp-command.spec.tsx +313 -0
  73. package/source/commands/mcp.tsx +162 -0
  74. package/source/commands/model-database.spec.tsx +758 -0
  75. package/source/commands/model-database.tsx +418 -0
  76. package/source/commands/model.ts +12 -0
  77. package/source/commands/provider.ts +12 -0
  78. package/source/commands/setup-config.tsx +16 -0
  79. package/source/commands/simple-commands.spec.tsx +175 -0
  80. package/source/commands/status.ts +12 -0
  81. package/source/commands/theme.ts +12 -0
  82. package/source/commands/update.spec.tsx +261 -0
  83. package/source/commands/update.tsx +201 -0
  84. package/source/commands/usage.spec.tsx +495 -0
  85. package/source/commands/usage.tsx +100 -0
  86. package/source/commands.spec.ts +436 -0
  87. package/source/commands.ts +83 -0
  88. package/source/components/assistant-message.spec.tsx +796 -0
  89. package/source/components/assistant-message.tsx +34 -0
  90. package/source/components/bash-execution-indicator.tsx +21 -0
  91. package/source/components/cancelling-indicator.tsx +16 -0
  92. package/source/components/chat-queue.spec.tsx +83 -0
  93. package/source/components/chat-queue.tsx +36 -0
  94. package/source/components/checkpoint-display.spec.tsx +219 -0
  95. package/source/components/checkpoint-display.tsx +126 -0
  96. package/source/components/checkpoint-selector.spec.tsx +173 -0
  97. package/source/components/checkpoint-selector.tsx +173 -0
  98. package/source/components/development-mode-indicator.spec.tsx +268 -0
  99. package/source/components/development-mode-indicator.tsx +38 -0
  100. package/source/components/message-box.spec.tsx +427 -0
  101. package/source/components/message-box.tsx +87 -0
  102. package/source/components/model-selector.tsx +132 -0
  103. package/source/components/provider-selector.tsx +75 -0
  104. package/source/components/random-spinner.tsx +19 -0
  105. package/source/components/security-disclaimer.tsx +73 -0
  106. package/source/components/status-connection-display.spec.tsx +133 -0
  107. package/source/components/status.tsx +267 -0
  108. package/source/components/theme-selector.tsx +126 -0
  109. package/source/components/tool-confirmation.tsx +190 -0
  110. package/source/components/tool-execution-indicator.tsx +33 -0
  111. package/source/components/tool-message.tsx +85 -0
  112. package/source/components/ui/titled-box.spec.tsx +207 -0
  113. package/source/components/ui/titled-box.tsx +57 -0
  114. package/source/components/usage/progress-bar.spec.tsx +398 -0
  115. package/source/components/usage/progress-bar.tsx +30 -0
  116. package/source/components/usage/usage-display.spec.tsx +780 -0
  117. package/source/components/usage/usage-display.tsx +291 -0
  118. package/source/components/user-input.spec.tsx +327 -0
  119. package/source/components/user-input.tsx +533 -0
  120. package/source/components/user-message.spec.tsx +230 -0
  121. package/source/components/user-message.tsx +84 -0
  122. package/source/components/welcome-message.tsx +76 -0
  123. package/source/config/env-substitution.ts +65 -0
  124. package/source/config/index.spec.ts +171 -0
  125. package/source/config/index.ts +154 -0
  126. package/source/config/paths.spec.ts +241 -0
  127. package/source/config/paths.ts +55 -0
  128. package/source/config/preferences.ts +51 -0
  129. package/source/config/themes.ts +315 -0
  130. package/source/constants.ts +130 -0
  131. package/source/context/mode-context.spec.ts +79 -0
  132. package/source/context/mode-context.ts +24 -0
  133. package/source/custom-commands/executor.spec.ts +142 -0
  134. package/source/custom-commands/executor.ts +64 -0
  135. package/source/custom-commands/loader.spec.ts +314 -0
  136. package/source/custom-commands/loader.ts +153 -0
  137. package/source/custom-commands/parser.ts +196 -0
  138. package/source/hooks/chat-handler/conversation/conversation-loop.spec.ts +39 -0
  139. package/source/hooks/chat-handler/conversation/conversation-loop.tsx +511 -0
  140. package/source/hooks/chat-handler/conversation/tool-executor.spec.ts +50 -0
  141. package/source/hooks/chat-handler/conversation/tool-executor.tsx +109 -0
  142. package/source/hooks/chat-handler/index.ts +12 -0
  143. package/source/hooks/chat-handler/state/streaming-state.spec.ts +26 -0
  144. package/source/hooks/chat-handler/state/streaming-state.ts +19 -0
  145. package/source/hooks/chat-handler/types.ts +38 -0
  146. package/source/hooks/chat-handler/useChatHandler.spec.tsx +321 -0
  147. package/source/hooks/chat-handler/useChatHandler.tsx +194 -0
  148. package/source/hooks/chat-handler/utils/context-checker.spec.ts +60 -0
  149. package/source/hooks/chat-handler/utils/context-checker.tsx +73 -0
  150. package/source/hooks/chat-handler/utils/message-helpers.spec.ts +42 -0
  151. package/source/hooks/chat-handler/utils/message-helpers.tsx +36 -0
  152. package/source/hooks/chat-handler/utils/tool-filters.spec.ts +109 -0
  153. package/source/hooks/chat-handler/utils/tool-filters.ts +64 -0
  154. package/source/hooks/useAppHandlers.tsx +291 -0
  155. package/source/hooks/useAppInitialization.tsx +422 -0
  156. package/source/hooks/useAppState.tsx +311 -0
  157. package/source/hooks/useDirectoryTrust.tsx +98 -0
  158. package/source/hooks/useInputState.ts +414 -0
  159. package/source/hooks/useModeHandlers.tsx +302 -0
  160. package/source/hooks/useNonInteractiveMode.ts +140 -0
  161. package/source/hooks/useTerminalWidth.tsx +81 -0
  162. package/source/hooks/useTheme.ts +18 -0
  163. package/source/hooks/useToolHandler.tsx +349 -0
  164. package/source/hooks/useUIState.ts +61 -0
  165. package/source/init/agents-template-generator.ts +421 -0
  166. package/source/init/existing-rules-extractor.ts +319 -0
  167. package/source/init/file-scanner.spec.ts +227 -0
  168. package/source/init/file-scanner.ts +238 -0
  169. package/source/init/framework-detector.ts +382 -0
  170. package/source/init/language-detector.ts +269 -0
  171. package/source/init/project-analyzer.spec.ts +231 -0
  172. package/source/init/project-analyzer.ts +458 -0
  173. package/source/lsp/index.ts +31 -0
  174. package/source/lsp/lsp-client.spec.ts +508 -0
  175. package/source/lsp/lsp-client.ts +487 -0
  176. package/source/lsp/lsp-manager.spec.ts +477 -0
  177. package/source/lsp/lsp-manager.ts +419 -0
  178. package/source/lsp/protocol.spec.ts +502 -0
  179. package/source/lsp/protocol.ts +360 -0
  180. package/source/lsp/server-discovery.spec.ts +654 -0
  181. package/source/lsp/server-discovery.ts +515 -0
  182. package/source/markdown-parser/html-entities.spec.ts +88 -0
  183. package/source/markdown-parser/html-entities.ts +45 -0
  184. package/source/markdown-parser/index.spec.ts +281 -0
  185. package/source/markdown-parser/index.ts +126 -0
  186. package/source/markdown-parser/table-parser.spec.ts +133 -0
  187. package/source/markdown-parser/table-parser.ts +114 -0
  188. package/source/markdown-parser/utils.spec.ts +70 -0
  189. package/source/markdown-parser/utils.ts +13 -0
  190. package/source/mcp/mcp-client.spec.ts +81 -0
  191. package/source/mcp/mcp-client.ts +625 -0
  192. package/source/mcp/transport-factory.spec.ts +406 -0
  193. package/source/mcp/transport-factory.ts +312 -0
  194. package/source/message-handler.ts +67 -0
  195. package/source/model-database/database-engine.spec.ts +494 -0
  196. package/source/model-database/database-engine.ts +50 -0
  197. package/source/model-database/model-database.spec.ts +363 -0
  198. package/source/model-database/model-database.ts +91 -0
  199. package/source/model-database/model-engine.spec.ts +447 -0
  200. package/source/model-database/model-engine.ts +65 -0
  201. package/source/model-database/model-fetcher.spec.ts +583 -0
  202. package/source/model-database/model-fetcher.ts +330 -0
  203. package/source/models/index.ts +1 -0
  204. package/source/models/models-cache.spec.ts +214 -0
  205. package/source/models/models-cache.ts +78 -0
  206. package/source/models/models-dev-client.spec.ts +379 -0
  207. package/source/models/models-dev-client.ts +329 -0
  208. package/source/models/models-types.ts +68 -0
  209. package/source/prompt-history.ts +155 -0
  210. package/source/security/command-injection.spec.ts +240 -0
  211. package/source/services/checkpoint-manager.spec.ts +523 -0
  212. package/source/services/checkpoint-manager.ts +466 -0
  213. package/source/services/file-snapshot.spec.ts +569 -0
  214. package/source/services/file-snapshot.ts +220 -0
  215. package/source/test-utils/render-with-theme.tsx +48 -0
  216. package/source/tokenization/index.ts +1 -0
  217. package/source/tokenization/tokenizer-factory.spec.ts +170 -0
  218. package/source/tokenization/tokenizer-factory.ts +125 -0
  219. package/source/tokenization/tokenizers/anthropic-tokenizer.spec.ts +200 -0
  220. package/source/tokenization/tokenizers/anthropic-tokenizer.ts +43 -0
  221. package/source/tokenization/tokenizers/fallback-tokenizer.spec.ts +236 -0
  222. package/source/tokenization/tokenizers/fallback-tokenizer.ts +26 -0
  223. package/source/tokenization/tokenizers/llama-tokenizer.spec.ts +224 -0
  224. package/source/tokenization/tokenizers/llama-tokenizer.ts +41 -0
  225. package/source/tokenization/tokenizers/openai-tokenizer.spec.ts +184 -0
  226. package/source/tokenization/tokenizers/openai-tokenizer.ts +57 -0
  227. package/source/tool-calling/index.ts +5 -0
  228. package/source/tool-calling/json-parser.spec.ts +639 -0
  229. package/source/tool-calling/json-parser.ts +247 -0
  230. package/source/tool-calling/tool-parser.spec.ts +395 -0
  231. package/source/tool-calling/tool-parser.ts +120 -0
  232. package/source/tool-calling/xml-parser.spec.ts +662 -0
  233. package/source/tool-calling/xml-parser.ts +289 -0
  234. package/source/tools/execute-bash.spec.tsx +353 -0
  235. package/source/tools/execute-bash.tsx +219 -0
  236. package/source/tools/execute-function.spec.ts +130 -0
  237. package/source/tools/fetch-url.spec.tsx +342 -0
  238. package/source/tools/fetch-url.tsx +172 -0
  239. package/source/tools/find-files.spec.tsx +924 -0
  240. package/source/tools/find-files.tsx +293 -0
  241. package/source/tools/index.ts +102 -0
  242. package/source/tools/lsp-get-diagnostics.tsx +192 -0
  243. package/source/tools/needs-approval.spec.ts +282 -0
  244. package/source/tools/read-file.spec.tsx +801 -0
  245. package/source/tools/read-file.tsx +387 -0
  246. package/source/tools/search-file-contents.spec.tsx +1273 -0
  247. package/source/tools/search-file-contents.tsx +293 -0
  248. package/source/tools/string-replace.spec.tsx +730 -0
  249. package/source/tools/string-replace.tsx +548 -0
  250. package/source/tools/tool-manager.ts +210 -0
  251. package/source/tools/tool-registry.spec.ts +415 -0
  252. package/source/tools/tool-registry.ts +228 -0
  253. package/source/tools/web-search.tsx +223 -0
  254. package/source/tools/write-file.spec.tsx +559 -0
  255. package/source/tools/write-file.tsx +228 -0
  256. package/source/types/app.ts +37 -0
  257. package/source/types/checkpoint.ts +48 -0
  258. package/source/types/commands.ts +46 -0
  259. package/source/types/components.ts +27 -0
  260. package/source/types/config.ts +103 -0
  261. package/source/types/core-connection-status.spec.ts +67 -0
  262. package/source/types/core.ts +181 -0
  263. package/source/types/hooks.ts +50 -0
  264. package/source/types/index.ts +12 -0
  265. package/source/types/markdown-parser.ts +11 -0
  266. package/source/types/mcp.ts +52 -0
  267. package/source/types/system.ts +16 -0
  268. package/source/types/tokenization.ts +41 -0
  269. package/source/types/ui.ts +40 -0
  270. package/source/types/usage.ts +58 -0
  271. package/source/types/utils.ts +16 -0
  272. package/source/usage/calculator.spec.ts +385 -0
  273. package/source/usage/calculator.ts +104 -0
  274. package/source/usage/storage.spec.ts +703 -0
  275. package/source/usage/storage.ts +238 -0
  276. package/source/usage/tracker.spec.ts +456 -0
  277. package/source/usage/tracker.ts +102 -0
  278. package/source/utils/atomic-deletion.spec.ts +194 -0
  279. package/source/utils/atomic-deletion.ts +127 -0
  280. package/source/utils/bounded-map.spec.ts +300 -0
  281. package/source/utils/bounded-map.ts +193 -0
  282. package/source/utils/checkpoint-utils.spec.ts +222 -0
  283. package/source/utils/checkpoint-utils.ts +92 -0
  284. package/source/utils/error-formatter.spec.ts +169 -0
  285. package/source/utils/error-formatter.ts +194 -0
  286. package/source/utils/file-autocomplete.spec.ts +173 -0
  287. package/source/utils/file-autocomplete.ts +196 -0
  288. package/source/utils/file-cache.spec.ts +309 -0
  289. package/source/utils/file-cache.ts +195 -0
  290. package/source/utils/file-content-loader.spec.ts +180 -0
  291. package/source/utils/file-content-loader.ts +179 -0
  292. package/source/utils/file-mention-handler.spec.ts +261 -0
  293. package/source/utils/file-mention-handler.ts +84 -0
  294. package/source/utils/file-mention-parser.spec.ts +182 -0
  295. package/source/utils/file-mention-parser.ts +170 -0
  296. package/source/utils/fuzzy-matching.spec.ts +149 -0
  297. package/source/utils/fuzzy-matching.ts +146 -0
  298. package/source/utils/indentation-normalizer.spec.ts +216 -0
  299. package/source/utils/indentation-normalizer.ts +76 -0
  300. package/source/utils/installation-detector.spec.ts +178 -0
  301. package/source/utils/installation-detector.ts +153 -0
  302. package/source/utils/logging/config.spec.ts +311 -0
  303. package/source/utils/logging/config.ts +210 -0
  304. package/source/utils/logging/console-facade.spec.ts +184 -0
  305. package/source/utils/logging/console-facade.ts +384 -0
  306. package/source/utils/logging/correlation.spec.ts +679 -0
  307. package/source/utils/logging/correlation.ts +474 -0
  308. package/source/utils/logging/formatters.spec.ts +464 -0
  309. package/source/utils/logging/formatters.ts +207 -0
  310. package/source/utils/logging/health-monitor/alerts/alert-manager.spec.ts +93 -0
  311. package/source/utils/logging/health-monitor/alerts/alert-manager.ts +79 -0
  312. package/source/utils/logging/health-monitor/checks/configuration-check.spec.ts +56 -0
  313. package/source/utils/logging/health-monitor/checks/configuration-check.ts +43 -0
  314. package/source/utils/logging/health-monitor/checks/logging-check.spec.ts +56 -0
  315. package/source/utils/logging/health-monitor/checks/logging-check.ts +58 -0
  316. package/source/utils/logging/health-monitor/checks/memory-check.spec.ts +100 -0
  317. package/source/utils/logging/health-monitor/checks/memory-check.ts +78 -0
  318. package/source/utils/logging/health-monitor/checks/performance-check.spec.ts +56 -0
  319. package/source/utils/logging/health-monitor/checks/performance-check.ts +56 -0
  320. package/source/utils/logging/health-monitor/checks/request-check.spec.ts +56 -0
  321. package/source/utils/logging/health-monitor/checks/request-check.ts +76 -0
  322. package/source/utils/logging/health-monitor/core/health-check-runner.spec.ts +70 -0
  323. package/source/utils/logging/health-monitor/core/health-check-runner.ts +138 -0
  324. package/source/utils/logging/health-monitor/core/health-monitor.spec.ts +58 -0
  325. package/source/utils/logging/health-monitor/core/health-monitor.ts +344 -0
  326. package/source/utils/logging/health-monitor/core/scoring.spec.ts +65 -0
  327. package/source/utils/logging/health-monitor/core/scoring.ts +91 -0
  328. package/source/utils/logging/health-monitor/index.ts +15 -0
  329. package/source/utils/logging/health-monitor/instances.ts +48 -0
  330. package/source/utils/logging/health-monitor/middleware/http-middleware.spec.ts +141 -0
  331. package/source/utils/logging/health-monitor/middleware/http-middleware.ts +75 -0
  332. package/source/utils/logging/health-monitor/types.ts +126 -0
  333. package/source/utils/logging/index.spec.ts +284 -0
  334. package/source/utils/logging/index.ts +236 -0
  335. package/source/utils/logging/integration.spec.ts +441 -0
  336. package/source/utils/logging/log-method-factory.spec.ts +573 -0
  337. package/source/utils/logging/log-method-factory.ts +233 -0
  338. package/source/utils/logging/log-query/aggregation/aggregator.spec.ts +277 -0
  339. package/source/utils/logging/log-query/aggregation/aggregator.ts +159 -0
  340. package/source/utils/logging/log-query/aggregation/facet-generator.spec.ts +159 -0
  341. package/source/utils/logging/log-query/aggregation/facet-generator.ts +47 -0
  342. package/source/utils/logging/log-query/index.ts +23 -0
  343. package/source/utils/logging/log-query/query/filter-predicates.spec.ts +247 -0
  344. package/source/utils/logging/log-query/query/filter-predicates.ts +154 -0
  345. package/source/utils/logging/log-query/query/query-builder.spec.ts +182 -0
  346. package/source/utils/logging/log-query/query/query-builder.ts +151 -0
  347. package/source/utils/logging/log-query/query/query-engine.spec.ts +214 -0
  348. package/source/utils/logging/log-query/query/query-engine.ts +45 -0
  349. package/source/utils/logging/log-query/storage/circular-buffer.spec.ts +143 -0
  350. package/source/utils/logging/log-query/storage/circular-buffer.ts +75 -0
  351. package/source/utils/logging/log-query/storage/index-manager.spec.ts +150 -0
  352. package/source/utils/logging/log-query/storage/index-manager.ts +71 -0
  353. package/source/utils/logging/log-query/storage/log-storage.spec.ts +257 -0
  354. package/source/utils/logging/log-query/storage/log-storage.ts +80 -0
  355. package/source/utils/logging/log-query/types.ts +163 -0
  356. package/source/utils/logging/log-query/utils/helpers.spec.ts +263 -0
  357. package/source/utils/logging/log-query/utils/helpers.ts +72 -0
  358. package/source/utils/logging/log-query/utils/sorting.spec.ts +182 -0
  359. package/source/utils/logging/log-query/utils/sorting.ts +61 -0
  360. package/source/utils/logging/logger-provider.spec.ts +262 -0
  361. package/source/utils/logging/logger-provider.ts +362 -0
  362. package/source/utils/logging/performance.spec.ts +209 -0
  363. package/source/utils/logging/performance.ts +757 -0
  364. package/source/utils/logging/pino-logger.spec.ts +425 -0
  365. package/source/utils/logging/pino-logger.ts +514 -0
  366. package/source/utils/logging/redaction.spec.ts +490 -0
  367. package/source/utils/logging/redaction.ts +267 -0
  368. package/source/utils/logging/request-tracker.spec.ts +1198 -0
  369. package/source/utils/logging/request-tracker.ts +803 -0
  370. package/source/utils/logging/transports.spec.ts +505 -0
  371. package/source/utils/logging/transports.ts +305 -0
  372. package/source/utils/logging/types.ts +216 -0
  373. package/source/utils/message-builder.spec.ts +179 -0
  374. package/source/utils/message-builder.ts +101 -0
  375. package/source/utils/message-queue.tsx +486 -0
  376. package/source/utils/paste-detection.spec.ts +69 -0
  377. package/source/utils/paste-detection.ts +124 -0
  378. package/source/utils/paste-roundtrip.spec.ts +442 -0
  379. package/source/utils/paste-utils.spec.ts +128 -0
  380. package/source/utils/paste-utils.ts +52 -0
  381. package/source/utils/programming-language-helper.spec.ts +74 -0
  382. package/source/utils/programming-language-helper.ts +32 -0
  383. package/source/utils/prompt-assembly.spec.ts +221 -0
  384. package/source/utils/prompt-processor.ts +173 -0
  385. package/source/utils/tool-args-parser.spec.ts +136 -0
  386. package/source/utils/tool-args-parser.ts +54 -0
  387. package/source/utils/tool-cancellation.spec.ts +230 -0
  388. package/source/utils/tool-cancellation.ts +28 -0
  389. package/source/utils/tool-result-display.spec.tsx +469 -0
  390. package/source/utils/tool-result-display.tsx +90 -0
  391. package/source/utils/update-checker.spec.ts +383 -0
  392. package/source/utils/update-checker.ts +183 -0
  393. package/source/wizard/config-wizard.spec.tsx +103 -0
  394. package/source/wizard/config-wizard.tsx +382 -0
  395. package/source/wizard/steps/location-step.spec.tsx +186 -0
  396. package/source/wizard/steps/location-step.tsx +147 -0
  397. package/source/wizard/steps/mcp-step.spec.tsx +607 -0
  398. package/source/wizard/steps/mcp-step.tsx +632 -0
  399. package/source/wizard/steps/provider-step.spec.tsx +342 -0
  400. package/source/wizard/steps/provider-step.tsx +957 -0
  401. package/source/wizard/steps/summary-step.spec.tsx +749 -0
  402. package/source/wizard/steps/summary-step.tsx +228 -0
  403. package/source/wizard/templates/mcp-templates.spec.ts +613 -0
  404. package/source/wizard/templates/mcp-templates.ts +570 -0
  405. package/source/wizard/templates/provider-templates.spec.ts +152 -0
  406. package/source/wizard/templates/provider-templates.ts +485 -0
  407. package/source/wizard/utils/fetch-cloud-models.spec.ts +428 -0
  408. package/source/wizard/utils/fetch-cloud-models.ts +223 -0
  409. package/source/wizard/utils/fetch-local-models.spec.ts +297 -0
  410. package/source/wizard/utils/fetch-local-models.ts +192 -0
  411. package/source/wizard/validation-array.spec.ts +264 -0
  412. package/source/wizard/validation.spec.ts +373 -0
  413. package/source/wizard/validation.ts +232 -0
  414. package/source/app/prompts/main-prompt.md +0 -122
@@ -0,0 +1,414 @@
1
+ import {
2
+ PASTE_CHUNK_BASE_WINDOW_MS,
3
+ PASTE_CHUNK_MAX_WINDOW_MS,
4
+ PASTE_LARGE_CONTENT_THRESHOLD_CHARS,
5
+ PASTE_RAPID_DETECTION_MS,
6
+ } from '@/constants';
7
+ import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
8
+ import {InputState, PlaceholderType} from '../types/hooks';
9
+ import {handleAtomicDeletion} from '../utils/atomic-deletion';
10
+ import {PasteDetector} from '../utils/paste-detection';
11
+ import {handlePaste} from '../utils/paste-utils';
12
+
13
+ // Scales the paste window size based on content length.
14
+ // Prevents truncation on slow terminals while keeping small pastes snappy
15
+ function getDynamicPasteWindow(contentLength: number): number {
16
+ // Add ~1ms buffer per 10 chars, capped at max window
17
+ const dynamicExtension = Math.floor(contentLength / 10);
18
+ return Math.min(
19
+ PASTE_CHUNK_BASE_WINDOW_MS + dynamicExtension,
20
+ PASTE_CHUNK_MAX_WINDOW_MS,
21
+ );
22
+ }
23
+
24
+ // Helper functions
25
+ function createEmptyInputState(): InputState {
26
+ return {
27
+ displayValue: '',
28
+ placeholderContent: {},
29
+ };
30
+ }
31
+
32
+ export function useInputState() {
33
+ // Core state following the spec
34
+ const [currentState, setCurrentState] = useState<InputState>(
35
+ createEmptyInputState(),
36
+ );
37
+
38
+ const [undoStack, setUndoStack] = useState<InputState[]>([]);
39
+ const [redoStack, setRedoStack] = useState<InputState[]>([]);
40
+
41
+ // Legacy compatibility - these are derived from currentState
42
+ const [historyIndex, setHistoryIndex] = useState(-1);
43
+ const [_hasLargeContent, setHasLargeContent] = useState(false);
44
+ const [originalInput, setOriginalInput] = useState('');
45
+
46
+ // Paste detection
47
+ const pasteDetectorRef = useRef(new PasteDetector());
48
+ const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
49
+
50
+ // Track recent paste for chunked paste handling (VS Code terminal issue)
51
+ const lastPasteTimeRef = useRef<number>(0);
52
+ const lastPasteIdRef = useRef<string | null>(null);
53
+
54
+ // Cached line count for performance
55
+ const [cachedLineCount, setCachedLineCount] = useState(1);
56
+
57
+ // Helper to push current state to undo stack
58
+ const pushToUndoStack = useCallback(
59
+ (newState: InputState) => {
60
+ setUndoStack(prev => [...prev, currentState]);
61
+ setRedoStack([]); // Clear redo stack on new action
62
+ setCurrentState(newState);
63
+ },
64
+ [currentState],
65
+ );
66
+
67
+ // Update input with paste detection and atomic deletion
68
+ const updateInput = useCallback(
69
+ (newInput: string) => {
70
+ // First, check for atomic deletion (placeholder removal)
71
+ const atomicDeletionResult = handleAtomicDeletion(currentState, newInput);
72
+ if (atomicDeletionResult) {
73
+ // Atomic deletion occurred - apply it
74
+ pushToUndoStack(atomicDeletionResult);
75
+ return;
76
+ }
77
+
78
+ const now = Date.now();
79
+ const timeSinceLastPaste = now - lastPasteTimeRef.current;
80
+
81
+ // Check if this might be a continuation of a recent paste (chunked paste in VS Code)
82
+ const existingPlaceholder = lastPasteIdRef.current
83
+ ? currentState.placeholderContent[lastPasteIdRef.current]
84
+ : null;
85
+ const dynamicWindow = existingPlaceholder
86
+ ? getDynamicPasteWindow(existingPlaceholder.content.length)
87
+ : PASTE_CHUNK_BASE_WINDOW_MS;
88
+
89
+ if (
90
+ lastPasteIdRef.current &&
91
+ timeSinceLastPaste < dynamicWindow &&
92
+ existingPlaceholder
93
+ ) {
94
+ // This looks like a chunked paste continuation
95
+ // Extract the new text that was added (should be at the end)
96
+ const placeholder =
97
+ currentState.placeholderContent[lastPasteIdRef.current];
98
+ const expectedLength = currentState.displayValue.length;
99
+ const addedChunk = newInput.slice(expectedLength);
100
+
101
+ if (
102
+ addedChunk.length > 0 &&
103
+ placeholder.type === PlaceholderType.PASTE
104
+ ) {
105
+ // Merge the new chunk into the existing paste placeholder
106
+ const updatedContent = placeholder.content + addedChunk;
107
+ const oldPlaceholder = placeholder.displayText;
108
+ const newPlaceholder = `[Paste #${lastPasteIdRef.current}: ${updatedContent.length} chars]`;
109
+
110
+ const updatedPlaceholderContent = {
111
+ ...currentState.placeholderContent,
112
+ [lastPasteIdRef.current]: {
113
+ ...placeholder,
114
+ content: updatedContent,
115
+ originalSize: updatedContent.length,
116
+ displayText: newPlaceholder,
117
+ },
118
+ };
119
+
120
+ // Replace old placeholder with updated one in display value
121
+ const newDisplayValue = currentState.displayValue.replace(
122
+ oldPlaceholder,
123
+ newPlaceholder,
124
+ );
125
+
126
+ pushToUndoStack({
127
+ displayValue: newDisplayValue,
128
+ placeholderContent: updatedPlaceholderContent,
129
+ });
130
+
131
+ // Update paste detector to the new display value
132
+ pasteDetectorRef.current.updateState(newDisplayValue);
133
+ lastPasteTimeRef.current = now; // Extend the window
134
+ return;
135
+ }
136
+ }
137
+
138
+ // Then detect if this might be a paste
139
+ const detection = pasteDetectorRef.current.detectPaste(newInput);
140
+
141
+ if (detection.isPaste && detection.addedText.length > 0) {
142
+ // If we have an active paste within a short window (even if state hasn't fully updated),
143
+ // treat this as a continuation to prevent duplicate placeholders
144
+ const isVeryRecentPaste = timeSinceLastPaste < PASTE_RAPID_DETECTION_MS;
145
+
146
+ const activePasteId = lastPasteIdRef.current;
147
+ const activePlaceholder = activePasteId
148
+ ? currentState.placeholderContent[activePasteId]
149
+ : null;
150
+ const activeWindow = activePlaceholder
151
+ ? getDynamicPasteWindow(activePlaceholder.content.length)
152
+ : PASTE_CHUNK_BASE_WINDOW_MS;
153
+
154
+ if (
155
+ activePasteId &&
156
+ (isVeryRecentPaste ||
157
+ (timeSinceLastPaste < activeWindow && activePlaceholder))
158
+ ) {
159
+ // If we don't have the placeholder in state yet, just update detector and skip
160
+ // This happens when multiple detections fire before React updates state
161
+ const placeholder = currentState.placeholderContent[activePasteId];
162
+ if (!placeholder) {
163
+ // Skip duplicate early detection
164
+ pasteDetectorRef.current.updateState(newInput);
165
+ return;
166
+ }
167
+
168
+ // Treat as chunked continuation
169
+ if (placeholder.type === PlaceholderType.PASTE) {
170
+ const updatedContent = placeholder.content + detection.addedText;
171
+ const oldPlaceholder = placeholder.displayText;
172
+ const newPlaceholder = `[Paste #${activePasteId}: ${updatedContent.length} chars]`;
173
+
174
+ const updatedPlaceholderContent = {
175
+ ...currentState.placeholderContent,
176
+ [activePasteId]: {
177
+ ...placeholder,
178
+ content: updatedContent,
179
+ originalSize: updatedContent.length,
180
+ displayText: newPlaceholder,
181
+ },
182
+ };
183
+
184
+ const newDisplayValue = currentState.displayValue.replace(
185
+ oldPlaceholder,
186
+ newPlaceholder,
187
+ );
188
+
189
+ pushToUndoStack({
190
+ displayValue: newDisplayValue,
191
+ placeholderContent: updatedPlaceholderContent,
192
+ });
193
+
194
+ pasteDetectorRef.current.updateState(newDisplayValue);
195
+ lastPasteTimeRef.current = now;
196
+ return;
197
+ }
198
+ }
199
+
200
+ // Try to handle as paste (new paste)
201
+ const pasteResult = handlePaste(
202
+ detection.addedText,
203
+ currentState.displayValue,
204
+ currentState.placeholderContent,
205
+ detection.method as 'rate' | 'size' | 'multiline',
206
+ );
207
+
208
+ if (pasteResult) {
209
+ // Large paste detected - create placeholder
210
+ pushToUndoStack(pasteResult);
211
+ // Update paste detector state to match the new display value (with placeholder)
212
+ // This prevents detection confusion on subsequent pastes
213
+ pasteDetectorRef.current.updateState(pasteResult.displayValue);
214
+
215
+ // Track this paste for potential chunked continuation
216
+ const pasteId = Object.keys(pasteResult.placeholderContent).find(
217
+ id =>
218
+ !currentState.placeholderContent[id] &&
219
+ pasteResult.placeholderContent[id].type === PlaceholderType.PASTE,
220
+ );
221
+ if (pasteId) {
222
+ lastPasteIdRef.current = pasteId;
223
+ lastPasteTimeRef.current = now;
224
+ }
225
+ } else {
226
+ // Small paste - treat as normal input
227
+ pushToUndoStack({
228
+ displayValue: newInput,
229
+ placeholderContent: currentState.placeholderContent,
230
+ });
231
+ }
232
+ } else {
233
+ // Normal typing
234
+ pushToUndoStack({
235
+ displayValue: newInput,
236
+ placeholderContent: currentState.placeholderContent,
237
+ });
238
+ }
239
+
240
+ // Update derived state
241
+ const immediateLineCount = Math.max(
242
+ 1,
243
+ newInput.split(/\r\n|\r|\n/).length,
244
+ );
245
+ setCachedLineCount(immediateLineCount);
246
+
247
+ // Clear any previous debounce timer
248
+ if (debounceTimerRef.current) {
249
+ clearTimeout(debounceTimerRef.current);
250
+ }
251
+
252
+ debounceTimerRef.current = setTimeout(() => {
253
+ setHasLargeContent(
254
+ newInput.length > PASTE_LARGE_CONTENT_THRESHOLD_CHARS,
255
+ );
256
+ }, 50);
257
+ },
258
+ [currentState, pushToUndoStack],
259
+ );
260
+
261
+ // Undo function (Ctrl+_)
262
+ const undo = useCallback(() => {
263
+ if (undoStack.length > 0) {
264
+ const previousState = undoStack[undoStack.length - 1];
265
+ const newUndoStack = undoStack.slice(0, -1);
266
+
267
+ setRedoStack(prev => [...prev, currentState]);
268
+ setUndoStack(newUndoStack);
269
+ setCurrentState(previousState);
270
+
271
+ // Update paste detector state
272
+ pasteDetectorRef.current.updateState(previousState.displayValue);
273
+ }
274
+ }, [undoStack, currentState]);
275
+
276
+ // Redo function (Ctrl+Y)
277
+ const redo = useCallback(() => {
278
+ if (redoStack.length > 0) {
279
+ const nextState = redoStack[redoStack.length - 1];
280
+ const newRedoStack = redoStack.slice(0, -1);
281
+
282
+ setUndoStack(prev => [...prev, currentState]);
283
+ setRedoStack(newRedoStack);
284
+ setCurrentState(nextState);
285
+
286
+ // Update paste detector state
287
+ pasteDetectorRef.current.updateState(nextState.displayValue);
288
+ }
289
+ }, [redoStack, currentState]);
290
+
291
+ // Delete placeholder atomically
292
+ const deletePlaceholder = useCallback(
293
+ (placeholderId: string) => {
294
+ // Sanitize placeholderId to ensure it only contains safe characters
295
+ const sanitizedPlaceholderId = placeholderId.replace(
296
+ /[^a-zA-Z0-9_-]/g,
297
+ '',
298
+ );
299
+ const placeholderPattern = `[Paste #${sanitizedPlaceholderId}: \\d+ chars]`;
300
+ /* nosemgrep */
301
+ const regex = new RegExp(
302
+ placeholderPattern.replace(/[[\]]/g, '\\$&'),
303
+ 'g',
304
+ );
305
+
306
+ const newDisplayValue = currentState.displayValue.replace(regex, '');
307
+ const newPlaceholderContent = {...currentState.placeholderContent};
308
+ delete newPlaceholderContent[placeholderId];
309
+
310
+ pushToUndoStack({
311
+ displayValue: newDisplayValue,
312
+ placeholderContent: newPlaceholderContent,
313
+ });
314
+ },
315
+ [currentState, pushToUndoStack],
316
+ );
317
+
318
+ // Reset all state
319
+ const resetInput = useCallback(() => {
320
+ if (debounceTimerRef.current) {
321
+ clearTimeout(debounceTimerRef.current);
322
+ debounceTimerRef.current = null;
323
+ }
324
+
325
+ setCurrentState(createEmptyInputState());
326
+ setUndoStack([]);
327
+ setRedoStack([]);
328
+ setHasLargeContent(false);
329
+ setOriginalInput('');
330
+ setHistoryIndex(-1);
331
+ setCachedLineCount(1);
332
+ pasteDetectorRef.current.reset();
333
+ lastPasteTimeRef.current = 0;
334
+ lastPasteIdRef.current = null;
335
+ }, []);
336
+
337
+ // Cleanup on unmount
338
+ useEffect(() => {
339
+ return () => {
340
+ if (debounceTimerRef.current) {
341
+ clearTimeout(debounceTimerRef.current);
342
+ debounceTimerRef.current = null;
343
+ }
344
+ };
345
+ }, []);
346
+
347
+ // Set full InputState (for history navigation)
348
+ const setInputState = useCallback((newState: InputState) => {
349
+ setCurrentState(newState);
350
+ pasteDetectorRef.current.updateState(newState.displayValue);
351
+ }, []);
352
+
353
+ // Legacy setters for compatibility
354
+ const setInput = useCallback((newInput: string) => {
355
+ setCurrentState(prev => ({
356
+ ...prev,
357
+ displayValue: newInput,
358
+ }));
359
+ pasteDetectorRef.current.updateState(newInput);
360
+ }, []);
361
+
362
+ // Compute legacy pastedContent for backward compatibility
363
+ const legacyPastedContent = useMemo(() => {
364
+ const pastedContent: Record<string, string> = {};
365
+ Object.entries(currentState.placeholderContent).forEach(([id, content]) => {
366
+ if (content.type === PlaceholderType.PASTE) {
367
+ pastedContent[id] = content.content;
368
+ }
369
+ });
370
+ return pastedContent;
371
+ }, [currentState.placeholderContent]);
372
+
373
+ return useMemo(
374
+ () => ({
375
+ // New spec-compliant interface
376
+ currentState,
377
+ undoStack,
378
+ redoStack,
379
+ undo,
380
+ redo,
381
+ deletePlaceholder,
382
+ setInputState,
383
+
384
+ // Legacy interface for compatibility
385
+ input: currentState.displayValue,
386
+ originalInput,
387
+ historyIndex,
388
+ setInput,
389
+ setOriginalInput,
390
+ setHistoryIndex,
391
+ updateInput,
392
+ resetInput,
393
+ cachedLineCount,
394
+ // Computed legacy property for backward compatibility
395
+ pastedContent: legacyPastedContent,
396
+ }),
397
+ [
398
+ currentState,
399
+ undoStack,
400
+ redoStack,
401
+ undo,
402
+ redo,
403
+ deletePlaceholder,
404
+ setInputState,
405
+ originalInput,
406
+ historyIndex,
407
+ setInput,
408
+ updateInput,
409
+ resetInput,
410
+ cachedLineCount,
411
+ legacyPastedContent,
412
+ ],
413
+ );
414
+ }
@@ -0,0 +1,302 @@
1
+ import { createLLMClient } from '@/client-factory';
2
+ import { ErrorMessage, SuccessMessage } from '@/components/message-box';
3
+ import { reloadAppConfig } from '@/config/index';
4
+ import {
5
+ loadPreferences,
6
+ savePreferences,
7
+ updateLastUsed,
8
+ } from '@/config/preferences';
9
+ import { getToolManager } from '@/message-handler';
10
+ import { LLMClient, Message } from '@/types/core';
11
+ import type { ThemePreset } from '@/types/ui';
12
+ import React from 'react';
13
+
14
+ interface UseModeHandlersProps {
15
+ client: LLMClient | null;
16
+ currentModel: string;
17
+ currentProvider: string;
18
+ currentTheme: ThemePreset;
19
+ setClient: (client: LLMClient | null) => void;
20
+ setCurrentModel: (model: string) => void;
21
+ setCurrentProvider: (provider: string) => void;
22
+ setCurrentTheme: (theme: ThemePreset) => void;
23
+ setMessages: (messages: Message[]) => void;
24
+ setIsModelSelectionMode: (mode: boolean) => void;
25
+ setIsProviderSelectionMode: (mode: boolean) => void;
26
+ setIsThemeSelectionMode: (mode: boolean) => void;
27
+ setIsModelDatabaseMode: (mode: boolean) => void;
28
+ setIsConfigWizardMode: (mode: boolean) => void;
29
+ addToChatQueue: (component: React.ReactNode) => void;
30
+ componentKeyCounter: number;
31
+ reinitializeMCPServers: (
32
+ toolManager: import('@/tools/tool-manager').ToolManager,
33
+ ) => Promise<void>;
34
+ }
35
+
36
+ export function useModeHandlers({
37
+ client,
38
+ currentModel,
39
+ currentProvider,
40
+ currentTheme: _currentTheme,
41
+ setClient,
42
+ setCurrentModel,
43
+ setCurrentProvider,
44
+ setCurrentTheme,
45
+ setMessages,
46
+ setIsModelSelectionMode,
47
+ setIsProviderSelectionMode,
48
+ setIsThemeSelectionMode,
49
+ setIsModelDatabaseMode,
50
+ setIsConfigWizardMode,
51
+ addToChatQueue,
52
+ componentKeyCounter,
53
+ reinitializeMCPServers,
54
+ }: UseModeHandlersProps) {
55
+ // Helper function to enter model selection mode
56
+ const enterModelSelectionMode = () => {
57
+ setIsModelSelectionMode(true);
58
+ };
59
+
60
+ // Helper function to enter provider selection mode
61
+ const enterProviderSelectionMode = () => {
62
+ setIsProviderSelectionMode(true);
63
+ };
64
+
65
+ // Handle model selection
66
+ const handleModelSelect = async (selectedModel: string) => {
67
+ if (client && selectedModel !== currentModel) {
68
+ client.setModel(selectedModel);
69
+ setCurrentModel(selectedModel);
70
+
71
+ // Clear message history when switching models
72
+ setMessages([]);
73
+ await client.clearContext();
74
+
75
+ // Update preferences
76
+ updateLastUsed(currentProvider, selectedModel);
77
+
78
+ // Add success message to chat queue
79
+ addToChatQueue(
80
+ <SuccessMessage
81
+ key={`model-changed-${componentKeyCounter}`}
82
+ message={`Model changed to: ${selectedModel}. Chat history cleared.`}
83
+ hideBox={true}
84
+ />,
85
+ );
86
+ }
87
+ setIsModelSelectionMode(false);
88
+ };
89
+
90
+ // Handle model selection cancel
91
+ const handleModelSelectionCancel = () => {
92
+ setIsModelSelectionMode(false);
93
+ };
94
+
95
+ // Handle provider selection
96
+ const handleProviderSelect = async (selectedProvider: string) => {
97
+ if (selectedProvider !== currentProvider) {
98
+ try {
99
+ // Create new client for the selected provider
100
+ const { client: newClient, actualProvider } =
101
+ await createLLMClient(selectedProvider);
102
+
103
+ // Check if we got the provider we requested
104
+ if (actualProvider !== selectedProvider) {
105
+ // Provider was forced to a different one (likely due to missing config)
106
+ addToChatQueue(
107
+ <ErrorMessage
108
+ key={`provider-forced-${componentKeyCounter}`}
109
+ message={`${selectedProvider} is not available. Please ensure it's properly configured in coder.config.json.`}
110
+ hideBox={true}
111
+ />,
112
+ );
113
+ return; // Don't change anything
114
+ }
115
+
116
+ setClient(newClient);
117
+ setCurrentProvider(actualProvider);
118
+
119
+ // Set the model from the new client
120
+ const newModel = newClient.getCurrentModel();
121
+ setCurrentModel(newModel);
122
+
123
+ // Clear message history when switching providers
124
+ setMessages([]);
125
+ await newClient.clearContext();
126
+
127
+ // Update preferences - use the actualProvider (which is what was successfully created)
128
+ updateLastUsed(actualProvider, newModel);
129
+
130
+ // Add success message to chat queue
131
+ addToChatQueue(
132
+ <SuccessMessage
133
+ key={`provider-changed-${componentKeyCounter}`}
134
+ message={`Provider changed to: ${actualProvider}, model: ${newModel}. Chat history cleared.`}
135
+ hideBox={true}
136
+ />,
137
+ );
138
+ } catch (error) {
139
+ // Add error message if provider change fails
140
+ addToChatQueue(
141
+ <ErrorMessage
142
+ key={`provider-error-${componentKeyCounter}`}
143
+ message={`Failed to change provider to ${selectedProvider}: ${String(
144
+ error,
145
+ )}`}
146
+ hideBox={true}
147
+ />,
148
+ );
149
+ }
150
+ }
151
+ setIsProviderSelectionMode(false);
152
+ };
153
+
154
+ // Handle provider selection cancel
155
+ const handleProviderSelectionCancel = () => {
156
+ setIsProviderSelectionMode(false);
157
+ };
158
+
159
+ // Helper function to enter theme selection mode
160
+ const enterThemeSelectionMode = () => {
161
+ setIsThemeSelectionMode(true);
162
+ };
163
+
164
+ // Handle theme selection
165
+ const handleThemeSelect = (selectedTheme: ThemePreset) => {
166
+ const preferences = loadPreferences();
167
+ preferences.selectedTheme = selectedTheme;
168
+ savePreferences(preferences);
169
+
170
+ // Update the theme state immediately for real-time switching
171
+ setCurrentTheme(selectedTheme);
172
+
173
+ // Add success message to chat queue
174
+ addToChatQueue(
175
+ <SuccessMessage
176
+ key={`theme-changed-${componentKeyCounter}`}
177
+ message={`Theme changed to: ${selectedTheme}.`}
178
+ hideBox={true}
179
+ />,
180
+ );
181
+
182
+ setIsThemeSelectionMode(false);
183
+ };
184
+
185
+ // Handle theme selection cancel
186
+ const handleThemeSelectionCancel = () => {
187
+ setIsThemeSelectionMode(false);
188
+ };
189
+
190
+ // Helper function to enter model database mode
191
+ const enterModelDatabaseMode = () => {
192
+ setIsModelDatabaseMode(true);
193
+ };
194
+
195
+ // Handle model database cancel
196
+ const handleModelDatabaseCancel = () => {
197
+ setIsModelDatabaseMode(false);
198
+ };
199
+
200
+ // Helper function to enter config wizard mode
201
+ const enterConfigWizardMode = () => {
202
+ setIsConfigWizardMode(true);
203
+ };
204
+
205
+ // Handle config wizard cancel/complete
206
+ const handleConfigWizardComplete = async (configPath?: string) => {
207
+ setIsConfigWizardMode(false);
208
+ if (configPath) {
209
+ addToChatQueue(
210
+ <SuccessMessage
211
+ key={`config-wizard-complete-${componentKeyCounter}`}
212
+ message={`Configuration saved to: ${configPath}.`}
213
+ hideBox={true}
214
+ />,
215
+ );
216
+
217
+ // Reload the app configuration to pick up the newly saved config
218
+ reloadAppConfig();
219
+
220
+ // Reinitialize client with new configuration
221
+ try {
222
+ const preferences = loadPreferences();
223
+ const { client: newClient, actualProvider } = await createLLMClient(
224
+ preferences.lastProvider,
225
+ );
226
+ setClient(newClient);
227
+ setCurrentProvider(actualProvider);
228
+
229
+ const newModel = newClient.getCurrentModel();
230
+ setCurrentModel(newModel);
231
+
232
+ // Clear message history when switching providers
233
+ setMessages([]);
234
+ await newClient.clearContext();
235
+
236
+ // Reinitialize MCP servers with the new configuration
237
+ const toolManager = getToolManager();
238
+ if (toolManager) {
239
+ try {
240
+ await reinitializeMCPServers(toolManager);
241
+ addToChatQueue(
242
+ <SuccessMessage
243
+ key={`mcp-reinit-${componentKeyCounter}`}
244
+ message="MCP servers reinitialized with new configuration."
245
+ hideBox={true}
246
+ />,
247
+ );
248
+ } catch (mcpError) {
249
+ addToChatQueue(
250
+ <ErrorMessage
251
+ key={`mcp-reinit-error-${componentKeyCounter}`}
252
+ message={`Failed to reinitialize MCP servers: ${String(
253
+ mcpError,
254
+ )}`}
255
+ hideBox={true}
256
+ />,
257
+ );
258
+ }
259
+ }
260
+
261
+ addToChatQueue(
262
+ <SuccessMessage
263
+ key={`config-init-${componentKeyCounter}`}
264
+ message={`Ready! Using provider: ${actualProvider}, model: ${newModel}`}
265
+ hideBox={true}
266
+ />,
267
+ );
268
+ } catch (error) {
269
+ addToChatQueue(
270
+ <ErrorMessage
271
+ key={`config-init-error-${componentKeyCounter}`}
272
+ message={`Failed to initialize with new configuration: ${String(
273
+ error,
274
+ )}`}
275
+ hideBox={true}
276
+ />,
277
+ );
278
+ }
279
+ }
280
+ };
281
+
282
+ const handleConfigWizardCancel = () => {
283
+ setIsConfigWizardMode(false);
284
+ };
285
+
286
+ return {
287
+ enterModelSelectionMode,
288
+ enterProviderSelectionMode,
289
+ enterThemeSelectionMode,
290
+ enterModelDatabaseMode,
291
+ enterConfigWizardMode,
292
+ handleModelSelect,
293
+ handleModelSelectionCancel,
294
+ handleProviderSelect,
295
+ handleProviderSelectionCancel,
296
+ handleThemeSelect,
297
+ handleThemeSelectionCancel,
298
+ handleModelDatabaseCancel,
299
+ handleConfigWizardComplete,
300
+ handleConfigWizardCancel,
301
+ };
302
+ }