@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,64 @@
|
|
|
1
|
+
import {substituteTemplateVariables} from '@/custom-commands/parser';
|
|
2
|
+
import type {CustomCommand} from '@/types/index';
|
|
3
|
+
|
|
4
|
+
export class CustomCommandExecutor {
|
|
5
|
+
/**
|
|
6
|
+
* Execute a custom command with given arguments
|
|
7
|
+
*/
|
|
8
|
+
execute(command: CustomCommand, args: string[]): string {
|
|
9
|
+
// Build template variables from parameters and arguments
|
|
10
|
+
const variables: Record<string, string> = {};
|
|
11
|
+
|
|
12
|
+
if (command.metadata.parameters && command.metadata.parameters.length > 0) {
|
|
13
|
+
// Map arguments to parameters
|
|
14
|
+
command.metadata.parameters.forEach((param: string, index: number) => {
|
|
15
|
+
variables[param] = args[index] || '';
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Also provide all args as a single variable
|
|
19
|
+
variables['args'] = args.join(' ');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Add some default context variables
|
|
23
|
+
variables['cwd'] = process.cwd();
|
|
24
|
+
variables['command'] = command.fullName;
|
|
25
|
+
|
|
26
|
+
// Substitute variables in the command content
|
|
27
|
+
const promptContent = substituteTemplateVariables(
|
|
28
|
+
command.content,
|
|
29
|
+
variables,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Add a note about this being a custom command that can be improved
|
|
33
|
+
const fullPrompt = `[Executing custom command: /${command.fullName}]\n\n${promptContent}\n\n[Note: If this custom command could be improved, please provide feedback on how to enhance it.]`;
|
|
34
|
+
|
|
35
|
+
// Execute the prompt as if the user typed it
|
|
36
|
+
return fullPrompt;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Format command help text
|
|
41
|
+
*/
|
|
42
|
+
formatHelp(command: CustomCommand): string {
|
|
43
|
+
const parts: string[] = [`/${command.fullName}`];
|
|
44
|
+
|
|
45
|
+
if (command.metadata.parameters && command.metadata.parameters.length > 0) {
|
|
46
|
+
parts.push(
|
|
47
|
+
command.metadata.parameters.map((p: string) => `<${p}>`).join(' '),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (command.metadata.description) {
|
|
52
|
+
parts.push(`- ${command.metadata.description}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (command.metadata.aliases && command.metadata.aliases.length > 0) {
|
|
56
|
+
const aliasNames = command.metadata.aliases.map((a: string) =>
|
|
57
|
+
command.namespace ? `${command.namespace}:${a}` : a,
|
|
58
|
+
);
|
|
59
|
+
parts.push(`(aliases: ${aliasNames.join(', ')})`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return parts.join(' ');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { writeFileSync, rmSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import test from 'ava';
|
|
5
|
+
import { CustomCommandLoader } from './loader';
|
|
6
|
+
|
|
7
|
+
// Helper to create a valid custom command file
|
|
8
|
+
function createCommandFile(path: string, content: string) {
|
|
9
|
+
const fullContent = `---
|
|
10
|
+
description: ${content}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
${content}`;
|
|
14
|
+
writeFileSync(path, fullContent, 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Helper to create a unique test directory for each test
|
|
18
|
+
function createTestDir(testName: string): string {
|
|
19
|
+
const dir = join(tmpdir(), `coder-cmd-test-${Date.now()}-${testName}`);
|
|
20
|
+
mkdirSync(dir, {recursive: true});
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Helper to clean up test directory
|
|
25
|
+
function cleanupTestDir(dir: string) {
|
|
26
|
+
if (existsSync(dir)) {
|
|
27
|
+
rmSync(dir, {recursive: true, force: true});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test('CustomCommandLoader - constructor initializes with project root', t => {
|
|
32
|
+
const testDir = createTestDir('constructor');
|
|
33
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
34
|
+
|
|
35
|
+
const loader = new CustomCommandLoader(testDir);
|
|
36
|
+
t.truthy(loader);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('CustomCommandLoader - constructor uses cwd when no path provided', t => {
|
|
40
|
+
const loader = new CustomCommandLoader();
|
|
41
|
+
t.truthy(loader);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('CustomCommandLoader - hasCustomCommands returns false when no commands directory', t => {
|
|
45
|
+
const testDir = createTestDir('has-no-commands');
|
|
46
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
47
|
+
|
|
48
|
+
const loader = new CustomCommandLoader(testDir);
|
|
49
|
+
t.false(loader.hasCustomCommands());
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('CustomCommandLoader - hasCustomCommands returns true when commands directory exists', t => {
|
|
53
|
+
const testDir = createTestDir('has-commands');
|
|
54
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
55
|
+
|
|
56
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
57
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
58
|
+
|
|
59
|
+
const loader = new CustomCommandLoader(testDir);
|
|
60
|
+
t.true(loader.hasCustomCommands());
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('CustomCommandLoader - getCommandsDirectory returns correct path', t => {
|
|
64
|
+
const testDir = createTestDir('get-dir');
|
|
65
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
66
|
+
|
|
67
|
+
const loader = new CustomCommandLoader(testDir);
|
|
68
|
+
const expectedPath = join(testDir, '.coder', 'commands');
|
|
69
|
+
t.is(loader.getCommandsDirectory(), expectedPath);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('CustomCommandLoader - loadCommands with no commands directory', t => {
|
|
73
|
+
const testDir = createTestDir('no-dir');
|
|
74
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
75
|
+
|
|
76
|
+
const loader = new CustomCommandLoader(testDir);
|
|
77
|
+
t.notThrows(() => loader.loadCommands());
|
|
78
|
+
|
|
79
|
+
const commands = loader.getAllCommands();
|
|
80
|
+
t.is(commands.length, 0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('CustomCommandLoader - loadCommands loads single command', t => {
|
|
84
|
+
const testDir = createTestDir('single-cmd');
|
|
85
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
86
|
+
|
|
87
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
88
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
89
|
+
createCommandFile(join(commandsDir, 'test.md'), 'A test command');
|
|
90
|
+
|
|
91
|
+
const loader = new CustomCommandLoader(testDir);
|
|
92
|
+
loader.loadCommands();
|
|
93
|
+
|
|
94
|
+
const commands = loader.getAllCommands();
|
|
95
|
+
t.is(commands.length, 1);
|
|
96
|
+
t.is(commands[0].name, 'test');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('CustomCommandLoader - loadCommands loads multiple commands', t => {
|
|
100
|
+
const testDir = createTestDir('multi-cmd');
|
|
101
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
102
|
+
|
|
103
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
104
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
105
|
+
createCommandFile(join(commandsDir, 'cmd1.md'), 'Command 1');
|
|
106
|
+
createCommandFile(join(commandsDir, 'cmd2.md'), 'Command 2');
|
|
107
|
+
createCommandFile(join(commandsDir, 'cmd3.md'), 'Command 3');
|
|
108
|
+
|
|
109
|
+
const loader = new CustomCommandLoader(testDir);
|
|
110
|
+
loader.loadCommands();
|
|
111
|
+
|
|
112
|
+
const commands = loader.getAllCommands();
|
|
113
|
+
t.is(commands.length, 3);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('CustomCommandLoader - loadCommands loads namespaced commands', t => {
|
|
117
|
+
const testDir = createTestDir('namespace');
|
|
118
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
119
|
+
|
|
120
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
121
|
+
const namespaceDir = join(commandsDir, 'git');
|
|
122
|
+
mkdirSync(namespaceDir, {recursive: true});
|
|
123
|
+
createCommandFile(join(namespaceDir, 'commit.md'), 'Git commit command');
|
|
124
|
+
|
|
125
|
+
const loader = new CustomCommandLoader(testDir);
|
|
126
|
+
loader.loadCommands();
|
|
127
|
+
|
|
128
|
+
const command = loader.getCommand('git:commit');
|
|
129
|
+
t.truthy(command);
|
|
130
|
+
t.is(command?.name, 'commit');
|
|
131
|
+
t.is(command?.namespace, 'git');
|
|
132
|
+
t.is(command?.fullName, 'git:commit');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('CustomCommandLoader - getCommand retrieves command by name', t => {
|
|
136
|
+
const testDir = createTestDir('get-cmd');
|
|
137
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
138
|
+
|
|
139
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
140
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
141
|
+
createCommandFile(join(commandsDir, 'test.md'), 'Test command');
|
|
142
|
+
|
|
143
|
+
const loader = new CustomCommandLoader(testDir);
|
|
144
|
+
loader.loadCommands();
|
|
145
|
+
|
|
146
|
+
const command = loader.getCommand('test');
|
|
147
|
+
t.truthy(command);
|
|
148
|
+
t.is(command?.name, 'test');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('CustomCommandLoader - getCommand returns undefined for non-existent command', t => {
|
|
152
|
+
const testDir = createTestDir('get-nonexistent');
|
|
153
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
154
|
+
|
|
155
|
+
const loader = new CustomCommandLoader(testDir);
|
|
156
|
+
loader.loadCommands();
|
|
157
|
+
|
|
158
|
+
const command = loader.getCommand('nonexistent');
|
|
159
|
+
t.is(command, undefined);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('CustomCommandLoader - getAllCommands returns array', t => {
|
|
163
|
+
const testDir = createTestDir('get-all');
|
|
164
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
165
|
+
|
|
166
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
167
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
168
|
+
createCommandFile(join(commandsDir, 'test.md'), 'Test command');
|
|
169
|
+
|
|
170
|
+
const loader = new CustomCommandLoader(testDir);
|
|
171
|
+
loader.loadCommands();
|
|
172
|
+
|
|
173
|
+
const commands = loader.getAllCommands();
|
|
174
|
+
t.true(Array.isArray(commands));
|
|
175
|
+
t.is(commands.length, 1);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('CustomCommandLoader - getSuggestions returns matching commands', t => {
|
|
179
|
+
const testDir = createTestDir('suggestions');
|
|
180
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
181
|
+
|
|
182
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
183
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
184
|
+
createCommandFile(join(commandsDir, 'test1.md'), 'Test 1');
|
|
185
|
+
createCommandFile(join(commandsDir, 'test2.md'), 'Test 2');
|
|
186
|
+
createCommandFile(join(commandsDir, 'other.md'), 'Other');
|
|
187
|
+
|
|
188
|
+
const loader = new CustomCommandLoader(testDir);
|
|
189
|
+
loader.loadCommands();
|
|
190
|
+
|
|
191
|
+
const suggestions = loader.getSuggestions('test');
|
|
192
|
+
|
|
193
|
+
t.true(suggestions.includes('test1'));
|
|
194
|
+
t.true(suggestions.includes('test2'));
|
|
195
|
+
t.false(suggestions.includes('other'));
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('CustomCommandLoader - getSuggestions is case insensitive', t => {
|
|
199
|
+
const testDir = createTestDir('case-insensitive');
|
|
200
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
201
|
+
|
|
202
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
203
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
204
|
+
createCommandFile(join(commandsDir, 'MyCommand.md'), 'My command');
|
|
205
|
+
|
|
206
|
+
const loader = new CustomCommandLoader(testDir);
|
|
207
|
+
loader.loadCommands();
|
|
208
|
+
|
|
209
|
+
const suggestionsLower = loader.getSuggestions('mycommand');
|
|
210
|
+
const suggestionsUpper = loader.getSuggestions('MYCOMMAND');
|
|
211
|
+
|
|
212
|
+
t.true(suggestionsLower.includes('MyCommand'));
|
|
213
|
+
t.true(suggestionsUpper.includes('MyCommand'));
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('CustomCommandLoader - getSuggestions returns empty array when no matches', t => {
|
|
217
|
+
const testDir = createTestDir('no-matches');
|
|
218
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
219
|
+
|
|
220
|
+
const loader = new CustomCommandLoader(testDir);
|
|
221
|
+
loader.loadCommands();
|
|
222
|
+
|
|
223
|
+
const suggestions = loader.getSuggestions('nonexistent');
|
|
224
|
+
t.deepEqual(suggestions, []);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('CustomCommandLoader - loadCommands clears existing commands', t => {
|
|
228
|
+
const testDir = createTestDir('clears');
|
|
229
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
230
|
+
|
|
231
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
232
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
233
|
+
createCommandFile(join(commandsDir, 'test.md'), 'Test');
|
|
234
|
+
|
|
235
|
+
const loader = new CustomCommandLoader(testDir);
|
|
236
|
+
loader.loadCommands();
|
|
237
|
+
t.is(loader.getAllCommands().length, 1);
|
|
238
|
+
|
|
239
|
+
// Reload
|
|
240
|
+
loader.loadCommands();
|
|
241
|
+
t.is(loader.getAllCommands().length, 1);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('CustomCommandLoader - loadCommands handles invalid command files gracefully', t => {
|
|
245
|
+
const testDir = createTestDir('invalid');
|
|
246
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
247
|
+
|
|
248
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
249
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
250
|
+
|
|
251
|
+
// Create invalid file (no frontmatter)
|
|
252
|
+
writeFileSync(join(commandsDir, 'invalid.md'), 'Just content without frontmatter', 'utf-8');
|
|
253
|
+
|
|
254
|
+
// Create valid file
|
|
255
|
+
createCommandFile(join(commandsDir, 'valid.md'), 'Valid command');
|
|
256
|
+
|
|
257
|
+
const loader = new CustomCommandLoader(testDir);
|
|
258
|
+
t.notThrows(() => loader.loadCommands());
|
|
259
|
+
|
|
260
|
+
// Should still load the valid command
|
|
261
|
+
const commands = loader.getAllCommands();
|
|
262
|
+
t.true(commands.some(c => c.name === 'valid'));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('CustomCommandLoader - handles deeply nested namespaces', t => {
|
|
266
|
+
const testDir = createTestDir('nested');
|
|
267
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
268
|
+
|
|
269
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
270
|
+
const deepDir = join(commandsDir, 'level1', 'level2', 'level3');
|
|
271
|
+
mkdirSync(deepDir, {recursive: true});
|
|
272
|
+
createCommandFile(join(deepDir, 'deep.md'), 'Deep command');
|
|
273
|
+
|
|
274
|
+
const loader = new CustomCommandLoader(testDir);
|
|
275
|
+
loader.loadCommands();
|
|
276
|
+
|
|
277
|
+
const command = loader.getCommand('level1:level2:level3:deep');
|
|
278
|
+
t.truthy(command);
|
|
279
|
+
t.is(command?.fullName, 'level1:level2:level3:deep');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('CustomCommandLoader - ignores non-markdown files', t => {
|
|
283
|
+
const testDir = createTestDir('ignore-non-md');
|
|
284
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
285
|
+
|
|
286
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
287
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
288
|
+
createCommandFile(join(commandsDir, 'test.md'), 'Test command');
|
|
289
|
+
writeFileSync(join(commandsDir, 'test.txt'), 'Not a command', 'utf-8');
|
|
290
|
+
writeFileSync(join(commandsDir, 'test.js'), 'console.log("test");', 'utf-8');
|
|
291
|
+
|
|
292
|
+
const loader = new CustomCommandLoader(testDir);
|
|
293
|
+
loader.loadCommands();
|
|
294
|
+
|
|
295
|
+
// Should only load the .md file
|
|
296
|
+
t.is(loader.getAllCommands().length, 1);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('CustomCommandLoader - loadCommands clears aliases on reload', t => {
|
|
300
|
+
const testDir = createTestDir('clear-aliases');
|
|
301
|
+
t.teardown(() => cleanupTestDir(testDir));
|
|
302
|
+
|
|
303
|
+
const commandsDir = join(testDir, '.coder', 'commands');
|
|
304
|
+
mkdirSync(commandsDir, {recursive: true});
|
|
305
|
+
createCommandFile(join(commandsDir, 'test.md'), 'Test');
|
|
306
|
+
|
|
307
|
+
const loader = new CustomCommandLoader(testDir);
|
|
308
|
+
loader.loadCommands();
|
|
309
|
+
t.is(loader.getAllCommands().length, 1);
|
|
310
|
+
|
|
311
|
+
// Reload
|
|
312
|
+
loader.loadCommands();
|
|
313
|
+
t.is(loader.getAllCommands().length, 1);
|
|
314
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {existsSync, readdirSync, statSync} from 'fs';
|
|
2
|
+
import {basename, join} from 'path';
|
|
3
|
+
import {parseCommandFile} from '@/custom-commands/parser';
|
|
4
|
+
import type {CustomCommand} from '@/types/index';
|
|
5
|
+
import {logError} from '@/utils/message-queue';
|
|
6
|
+
|
|
7
|
+
export class CustomCommandLoader {
|
|
8
|
+
private commands: Map<string, CustomCommand> = new Map();
|
|
9
|
+
private aliases: Map<string, string> = new Map(); // alias -> command name
|
|
10
|
+
private projectRoot: string;
|
|
11
|
+
private commandsDir: string;
|
|
12
|
+
|
|
13
|
+
constructor(projectRoot: string = process.cwd()) {
|
|
14
|
+
this.projectRoot = projectRoot;
|
|
15
|
+
// nosemgrep
|
|
16
|
+
this.commandsDir = join(projectRoot, '.coder', 'commands'); // nosemgrep
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load all custom commands from the .coder/commands directory
|
|
21
|
+
*/
|
|
22
|
+
loadCommands(): void {
|
|
23
|
+
this.commands.clear();
|
|
24
|
+
this.aliases.clear();
|
|
25
|
+
|
|
26
|
+
if (!existsSync(this.commandsDir)) {
|
|
27
|
+
return; // No custom commands directory
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.scanDirectory(this.commandsDir);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Recursively scan directory for .md files
|
|
35
|
+
*/
|
|
36
|
+
private scanDirectory(dir: string, namespace?: string): void {
|
|
37
|
+
const entries = readdirSync(dir);
|
|
38
|
+
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const fullPath = join(dir, entry); // nosemgrep
|
|
41
|
+
const stat = statSync(fullPath);
|
|
42
|
+
|
|
43
|
+
if (stat.isDirectory()) {
|
|
44
|
+
// Subdirectory becomes a namespace
|
|
45
|
+
const subNamespace = namespace ? `${namespace}:${entry}` : entry;
|
|
46
|
+
this.scanDirectory(fullPath, subNamespace);
|
|
47
|
+
} else if (entry.endsWith('.md')) {
|
|
48
|
+
// Parse and register command
|
|
49
|
+
this.loadCommand(fullPath, namespace);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load a single command file
|
|
56
|
+
*/
|
|
57
|
+
private loadCommand(filePath: string, namespace?: string): void {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = parseCommandFile(filePath);
|
|
60
|
+
const commandName = basename(filePath, '.md');
|
|
61
|
+
const fullName = namespace ? `${namespace}:${commandName}` : commandName;
|
|
62
|
+
|
|
63
|
+
const command: CustomCommand = {
|
|
64
|
+
name: commandName,
|
|
65
|
+
path: filePath,
|
|
66
|
+
namespace,
|
|
67
|
+
fullName,
|
|
68
|
+
metadata: parsed.metadata,
|
|
69
|
+
content: parsed.content,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Register main command
|
|
73
|
+
this.commands.set(fullName, command);
|
|
74
|
+
|
|
75
|
+
// Register aliases
|
|
76
|
+
if (parsed.metadata.aliases) {
|
|
77
|
+
for (const alias of parsed.metadata.aliases) {
|
|
78
|
+
const fullAlias = namespace ? `${namespace}:${alias}` : alias;
|
|
79
|
+
this.aliases.set(fullAlias, fullName);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logError(
|
|
84
|
+
`Failed to load custom command from ${filePath}: ${String(error)}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get a command by name (checking aliases too)
|
|
91
|
+
*/
|
|
92
|
+
getCommand(name: string): CustomCommand | undefined {
|
|
93
|
+
// Check direct command name
|
|
94
|
+
const command = this.commands.get(name);
|
|
95
|
+
if (command) return command;
|
|
96
|
+
|
|
97
|
+
// Check aliases
|
|
98
|
+
const aliasTarget = this.aliases.get(name);
|
|
99
|
+
if (aliasTarget) {
|
|
100
|
+
return this.commands.get(aliasTarget);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get all available commands
|
|
108
|
+
*/
|
|
109
|
+
getAllCommands(): CustomCommand[] {
|
|
110
|
+
return Array.from(this.commands.values());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get command suggestions for autocomplete
|
|
115
|
+
*/
|
|
116
|
+
getSuggestions(prefix: string): string[] {
|
|
117
|
+
const suggestions: string[] = [];
|
|
118
|
+
const lowerPrefix = prefix.toLowerCase();
|
|
119
|
+
|
|
120
|
+
// Add matching command names
|
|
121
|
+
for (const [name, _command] of this.commands.entries()) {
|
|
122
|
+
if (name.toLowerCase().startsWith(lowerPrefix)) {
|
|
123
|
+
suggestions.push(name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add matching aliases
|
|
128
|
+
for (const [alias, _target] of this.aliases.entries()) {
|
|
129
|
+
if (
|
|
130
|
+
alias.toLowerCase().startsWith(lowerPrefix) &&
|
|
131
|
+
!suggestions.includes(alias)
|
|
132
|
+
) {
|
|
133
|
+
suggestions.push(alias);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return suggestions.sort();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if commands directory exists
|
|
142
|
+
*/
|
|
143
|
+
hasCustomCommands(): boolean {
|
|
144
|
+
return existsSync(this.commandsDir);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get the commands directory path
|
|
149
|
+
*/
|
|
150
|
+
getCommandsDirectory(): string {
|
|
151
|
+
return this.commandsDir;
|
|
152
|
+
}
|
|
153
|
+
}
|