@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,366 @@
1
+ import {CheckpointListDisplay} from '@/components/checkpoint-display';
2
+ import {
3
+ ErrorMessage,
4
+ InfoMessage,
5
+ SuccessMessage,
6
+ WarningMessage,
7
+ } from '@/components/message-box';
8
+ import {CheckpointManager} from '@/services/checkpoint-manager';
9
+ import {Command, Message} from '@/types/index';
10
+ import {addToMessageQueue} from '@/utils/message-queue';
11
+ import React from 'react';
12
+
13
+ // Default checkpoint manager instance (lazy-initialized)
14
+ let defaultCheckpointManager: CheckpointManager | null = null;
15
+
16
+ /**
17
+ * Get or create the default checkpoint manager.
18
+ * For testing, use createCheckpointCommand() with a custom manager.
19
+ */
20
+ function getDefaultCheckpointManager(): CheckpointManager {
21
+ if (!defaultCheckpointManager) {
22
+ defaultCheckpointManager = new CheckpointManager();
23
+ }
24
+ return defaultCheckpointManager;
25
+ }
26
+
27
+ /**
28
+ * Show checkpoint command help
29
+ */
30
+ function CheckpointHelp() {
31
+ return (
32
+ <InfoMessage
33
+ message={`Checkpoint Commands:
34
+
35
+ /checkpoint create [name] - Create a new checkpoint
36
+ • Creates a snapshot of current conversation and modified files
37
+ • Auto-generates timestamped name if not provided
38
+ • Example: /checkpoint create feature-auth-v1
39
+
40
+ /checkpoint list - List all available checkpoints
41
+ • Shows checkpoint name, creation time, message count, and files changed
42
+
43
+ /checkpoint load - Interactive checkpoint selection and restore
44
+ • Choose from available checkpoints
45
+ • Shows confirmation before restoring
46
+ • Optionally creates backup of current session
47
+
48
+ /checkpoint delete <name> - Delete a specific checkpoint
49
+ • Permanently removes checkpoint and all its data
50
+ • Shows confirmation before deletion
51
+
52
+ /checkpoint help - Show this help message
53
+
54
+ Note: Checkpoints are stored in your coder config directory.`}
55
+ hideBox={false}
56
+ />
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Create checkpoint subcommand
62
+ */
63
+ async function createCheckpoint(
64
+ args: string[],
65
+ messages: Message[],
66
+ metadata: {provider: string; model: string},
67
+ ): Promise<React.ReactElement> {
68
+ try {
69
+ const manager = getDefaultCheckpointManager();
70
+ const name = args.length > 0 ? args.join(' ') : undefined;
71
+
72
+ if (messages.length === 0) {
73
+ return React.createElement(WarningMessage, {
74
+ key: `warning-${Date.now()}`,
75
+ message: 'No messages to checkpoint. Start a conversation first.',
76
+ hideBox: true,
77
+ });
78
+ }
79
+
80
+ const checkpointMetadata = await manager.saveCheckpoint(
81
+ name,
82
+ messages,
83
+ metadata.provider,
84
+ metadata.model,
85
+ );
86
+
87
+ return React.createElement(SuccessMessage, {
88
+ key: `success-${Date.now()}`,
89
+ message: `Checkpoint '${checkpointMetadata.name}' created successfully
90
+ └─ ${checkpointMetadata.messageCount} messages saved
91
+ └─ ${
92
+ checkpointMetadata.filesChanged.length
93
+ } files captured: ${checkpointMetadata.filesChanged.slice(0, 3).join(', ')}${
94
+ checkpointMetadata.filesChanged.length > 3 ? '...' : ''
95
+ }
96
+ └─ Provider: ${checkpointMetadata.provider.name} (${
97
+ checkpointMetadata.provider.model
98
+ })`,
99
+ hideBox: true,
100
+ });
101
+ } catch (error) {
102
+ return React.createElement(ErrorMessage, {
103
+ key: `error-${Date.now()}`,
104
+ message: `Failed to create checkpoint: ${
105
+ error instanceof Error ? error.message : 'Unknown error'
106
+ }`,
107
+ hideBox: true,
108
+ });
109
+ }
110
+ }
111
+
112
+ /**
113
+ * List checkpoints subcommand
114
+ */
115
+ async function listCheckpoints(): Promise<React.ReactElement> {
116
+ try {
117
+ const manager = getDefaultCheckpointManager();
118
+ const checkpoints = await manager.listCheckpoints();
119
+
120
+ return React.createElement(CheckpointListDisplay, {
121
+ key: `list-${Date.now()}`,
122
+ checkpoints,
123
+ });
124
+ } catch (error) {
125
+ return React.createElement(ErrorMessage, {
126
+ key: `error-${Date.now()}`,
127
+ message: `Failed to list checkpoints: ${
128
+ error instanceof Error ? error.message : 'Unknown error'
129
+ }`,
130
+ hideBox: true,
131
+ });
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Load checkpoint subcommand
137
+ */
138
+ async function loadCheckpoint(
139
+ args: string[],
140
+ messages: Message[],
141
+ metadata: {provider: string; model: string},
142
+ ): Promise<React.ReactElement> {
143
+ try {
144
+ const manager = getDefaultCheckpointManager();
145
+ const checkpointName = args.join(' ');
146
+
147
+ if (checkpointName) {
148
+ if (!manager.checkpointExists(checkpointName)) {
149
+ return React.createElement(ErrorMessage, {
150
+ key: `error-${Date.now()}`,
151
+ message: `Checkpoint '${checkpointName}' does not exist. Use /checkpoint list to see available checkpoints.`,
152
+ hideBox: true,
153
+ });
154
+ }
155
+
156
+ const checkpointData = await manager.loadCheckpoint(checkpointName, {
157
+ validateIntegrity: true,
158
+ });
159
+
160
+ await manager.restoreFiles(checkpointData);
161
+
162
+ return React.createElement(
163
+ React.Fragment,
164
+ {key: `load-success-${Date.now()}`},
165
+ React.createElement(SuccessMessage, {
166
+ key: 'success',
167
+ message: `✓ Checkpoint '${checkpointName}' files restored successfully`,
168
+ hideBox: true,
169
+ }),
170
+ React.createElement(InfoMessage, {
171
+ key: 'details',
172
+ message: `Restored checkpoint:
173
+ • ${checkpointData.fileSnapshots.size} file(s) restored to workspace
174
+ • Provider: ${checkpointData.metadata.provider.name} (${
175
+ checkpointData.metadata.provider.model
176
+ })
177
+ • Created: ${new Date(checkpointData.metadata.timestamp).toLocaleString()}`,
178
+ hideBox: true,
179
+ }),
180
+ );
181
+ }
182
+
183
+ const checkpoints = await manager.listCheckpoints();
184
+
185
+ if (checkpoints.length === 0) {
186
+ return React.createElement(InfoMessage, {
187
+ key: `info-${Date.now()}`,
188
+ message:
189
+ 'No checkpoints available. Create one with /checkpoint create [name]',
190
+ hideBox: true,
191
+ });
192
+ }
193
+
194
+ const CheckpointSelector = (
195
+ await import('@/components/checkpoint-selector')
196
+ ).default;
197
+
198
+ const handleError = (error: Error) => {
199
+ addToMessageQueue(
200
+ React.createElement(ErrorMessage, {
201
+ key: `restore-error-${Date.now()}`,
202
+ message: `Failed to restore checkpoint: ${error.message}`,
203
+ hideBox: true,
204
+ }),
205
+ );
206
+ };
207
+
208
+ return React.createElement(CheckpointSelector, {
209
+ key: `selector-${Date.now()}`,
210
+ checkpoints,
211
+ currentMessageCount: messages.length,
212
+ onSelect: (selectedName: string, createBackup: boolean) => {
213
+ void (async () => {
214
+ try {
215
+ if (createBackup) {
216
+ try {
217
+ await manager.saveCheckpoint(
218
+ `backup-${new Date().toISOString().replace(/[:.]/g, '-')}`,
219
+ messages,
220
+ metadata.provider,
221
+ metadata.model,
222
+ );
223
+ } catch (error) {
224
+ // Show backup error but continue with restore
225
+ addToMessageQueue(
226
+ React.createElement(WarningMessage, {
227
+ key: `backup-warning-${Date.now()}`,
228
+ message: `Warning: Failed to create backup: ${
229
+ error instanceof Error ? error.message : 'Unknown error'
230
+ }`,
231
+ hideBox: true,
232
+ }),
233
+ );
234
+ }
235
+ }
236
+
237
+ const checkpointData = await manager.loadCheckpoint(selectedName, {
238
+ validateIntegrity: true,
239
+ });
240
+
241
+ await manager.restoreFiles(checkpointData);
242
+
243
+ addToMessageQueue(
244
+ React.createElement(SuccessMessage, {
245
+ key: `restore-success-${Date.now()}`,
246
+ message: `✓ Checkpoint '${selectedName}' restored successfully`,
247
+ hideBox: true,
248
+ }),
249
+ );
250
+ } catch (error) {
251
+ handleError(
252
+ error instanceof Error ? error : new Error('Unknown error'),
253
+ );
254
+ }
255
+ })();
256
+ },
257
+ onCancel: () => {
258
+ // Nothing to do, component will unmount
259
+ },
260
+ onError: handleError,
261
+ });
262
+ } catch (error) {
263
+ return React.createElement(ErrorMessage, {
264
+ key: `error-${Date.now()}`,
265
+ message: `Failed to load checkpoint: ${
266
+ error instanceof Error ? error.message : 'Unknown error'
267
+ }`,
268
+ hideBox: true,
269
+ });
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Delete checkpoint subcommand
275
+ */
276
+ async function deleteCheckpoint(args: string[]): Promise<React.ReactElement> {
277
+ try {
278
+ if (args.length === 0) {
279
+ return React.createElement(ErrorMessage, {
280
+ key: `error-${Date.now()}`,
281
+ message:
282
+ 'Please specify a checkpoint name to delete. Usage: /checkpoint delete <name>',
283
+ hideBox: true,
284
+ });
285
+ }
286
+
287
+ const manager = getDefaultCheckpointManager();
288
+ const checkpointName = args.join(' ');
289
+
290
+ if (!manager.checkpointExists(checkpointName)) {
291
+ return React.createElement(ErrorMessage, {
292
+ key: `error-${Date.now()}`,
293
+ message: `Checkpoint '${checkpointName}' does not exist. Use /checkpoint list to see available checkpoints.`,
294
+ hideBox: true,
295
+ });
296
+ }
297
+
298
+ // Actually delete the checkpoint
299
+ await manager.deleteCheckpoint(checkpointName);
300
+
301
+ // Show success with what was deleted
302
+ return React.createElement(SuccessMessage, {
303
+ key: `delete-success-${Date.now()}`,
304
+ message: `✓ Checkpoint '${checkpointName}' deleted successfully`,
305
+ hideBox: true,
306
+ });
307
+ } catch (error) {
308
+ return React.createElement(ErrorMessage, {
309
+ key: `error-${Date.now()}`,
310
+ message: `Failed to delete checkpoint: ${
311
+ error instanceof Error ? error.message : 'Unknown error'
312
+ }`,
313
+ hideBox: true,
314
+ });
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Main checkpoint command handler
320
+ */
321
+ export const checkpointCommand: Command = {
322
+ name: 'checkpoint',
323
+ description:
324
+ 'Manage conversation checkpoints - save and restore session snapshots',
325
+ handler: async (args: string[], messages: Message[], metadata) => {
326
+ if (args.length === 0) {
327
+ return checkpointCommand.handler(['help'], messages, metadata);
328
+ }
329
+
330
+ const subcommand = args[0].toLowerCase();
331
+ const subArgs = args.slice(1);
332
+
333
+ switch (subcommand) {
334
+ case 'create':
335
+ case 'save':
336
+ return await createCheckpoint(subArgs, messages, metadata);
337
+
338
+ case 'list':
339
+ case 'ls':
340
+ return await listCheckpoints();
341
+
342
+ case 'load':
343
+ case 'restore':
344
+ return await loadCheckpoint(subArgs, messages, metadata);
345
+
346
+ case 'delete':
347
+ case 'remove':
348
+ case 'rm':
349
+ return await deleteCheckpoint(subArgs);
350
+
351
+ case 'help':
352
+ case '--help':
353
+ case '-h':
354
+ return React.createElement(CheckpointHelp, {
355
+ key: `help-${Date.now()}`,
356
+ });
357
+
358
+ default:
359
+ return React.createElement(ErrorMessage, {
360
+ key: `error-${Date.now()}`,
361
+ message: `Unknown checkpoint subcommand: ${subcommand}. Use /checkpoint help for available commands.`,
362
+ hideBox: true,
363
+ });
364
+ }
365
+ },
366
+ };
@@ -0,0 +1,22 @@
1
+ import {SuccessMessage} from '@/components/message-box';
2
+ import {Command} from '@/types/index';
3
+ import React from 'react';
4
+
5
+ function Clear() {
6
+ return (
7
+ <SuccessMessage hideBox={true} message="Chat Cleared."></SuccessMessage>
8
+ );
9
+ }
10
+
11
+ export const clearCommand: Command = {
12
+ name: 'clear',
13
+ description: 'Clear the chat history and model context',
14
+ handler: (_args: string[]) => {
15
+ // Return info message saying chat was cleared
16
+ return Promise.resolve(
17
+ React.createElement(Clear, {
18
+ key: `clear-${Date.now()}`,
19
+ }),
20
+ );
21
+ },
22
+ };
@@ -0,0 +1,121 @@
1
+ import {TitledBox} from '@/components/ui/titled-box';
2
+ import {CustomCommandLoader} from '@/custom-commands/loader';
3
+ import {useTheme} from '@/hooks/useTheme';
4
+ import type {Command, CustomCommand} from '@/types/index';
5
+ import {Box, Text} from 'ink';
6
+ import React from 'react';
7
+
8
+ interface CustomCommandsProps {
9
+ commands: CustomCommand[];
10
+ }
11
+
12
+ function formatCommand(cmd: CustomCommand): string {
13
+ const parts: string[] = [`/${cmd.fullName}`];
14
+
15
+ if (cmd.metadata.parameters && cmd.metadata.parameters.length > 0) {
16
+ parts.push(cmd.metadata.parameters.map((p: string) => `<${p}>`).join(' '));
17
+ }
18
+
19
+ if (cmd.metadata.description) {
20
+ parts.push(`- ${cmd.metadata.description}`);
21
+ }
22
+
23
+ if (cmd.metadata.aliases && cmd.metadata.aliases.length > 0) {
24
+ const aliasNames = cmd.metadata.aliases.map((a: string) =>
25
+ cmd.namespace ? `${cmd.namespace}:${a}` : a,
26
+ );
27
+ parts.push(`(aliases: ${aliasNames.join(', ')})`);
28
+ }
29
+
30
+ return parts.join(' ');
31
+ }
32
+
33
+ function CustomCommands({commands}: CustomCommandsProps) {
34
+ const {colors} = useTheme();
35
+ // Sort commands alphabetically by full name
36
+ const sortedCommands = [...commands].sort((a, b) =>
37
+ a.fullName.localeCompare(b.fullName),
38
+ );
39
+
40
+ return (
41
+ <TitledBox
42
+ title="Custom Commands"
43
+ width={75}
44
+ borderColor={colors.primary}
45
+ paddingX={2}
46
+ paddingY={1}
47
+ flexDirection="column"
48
+ marginBottom={1}
49
+ >
50
+ {commands.length === 0 ? (
51
+ <>
52
+ <Box marginBottom={1}>
53
+ <Text color={colors.white} bold>
54
+ No custom commands found
55
+ </Text>
56
+ </Box>
57
+
58
+ <Text color={colors.white}>To create custom commands:</Text>
59
+
60
+ <Text color={colors.secondary}>
61
+ 1. Create a <Text color={colors.primary}>.coder/commands</Text>{' '}
62
+ directory in your project
63
+ </Text>
64
+
65
+ <Text color={colors.secondary}>
66
+ 2. Add <Text color={colors.primary}>.md</Text> files with command
67
+ prompts
68
+ </Text>
69
+
70
+ <Text color={colors.secondary}>
71
+ 3. Optionally add frontmatter for metadata:
72
+ </Text>
73
+
74
+ <Box marginTop={1} marginBottom={1}>
75
+ <Text color={colors.secondary}>
76
+ {`---\n`}
77
+ {`description: Generate unit tests\n`}
78
+ {`aliases: [test, unittest]\n`}
79
+ {`parameters: [filename]\n`}
80
+ {`---\n`}
81
+ {`Generate comprehensive unit tests for {{filename}}...`}
82
+ </Text>
83
+ </Box>
84
+ </>
85
+ ) : (
86
+ <>
87
+ <Box marginBottom={1}>
88
+ <Text color={colors.white}>
89
+ Found {commands.length} custom command
90
+ {commands.length !== 1 ? 's' : ''}:
91
+ </Text>
92
+ </Box>
93
+
94
+ {sortedCommands.map((cmd, index) => (
95
+ <Text key={index} color={colors.white}>
96
+ • {formatCommand(cmd)}
97
+ </Text>
98
+ ))}
99
+ </>
100
+ )}
101
+ </TitledBox>
102
+ );
103
+ }
104
+
105
+ export const commandsCommand: Command = {
106
+ name: 'custom-commands',
107
+ description: 'List all custom commands from .coder/commands',
108
+ handler: (_args: string[]) => {
109
+ // Create a custom command loader to get the commands
110
+ const loader = new CustomCommandLoader();
111
+ loader.loadCommands();
112
+ const commands = loader.getAllCommands() || [];
113
+
114
+ return Promise.resolve(
115
+ React.createElement(CustomCommands, {
116
+ key: `custom-commands-${Date.now()}`,
117
+ commands: commands,
118
+ }),
119
+ );
120
+ },
121
+ };
@@ -0,0 +1,21 @@
1
+ import {InfoMessage} from '@/components/message-box';
2
+ import {Command} from '@/types/index';
3
+ import React from 'react';
4
+
5
+ export const exitCommand: Command = {
6
+ name: 'exit',
7
+ description: 'Exit the application',
8
+ handler: (_args: string[], _messages, _metadata) => {
9
+ // Return InfoMessage component first, then exit after a short delay
10
+ setTimeout(() => {
11
+ process.exit(0);
12
+ }, 500); // 500ms delay to allow message to render
13
+
14
+ return Promise.resolve(
15
+ React.createElement(InfoMessage, {
16
+ message: 'Goodbye! 👋',
17
+ hideTitle: true,
18
+ }),
19
+ );
20
+ },
21
+ };
@@ -0,0 +1,131 @@
1
+ import test from 'ava';
2
+ import type {Message} from '@/types/index';
3
+ import {exportCommand} from './export';
4
+ import {promises as fs} from 'fs';
5
+ import React from 'react';
6
+
7
+ // Mock fs module
8
+ const originalWriteFile = fs.writeFile;
9
+ let mockWriteFileCalls: Array<{path: string; content: string}> = [];
10
+
11
+ test.beforeEach(() => {
12
+ mockWriteFileCalls = [];
13
+ fs.writeFile = async (filepath: string, content: string) => {
14
+ mockWriteFileCalls.push({path: filepath, content});
15
+ return Promise.resolve(void 0);
16
+ };
17
+ });
18
+
19
+ test.afterEach(() => {
20
+ fs.writeFile = originalWriteFile;
21
+ });
22
+
23
+ const testMessages: Message[] = [
24
+ {role: 'user', content: 'Hello'},
25
+ {role: 'assistant', content: 'Hi there', tool_calls: undefined},
26
+ {role: 'tool', name: 'test', content: 'Tool result'},
27
+ {role: 'system', content: 'System message'},
28
+ ];
29
+
30
+ const testMetadata = {
31
+ provider: 'test-provider',
32
+ model: 'test-model',
33
+ tokens: 100,
34
+ getMessageTokens: (m: Message) => 0,
35
+ };
36
+
37
+ test('exportCommand has correct name and description', t => {
38
+ t.is(exportCommand.name, 'export');
39
+ t.is(exportCommand.description, 'Export the chat history to a markdown file');
40
+ });
41
+
42
+ test('exportCommand handler returns React element', async t => {
43
+ const result = await exportCommand.handler([], testMessages, testMetadata);
44
+ t.truthy(React.isValidElement(result));
45
+ });
46
+
47
+ test('exportCommand uses provided filename', async t => {
48
+ await exportCommand.handler(['custom-export.md'], testMessages, testMetadata);
49
+
50
+ t.is(mockWriteFileCalls.length, 1);
51
+ t.true(mockWriteFileCalls[0].path.includes('custom-export.md'));
52
+ });
53
+
54
+ test('exportCommand generates default filename when none provided', async t => {
55
+ await exportCommand.handler([], testMessages, testMetadata);
56
+
57
+ t.is(mockWriteFileCalls.length, 1);
58
+ t.true(mockWriteFileCalls[0].path.includes('coder-chat-'));
59
+ t.true(mockWriteFileCalls[0].path.endsWith('.md'));
60
+ });
61
+
62
+ test('exportCommand includes frontmatter in export', async t => {
63
+ await exportCommand.handler(['test.md'], testMessages, testMetadata);
64
+
65
+ const content = mockWriteFileCalls[0].content;
66
+ t.true(content.includes('session_date:'));
67
+ t.true(content.includes('provider: test-provider'));
68
+ t.true(content.includes('model: test-model'));
69
+ t.true(content.includes('total_tokens: 100'));
70
+ });
71
+
72
+ test('exportCommand formats user messages correctly', async t => {
73
+ const messages: Message[] = [{role: 'user', content: 'Hello world'}];
74
+ await exportCommand.handler(['test.md'], messages, testMetadata);
75
+
76
+ const content = mockWriteFileCalls[0].content;
77
+ t.true(content.includes('## User'));
78
+ t.true(content.includes('Hello world'));
79
+ });
80
+
81
+ test('exportCommand formats assistant messages correctly', async t => {
82
+ const messages: Message[] = [{role: 'assistant', content: 'Assistant response'}];
83
+ await exportCommand.handler(['test.md'], messages, testMetadata);
84
+
85
+ const content = mockWriteFileCalls[0].content;
86
+ t.true(content.includes('## Assistant'));
87
+ t.true(content.includes('Assistant response'));
88
+ });
89
+
90
+ test('exportCommand formats assistant messages with tool calls', async t => {
91
+ const messages: Message[] = [
92
+ {
93
+ role: 'assistant',
94
+ content: 'Using tools',
95
+ tool_calls: [
96
+ {function: {name: 'tool1', arguments: '{}'}, id: '1'},
97
+ {function: {name: 'tool2', arguments: '{}'}, id: '2'},
98
+ ],
99
+ },
100
+ ];
101
+ await exportCommand.handler(['test.md'], messages, testMetadata);
102
+
103
+ const content = mockWriteFileCalls[0].content;
104
+ t.true(content.includes('[tool_use: tool1, tool2]'));
105
+ });
106
+
107
+ test('exportCommand formats tool messages correctly', async t => {
108
+ const messages: Message[] = [{role: 'tool', name: 'my_tool', content: 'Tool output'}];
109
+ await exportCommand.handler(['test.md'], messages, testMetadata);
110
+
111
+ const content = mockWriteFileCalls[0].content;
112
+ t.true(content.includes('## Tool Output: my_tool'));
113
+ t.true(content.includes('```'));
114
+ t.true(content.includes('Tool output'));
115
+ });
116
+
117
+ test('exportCommand excludes system messages', async t => {
118
+ const messages: Message[] = [{role: 'system', content: 'System instruction'}];
119
+ await exportCommand.handler(['test.md'], messages, testMetadata);
120
+
121
+ const content = mockWriteFileCalls[0].content;
122
+ t.false(content.includes('System instruction'));
123
+ });
124
+
125
+ test('exportCommand handles unknown message role', async t => {
126
+ const messages: Message[] = [{role: 'unknown' as const, content: 'Unknown'}];
127
+ await exportCommand.handler(['test.md'], messages, testMetadata);
128
+
129
+ // Should not throw, just handle gracefully
130
+ t.is(mockWriteFileCalls.length, 1);
131
+ });