@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.
- package/.editorconfig +16 -0
- package/.env.example +63 -0
- package/.gitattributes +1 -0
- package/.semgrepignore +19 -0
- package/coder-dummy-file.ts +52 -0
- package/coder.config.example.json +59 -0
- package/coder.config.json +13 -0
- package/color_picker.html +36 -0
- package/package.json +2 -14
- package/scripts/extract-changelog.js +73 -0
- package/scripts/fetch-models.js +143 -0
- package/scripts/test.sh +40 -0
- package/scripts/update-homebrew-formula.sh +125 -0
- package/scripts/update-nix-version.sh +157 -0
- package/source/ai-sdk-client/AISDKClient.spec.ts +117 -0
- package/source/ai-sdk-client/AISDKClient.ts +155 -0
- package/source/ai-sdk-client/chat/chat-handler.spec.ts +121 -0
- package/source/ai-sdk-client/chat/chat-handler.ts +276 -0
- package/source/ai-sdk-client/chat/streaming-handler.spec.ts +173 -0
- package/source/ai-sdk-client/chat/streaming-handler.ts +110 -0
- package/source/ai-sdk-client/chat/tool-processor.spec.ts +92 -0
- package/source/ai-sdk-client/chat/tool-processor.ts +70 -0
- package/source/ai-sdk-client/converters/message-converter.spec.ts +220 -0
- package/source/ai-sdk-client/converters/message-converter.ts +113 -0
- package/source/ai-sdk-client/converters/tool-converter.spec.ts +90 -0
- package/source/ai-sdk-client/converters/tool-converter.ts +46 -0
- package/source/ai-sdk-client/error-handling/error-extractor.spec.ts +55 -0
- package/source/ai-sdk-client/error-handling/error-extractor.ts +15 -0
- package/source/ai-sdk-client/error-handling/error-parser.spec.ts +169 -0
- package/source/ai-sdk-client/error-handling/error-parser.ts +161 -0
- package/source/ai-sdk-client/index.ts +7 -0
- package/source/ai-sdk-client/providers/provider-factory.spec.ts +71 -0
- package/source/ai-sdk-client/providers/provider-factory.ts +41 -0
- package/source/ai-sdk-client/types.ts +9 -0
- package/source/ai-sdk-client-empty-message.spec.ts +141 -0
- package/source/ai-sdk-client-error-handling.spec.ts +186 -0
- package/source/ai-sdk-client-maxretries.spec.ts +114 -0
- package/source/ai-sdk-client-preparestep.spec.ts +279 -0
- package/source/app/App.spec.tsx +32 -0
- package/source/app/App.tsx +480 -0
- package/source/app/components/AppContainer.spec.tsx +96 -0
- package/source/app/components/AppContainer.tsx +56 -0
- package/source/app/components/ChatInterface.spec.tsx +163 -0
- package/source/app/components/ChatInterface.tsx +144 -0
- package/source/app/components/ModalSelectors.spec.tsx +141 -0
- package/source/app/components/ModalSelectors.tsx +135 -0
- package/source/app/helpers.spec.ts +97 -0
- package/source/app/helpers.ts +63 -0
- package/source/app/index.ts +4 -0
- package/source/app/types.ts +39 -0
- package/source/app/utils/appUtils.ts +294 -0
- package/source/app/utils/conversationState.ts +310 -0
- package/source/app.spec.tsx +244 -0
- package/source/cli.spec.ts +73 -0
- package/source/cli.tsx +51 -0
- package/source/client-factory.spec.ts +48 -0
- package/source/client-factory.ts +178 -0
- package/source/command-parser.spec.ts +127 -0
- package/source/command-parser.ts +36 -0
- package/source/commands/checkpoint.spec.tsx +277 -0
- package/source/commands/checkpoint.tsx +366 -0
- package/source/commands/clear.tsx +22 -0
- package/source/commands/custom-commands.tsx +121 -0
- package/source/commands/exit.ts +21 -0
- package/source/commands/export.spec.tsx +131 -0
- package/source/commands/export.tsx +79 -0
- package/source/commands/help.tsx +120 -0
- package/source/commands/index.ts +17 -0
- package/source/commands/init.tsx +339 -0
- package/source/commands/lsp-command.spec.tsx +281 -0
- package/source/commands/lsp.tsx +120 -0
- package/source/commands/mcp-command.spec.tsx +313 -0
- package/source/commands/mcp.tsx +162 -0
- package/source/commands/model-database.spec.tsx +758 -0
- package/source/commands/model-database.tsx +418 -0
- package/source/commands/model.ts +12 -0
- package/source/commands/provider.ts +12 -0
- package/source/commands/setup-config.tsx +16 -0
- package/source/commands/simple-commands.spec.tsx +175 -0
- package/source/commands/status.ts +12 -0
- package/source/commands/theme.ts +12 -0
- package/source/commands/update.spec.tsx +261 -0
- package/source/commands/update.tsx +201 -0
- package/source/commands/usage.spec.tsx +495 -0
- package/source/commands/usage.tsx +100 -0
- package/source/commands.spec.ts +436 -0
- package/source/commands.ts +83 -0
- package/source/components/assistant-message.spec.tsx +796 -0
- package/source/components/assistant-message.tsx +34 -0
- package/source/components/bash-execution-indicator.tsx +21 -0
- package/source/components/cancelling-indicator.tsx +16 -0
- package/source/components/chat-queue.spec.tsx +83 -0
- package/source/components/chat-queue.tsx +36 -0
- package/source/components/checkpoint-display.spec.tsx +219 -0
- package/source/components/checkpoint-display.tsx +126 -0
- package/source/components/checkpoint-selector.spec.tsx +173 -0
- package/source/components/checkpoint-selector.tsx +173 -0
- package/source/components/development-mode-indicator.spec.tsx +268 -0
- package/source/components/development-mode-indicator.tsx +38 -0
- package/source/components/message-box.spec.tsx +427 -0
- package/source/components/message-box.tsx +87 -0
- package/source/components/model-selector.tsx +132 -0
- package/source/components/provider-selector.tsx +75 -0
- package/source/components/random-spinner.tsx +19 -0
- package/source/components/security-disclaimer.tsx +73 -0
- package/source/components/status-connection-display.spec.tsx +133 -0
- package/source/components/status.tsx +267 -0
- package/source/components/theme-selector.tsx +126 -0
- package/source/components/tool-confirmation.tsx +190 -0
- package/source/components/tool-execution-indicator.tsx +33 -0
- package/source/components/tool-message.tsx +85 -0
- package/source/components/ui/titled-box.spec.tsx +207 -0
- package/source/components/ui/titled-box.tsx +57 -0
- package/source/components/usage/progress-bar.spec.tsx +398 -0
- package/source/components/usage/progress-bar.tsx +30 -0
- package/source/components/usage/usage-display.spec.tsx +780 -0
- package/source/components/usage/usage-display.tsx +291 -0
- package/source/components/user-input.spec.tsx +327 -0
- package/source/components/user-input.tsx +533 -0
- package/source/components/user-message.spec.tsx +230 -0
- package/source/components/user-message.tsx +84 -0
- package/source/components/welcome-message.tsx +76 -0
- package/source/config/env-substitution.ts +65 -0
- package/source/config/index.spec.ts +171 -0
- package/source/config/index.ts +154 -0
- package/source/config/paths.spec.ts +241 -0
- package/source/config/paths.ts +55 -0
- package/source/config/preferences.ts +51 -0
- package/source/config/themes.ts +315 -0
- package/source/constants.ts +130 -0
- package/source/context/mode-context.spec.ts +79 -0
- package/source/context/mode-context.ts +24 -0
- package/source/custom-commands/executor.spec.ts +142 -0
- package/source/custom-commands/executor.ts +64 -0
- package/source/custom-commands/loader.spec.ts +314 -0
- package/source/custom-commands/loader.ts +153 -0
- package/source/custom-commands/parser.ts +196 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.spec.ts +39 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.tsx +511 -0
- package/source/hooks/chat-handler/conversation/tool-executor.spec.ts +50 -0
- package/source/hooks/chat-handler/conversation/tool-executor.tsx +109 -0
- package/source/hooks/chat-handler/index.ts +12 -0
- package/source/hooks/chat-handler/state/streaming-state.spec.ts +26 -0
- package/source/hooks/chat-handler/state/streaming-state.ts +19 -0
- package/source/hooks/chat-handler/types.ts +38 -0
- package/source/hooks/chat-handler/useChatHandler.spec.tsx +321 -0
- package/source/hooks/chat-handler/useChatHandler.tsx +194 -0
- package/source/hooks/chat-handler/utils/context-checker.spec.ts +60 -0
- package/source/hooks/chat-handler/utils/context-checker.tsx +73 -0
- package/source/hooks/chat-handler/utils/message-helpers.spec.ts +42 -0
- package/source/hooks/chat-handler/utils/message-helpers.tsx +36 -0
- package/source/hooks/chat-handler/utils/tool-filters.spec.ts +109 -0
- package/source/hooks/chat-handler/utils/tool-filters.ts +64 -0
- package/source/hooks/useAppHandlers.tsx +291 -0
- package/source/hooks/useAppInitialization.tsx +422 -0
- package/source/hooks/useAppState.tsx +311 -0
- package/source/hooks/useDirectoryTrust.tsx +98 -0
- package/source/hooks/useInputState.ts +414 -0
- package/source/hooks/useModeHandlers.tsx +302 -0
- package/source/hooks/useNonInteractiveMode.ts +140 -0
- package/source/hooks/useTerminalWidth.tsx +81 -0
- package/source/hooks/useTheme.ts +18 -0
- package/source/hooks/useToolHandler.tsx +349 -0
- package/source/hooks/useUIState.ts +61 -0
- package/source/init/agents-template-generator.ts +421 -0
- package/source/init/existing-rules-extractor.ts +319 -0
- package/source/init/file-scanner.spec.ts +227 -0
- package/source/init/file-scanner.ts +238 -0
- package/source/init/framework-detector.ts +382 -0
- package/source/init/language-detector.ts +269 -0
- package/source/init/project-analyzer.spec.ts +231 -0
- package/source/init/project-analyzer.ts +458 -0
- package/source/lsp/index.ts +31 -0
- package/source/lsp/lsp-client.spec.ts +508 -0
- package/source/lsp/lsp-client.ts +487 -0
- package/source/lsp/lsp-manager.spec.ts +477 -0
- package/source/lsp/lsp-manager.ts +419 -0
- package/source/lsp/protocol.spec.ts +502 -0
- package/source/lsp/protocol.ts +360 -0
- package/source/lsp/server-discovery.spec.ts +654 -0
- package/source/lsp/server-discovery.ts +515 -0
- package/source/markdown-parser/html-entities.spec.ts +88 -0
- package/source/markdown-parser/html-entities.ts +45 -0
- package/source/markdown-parser/index.spec.ts +281 -0
- package/source/markdown-parser/index.ts +126 -0
- package/source/markdown-parser/table-parser.spec.ts +133 -0
- package/source/markdown-parser/table-parser.ts +114 -0
- package/source/markdown-parser/utils.spec.ts +70 -0
- package/source/markdown-parser/utils.ts +13 -0
- package/source/mcp/mcp-client.spec.ts +81 -0
- package/source/mcp/mcp-client.ts +625 -0
- package/source/mcp/transport-factory.spec.ts +406 -0
- package/source/mcp/transport-factory.ts +312 -0
- package/source/message-handler.ts +67 -0
- package/source/model-database/database-engine.spec.ts +494 -0
- package/source/model-database/database-engine.ts +50 -0
- package/source/model-database/model-database.spec.ts +363 -0
- package/source/model-database/model-database.ts +91 -0
- package/source/model-database/model-engine.spec.ts +447 -0
- package/source/model-database/model-engine.ts +65 -0
- package/source/model-database/model-fetcher.spec.ts +583 -0
- package/source/model-database/model-fetcher.ts +330 -0
- package/source/models/index.ts +1 -0
- package/source/models/models-cache.spec.ts +214 -0
- package/source/models/models-cache.ts +78 -0
- package/source/models/models-dev-client.spec.ts +379 -0
- package/source/models/models-dev-client.ts +329 -0
- package/source/models/models-types.ts +68 -0
- package/source/prompt-history.ts +155 -0
- package/source/security/command-injection.spec.ts +240 -0
- package/source/services/checkpoint-manager.spec.ts +523 -0
- package/source/services/checkpoint-manager.ts +466 -0
- package/source/services/file-snapshot.spec.ts +569 -0
- package/source/services/file-snapshot.ts +220 -0
- package/source/test-utils/render-with-theme.tsx +48 -0
- package/source/tokenization/index.ts +1 -0
- package/source/tokenization/tokenizer-factory.spec.ts +170 -0
- package/source/tokenization/tokenizer-factory.ts +125 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.spec.ts +200 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.ts +43 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.spec.ts +236 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.ts +26 -0
- package/source/tokenization/tokenizers/llama-tokenizer.spec.ts +224 -0
- package/source/tokenization/tokenizers/llama-tokenizer.ts +41 -0
- package/source/tokenization/tokenizers/openai-tokenizer.spec.ts +184 -0
- package/source/tokenization/tokenizers/openai-tokenizer.ts +57 -0
- package/source/tool-calling/index.ts +5 -0
- package/source/tool-calling/json-parser.spec.ts +639 -0
- package/source/tool-calling/json-parser.ts +247 -0
- package/source/tool-calling/tool-parser.spec.ts +395 -0
- package/source/tool-calling/tool-parser.ts +120 -0
- package/source/tool-calling/xml-parser.spec.ts +662 -0
- package/source/tool-calling/xml-parser.ts +289 -0
- package/source/tools/execute-bash.spec.tsx +353 -0
- package/source/tools/execute-bash.tsx +219 -0
- package/source/tools/execute-function.spec.ts +130 -0
- package/source/tools/fetch-url.spec.tsx +342 -0
- package/source/tools/fetch-url.tsx +172 -0
- package/source/tools/find-files.spec.tsx +924 -0
- package/source/tools/find-files.tsx +293 -0
- package/source/tools/index.ts +102 -0
- package/source/tools/lsp-get-diagnostics.tsx +192 -0
- package/source/tools/needs-approval.spec.ts +282 -0
- package/source/tools/read-file.spec.tsx +801 -0
- package/source/tools/read-file.tsx +387 -0
- package/source/tools/search-file-contents.spec.tsx +1273 -0
- package/source/tools/search-file-contents.tsx +293 -0
- package/source/tools/string-replace.spec.tsx +730 -0
- package/source/tools/string-replace.tsx +548 -0
- package/source/tools/tool-manager.ts +210 -0
- package/source/tools/tool-registry.spec.ts +415 -0
- package/source/tools/tool-registry.ts +228 -0
- package/source/tools/web-search.tsx +223 -0
- package/source/tools/write-file.spec.tsx +559 -0
- package/source/tools/write-file.tsx +228 -0
- package/source/types/app.ts +37 -0
- package/source/types/checkpoint.ts +48 -0
- package/source/types/commands.ts +46 -0
- package/source/types/components.ts +27 -0
- package/source/types/config.ts +103 -0
- package/source/types/core-connection-status.spec.ts +67 -0
- package/source/types/core.ts +181 -0
- package/source/types/hooks.ts +50 -0
- package/source/types/index.ts +12 -0
- package/source/types/markdown-parser.ts +11 -0
- package/source/types/mcp.ts +52 -0
- package/source/types/system.ts +16 -0
- package/source/types/tokenization.ts +41 -0
- package/source/types/ui.ts +40 -0
- package/source/types/usage.ts +58 -0
- package/source/types/utils.ts +16 -0
- package/source/usage/calculator.spec.ts +385 -0
- package/source/usage/calculator.ts +104 -0
- package/source/usage/storage.spec.ts +703 -0
- package/source/usage/storage.ts +238 -0
- package/source/usage/tracker.spec.ts +456 -0
- package/source/usage/tracker.ts +102 -0
- package/source/utils/atomic-deletion.spec.ts +194 -0
- package/source/utils/atomic-deletion.ts +127 -0
- package/source/utils/bounded-map.spec.ts +300 -0
- package/source/utils/bounded-map.ts +193 -0
- package/source/utils/checkpoint-utils.spec.ts +222 -0
- package/source/utils/checkpoint-utils.ts +92 -0
- package/source/utils/error-formatter.spec.ts +169 -0
- package/source/utils/error-formatter.ts +194 -0
- package/source/utils/file-autocomplete.spec.ts +173 -0
- package/source/utils/file-autocomplete.ts +196 -0
- package/source/utils/file-cache.spec.ts +309 -0
- package/source/utils/file-cache.ts +195 -0
- package/source/utils/file-content-loader.spec.ts +180 -0
- package/source/utils/file-content-loader.ts +179 -0
- package/source/utils/file-mention-handler.spec.ts +261 -0
- package/source/utils/file-mention-handler.ts +84 -0
- package/source/utils/file-mention-parser.spec.ts +182 -0
- package/source/utils/file-mention-parser.ts +170 -0
- package/source/utils/fuzzy-matching.spec.ts +149 -0
- package/source/utils/fuzzy-matching.ts +146 -0
- package/source/utils/indentation-normalizer.spec.ts +216 -0
- package/source/utils/indentation-normalizer.ts +76 -0
- package/source/utils/installation-detector.spec.ts +178 -0
- package/source/utils/installation-detector.ts +153 -0
- package/source/utils/logging/config.spec.ts +311 -0
- package/source/utils/logging/config.ts +210 -0
- package/source/utils/logging/console-facade.spec.ts +184 -0
- package/source/utils/logging/console-facade.ts +384 -0
- package/source/utils/logging/correlation.spec.ts +679 -0
- package/source/utils/logging/correlation.ts +474 -0
- package/source/utils/logging/formatters.spec.ts +464 -0
- package/source/utils/logging/formatters.ts +207 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.spec.ts +93 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.ts +79 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.ts +43 -0
- package/source/utils/logging/health-monitor/checks/logging-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/logging-check.ts +58 -0
- package/source/utils/logging/health-monitor/checks/memory-check.spec.ts +100 -0
- package/source/utils/logging/health-monitor/checks/memory-check.ts +78 -0
- package/source/utils/logging/health-monitor/checks/performance-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/performance-check.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.ts +76 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.spec.ts +70 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.ts +138 -0
- package/source/utils/logging/health-monitor/core/health-monitor.spec.ts +58 -0
- package/source/utils/logging/health-monitor/core/health-monitor.ts +344 -0
- package/source/utils/logging/health-monitor/core/scoring.spec.ts +65 -0
- package/source/utils/logging/health-monitor/core/scoring.ts +91 -0
- package/source/utils/logging/health-monitor/index.ts +15 -0
- package/source/utils/logging/health-monitor/instances.ts +48 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.spec.ts +141 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.ts +75 -0
- package/source/utils/logging/health-monitor/types.ts +126 -0
- package/source/utils/logging/index.spec.ts +284 -0
- package/source/utils/logging/index.ts +236 -0
- package/source/utils/logging/integration.spec.ts +441 -0
- package/source/utils/logging/log-method-factory.spec.ts +573 -0
- package/source/utils/logging/log-method-factory.ts +233 -0
- package/source/utils/logging/log-query/aggregation/aggregator.spec.ts +277 -0
- package/source/utils/logging/log-query/aggregation/aggregator.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.spec.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.ts +47 -0
- package/source/utils/logging/log-query/index.ts +23 -0
- package/source/utils/logging/log-query/query/filter-predicates.spec.ts +247 -0
- package/source/utils/logging/log-query/query/filter-predicates.ts +154 -0
- package/source/utils/logging/log-query/query/query-builder.spec.ts +182 -0
- package/source/utils/logging/log-query/query/query-builder.ts +151 -0
- package/source/utils/logging/log-query/query/query-engine.spec.ts +214 -0
- package/source/utils/logging/log-query/query/query-engine.ts +45 -0
- package/source/utils/logging/log-query/storage/circular-buffer.spec.ts +143 -0
- package/source/utils/logging/log-query/storage/circular-buffer.ts +75 -0
- package/source/utils/logging/log-query/storage/index-manager.spec.ts +150 -0
- package/source/utils/logging/log-query/storage/index-manager.ts +71 -0
- package/source/utils/logging/log-query/storage/log-storage.spec.ts +257 -0
- package/source/utils/logging/log-query/storage/log-storage.ts +80 -0
- package/source/utils/logging/log-query/types.ts +163 -0
- package/source/utils/logging/log-query/utils/helpers.spec.ts +263 -0
- package/source/utils/logging/log-query/utils/helpers.ts +72 -0
- package/source/utils/logging/log-query/utils/sorting.spec.ts +182 -0
- package/source/utils/logging/log-query/utils/sorting.ts +61 -0
- package/source/utils/logging/logger-provider.spec.ts +262 -0
- package/source/utils/logging/logger-provider.ts +362 -0
- package/source/utils/logging/performance.spec.ts +209 -0
- package/source/utils/logging/performance.ts +757 -0
- package/source/utils/logging/pino-logger.spec.ts +425 -0
- package/source/utils/logging/pino-logger.ts +514 -0
- package/source/utils/logging/redaction.spec.ts +490 -0
- package/source/utils/logging/redaction.ts +267 -0
- package/source/utils/logging/request-tracker.spec.ts +1198 -0
- package/source/utils/logging/request-tracker.ts +803 -0
- package/source/utils/logging/transports.spec.ts +505 -0
- package/source/utils/logging/transports.ts +305 -0
- package/source/utils/logging/types.ts +216 -0
- package/source/utils/message-builder.spec.ts +179 -0
- package/source/utils/message-builder.ts +101 -0
- package/source/utils/message-queue.tsx +486 -0
- package/source/utils/paste-detection.spec.ts +69 -0
- package/source/utils/paste-detection.ts +124 -0
- package/source/utils/paste-roundtrip.spec.ts +442 -0
- package/source/utils/paste-utils.spec.ts +128 -0
- package/source/utils/paste-utils.ts +52 -0
- package/source/utils/programming-language-helper.spec.ts +74 -0
- package/source/utils/programming-language-helper.ts +32 -0
- package/source/utils/prompt-assembly.spec.ts +221 -0
- package/source/utils/prompt-processor.ts +173 -0
- package/source/utils/tool-args-parser.spec.ts +136 -0
- package/source/utils/tool-args-parser.ts +54 -0
- package/source/utils/tool-cancellation.spec.ts +230 -0
- package/source/utils/tool-cancellation.ts +28 -0
- package/source/utils/tool-result-display.spec.tsx +469 -0
- package/source/utils/tool-result-display.tsx +90 -0
- package/source/utils/update-checker.spec.ts +383 -0
- package/source/utils/update-checker.ts +183 -0
- package/source/wizard/config-wizard.spec.tsx +103 -0
- package/source/wizard/config-wizard.tsx +382 -0
- package/source/wizard/steps/location-step.spec.tsx +186 -0
- package/source/wizard/steps/location-step.tsx +147 -0
- package/source/wizard/steps/mcp-step.spec.tsx +607 -0
- package/source/wizard/steps/mcp-step.tsx +632 -0
- package/source/wizard/steps/provider-step.spec.tsx +342 -0
- package/source/wizard/steps/provider-step.tsx +957 -0
- package/source/wizard/steps/summary-step.spec.tsx +749 -0
- package/source/wizard/steps/summary-step.tsx +228 -0
- package/source/wizard/templates/mcp-templates.spec.ts +613 -0
- package/source/wizard/templates/mcp-templates.ts +570 -0
- package/source/wizard/templates/provider-templates.spec.ts +152 -0
- package/source/wizard/templates/provider-templates.ts +485 -0
- package/source/wizard/utils/fetch-cloud-models.spec.ts +428 -0
- package/source/wizard/utils/fetch-cloud-models.ts +223 -0
- package/source/wizard/utils/fetch-local-models.spec.ts +297 -0
- package/source/wizard/utils/fetch-local-models.ts +192 -0
- package/source/wizard/validation-array.spec.ts +264 -0
- package/source/wizard/validation.spec.ts +373 -0
- package/source/wizard/validation.ts +232 -0
- 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
|
+
});
|