@gguf/coder 0.2.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/dist/tools/execute-bash.js +3 -3
- package/dist/tools/execute-bash.js.map +1 -1
- package/dist/tools/fetch-url.js +3 -3
- package/dist/tools/fetch-url.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +1 -1
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/lsp-get-diagnostics.js +1 -1
- package/dist/tools/lsp-get-diagnostics.js.map +1 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +6 -6
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/search-file-contents.d.ts.map +1 -1
- package/dist/tools/search-file-contents.js +1 -1
- package/dist/tools/search-file-contents.js.map +1 -1
- package/dist/tools/string-replace.js +11 -11
- package/dist/tools/string-replace.js.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools/write-file.js +4 -4
- package/dist/tools/write-file.js.map +1 -1
- package/dist/utils/tool-result-display.d.ts.map +1 -1
- package/dist/utils/tool-result-display.js +3 -3
- package/dist/utils/tool-result-display.js.map +1 -1
- 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,569 @@
|
|
|
1
|
+
import {chmodSync, existsSync} from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import test from 'ava';
|
|
4
|
+
import * as fs from 'fs/promises';
|
|
5
|
+
import {FileSnapshotService} from './file-snapshot';
|
|
6
|
+
|
|
7
|
+
// Helper to create a temporary directory for tests
|
|
8
|
+
async function createTempDir(): Promise<string> {
|
|
9
|
+
const tempDir = path.join(
|
|
10
|
+
process.cwd(),
|
|
11
|
+
'.test-temp',
|
|
12
|
+
`snapshot-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
13
|
+
);
|
|
14
|
+
await fs.mkdir(tempDir, {recursive: true});
|
|
15
|
+
return tempDir;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Helper to clean up temp directory
|
|
19
|
+
async function cleanupTempDir(dir: string): Promise<void> {
|
|
20
|
+
try {
|
|
21
|
+
await fs.rm(dir, {recursive: true, force: true});
|
|
22
|
+
} catch {
|
|
23
|
+
// Ignore cleanup errors
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to create a test file
|
|
28
|
+
async function createTestFile(
|
|
29
|
+
dir: string,
|
|
30
|
+
relativePath: string,
|
|
31
|
+
content: string,
|
|
32
|
+
): Promise<string> {
|
|
33
|
+
const fullPath = path.join(dir, relativePath);
|
|
34
|
+
await fs.mkdir(path.dirname(fullPath), {recursive: true});
|
|
35
|
+
await fs.writeFile(fullPath, content, 'utf-8');
|
|
36
|
+
return fullPath;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test.serial('FileSnapshotService captures single file', async t => {
|
|
40
|
+
const tempDir = await createTempDir();
|
|
41
|
+
try {
|
|
42
|
+
await createTestFile(tempDir, 'test.txt', 'Hello, World!');
|
|
43
|
+
|
|
44
|
+
const service = new FileSnapshotService(tempDir);
|
|
45
|
+
const snapshots = await service.captureFiles(['test.txt']);
|
|
46
|
+
|
|
47
|
+
t.is(snapshots.size, 1);
|
|
48
|
+
t.is(snapshots.get('test.txt'), 'Hello, World!');
|
|
49
|
+
} finally {
|
|
50
|
+
await cleanupTempDir(tempDir);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test.serial('FileSnapshotService captures multiple files', async t => {
|
|
55
|
+
const tempDir = await createTempDir();
|
|
56
|
+
try {
|
|
57
|
+
await createTestFile(tempDir, 'file1.txt', 'Content 1');
|
|
58
|
+
await createTestFile(tempDir, 'file2.txt', 'Content 2');
|
|
59
|
+
await createTestFile(tempDir, 'file3.txt', 'Content 3');
|
|
60
|
+
|
|
61
|
+
const service = new FileSnapshotService(tempDir);
|
|
62
|
+
const snapshots = await service.captureFiles([
|
|
63
|
+
'file1.txt',
|
|
64
|
+
'file2.txt',
|
|
65
|
+
'file3.txt',
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
t.is(snapshots.size, 3);
|
|
69
|
+
t.is(snapshots.get('file1.txt'), 'Content 1');
|
|
70
|
+
t.is(snapshots.get('file2.txt'), 'Content 2');
|
|
71
|
+
t.is(snapshots.get('file3.txt'), 'Content 3');
|
|
72
|
+
} finally {
|
|
73
|
+
await cleanupTempDir(tempDir);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test.serial('FileSnapshotService captures files in subdirectories', async t => {
|
|
78
|
+
const tempDir = await createTempDir();
|
|
79
|
+
try {
|
|
80
|
+
await createTestFile(tempDir, 'src/index.ts', 'export {};');
|
|
81
|
+
await createTestFile(
|
|
82
|
+
tempDir,
|
|
83
|
+
'src/utils/helper.ts',
|
|
84
|
+
'export function help() {}',
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const service = new FileSnapshotService(tempDir);
|
|
88
|
+
const snapshots = await service.captureFiles([
|
|
89
|
+
'src/index.ts',
|
|
90
|
+
'src/utils/helper.ts',
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
t.is(snapshots.size, 2);
|
|
94
|
+
t.is(snapshots.get('src/index.ts'), 'export {};');
|
|
95
|
+
t.is(snapshots.get('src/utils/helper.ts'), 'export function help() {}');
|
|
96
|
+
} finally {
|
|
97
|
+
await cleanupTempDir(tempDir);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test.serial(
|
|
102
|
+
'FileSnapshotService handles non-existent files gracefully',
|
|
103
|
+
async t => {
|
|
104
|
+
const tempDir = await createTempDir();
|
|
105
|
+
try {
|
|
106
|
+
await createTestFile(tempDir, 'exists.txt', 'I exist');
|
|
107
|
+
|
|
108
|
+
const service = new FileSnapshotService(tempDir);
|
|
109
|
+
const snapshots = await service.captureFiles([
|
|
110
|
+
'exists.txt',
|
|
111
|
+
'does-not-exist.txt',
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
// Should only capture the existing file
|
|
115
|
+
t.is(snapshots.size, 1);
|
|
116
|
+
t.is(snapshots.get('exists.txt'), 'I exist');
|
|
117
|
+
} finally {
|
|
118
|
+
await cleanupTempDir(tempDir);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
test.serial('FileSnapshotService restores files', async t => {
|
|
124
|
+
const tempDir = await createTempDir();
|
|
125
|
+
try {
|
|
126
|
+
const service = new FileSnapshotService(tempDir);
|
|
127
|
+
const snapshots = new Map<string, string>();
|
|
128
|
+
snapshots.set('restored.txt', 'Restored content');
|
|
129
|
+
|
|
130
|
+
await service.restoreFiles(snapshots);
|
|
131
|
+
|
|
132
|
+
const restoredPath = path.join(tempDir, 'restored.txt');
|
|
133
|
+
t.true(existsSync(restoredPath));
|
|
134
|
+
const content = await fs.readFile(restoredPath, 'utf-8');
|
|
135
|
+
t.is(content, 'Restored content');
|
|
136
|
+
} finally {
|
|
137
|
+
await cleanupTempDir(tempDir);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test.serial('FileSnapshotService restores files in subdirectories', async t => {
|
|
142
|
+
const tempDir = await createTempDir();
|
|
143
|
+
try {
|
|
144
|
+
const service = new FileSnapshotService(tempDir);
|
|
145
|
+
const snapshots = new Map<string, string>();
|
|
146
|
+
snapshots.set('deep/nested/file.txt', 'Nested content');
|
|
147
|
+
|
|
148
|
+
await service.restoreFiles(snapshots);
|
|
149
|
+
|
|
150
|
+
const restoredPath = path.join(tempDir, 'deep/nested/file.txt');
|
|
151
|
+
t.true(existsSync(restoredPath));
|
|
152
|
+
const content = await fs.readFile(restoredPath, 'utf-8');
|
|
153
|
+
t.is(content, 'Nested content');
|
|
154
|
+
} finally {
|
|
155
|
+
await cleanupTempDir(tempDir);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test.serial('FileSnapshotService restores multiple files', async t => {
|
|
160
|
+
const tempDir = await createTempDir();
|
|
161
|
+
try {
|
|
162
|
+
const service = new FileSnapshotService(tempDir);
|
|
163
|
+
const snapshots = new Map<string, string>();
|
|
164
|
+
snapshots.set('file1.txt', 'Content 1');
|
|
165
|
+
snapshots.set('file2.txt', 'Content 2');
|
|
166
|
+
snapshots.set('subdir/file3.txt', 'Content 3');
|
|
167
|
+
|
|
168
|
+
await service.restoreFiles(snapshots);
|
|
169
|
+
|
|
170
|
+
t.true(existsSync(path.join(tempDir, 'file1.txt')));
|
|
171
|
+
t.true(existsSync(path.join(tempDir, 'file2.txt')));
|
|
172
|
+
t.true(existsSync(path.join(tempDir, 'subdir/file3.txt')));
|
|
173
|
+
} finally {
|
|
174
|
+
await cleanupTempDir(tempDir);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test.serial(
|
|
179
|
+
'FileSnapshotService overwrites existing files on restore',
|
|
180
|
+
async t => {
|
|
181
|
+
const tempDir = await createTempDir();
|
|
182
|
+
try {
|
|
183
|
+
await createTestFile(tempDir, 'existing.txt', 'Old content');
|
|
184
|
+
|
|
185
|
+
const service = new FileSnapshotService(tempDir);
|
|
186
|
+
const snapshots = new Map<string, string>();
|
|
187
|
+
snapshots.set('existing.txt', 'New content');
|
|
188
|
+
|
|
189
|
+
await service.restoreFiles(snapshots);
|
|
190
|
+
|
|
191
|
+
const content = await fs.readFile(
|
|
192
|
+
path.join(tempDir, 'existing.txt'),
|
|
193
|
+
'utf-8',
|
|
194
|
+
);
|
|
195
|
+
t.is(content, 'New content');
|
|
196
|
+
} finally {
|
|
197
|
+
await cleanupTempDir(tempDir);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
test.serial(
|
|
203
|
+
'FileSnapshotService getSnapshotSize calculates correct size',
|
|
204
|
+
async t => {
|
|
205
|
+
const service = new FileSnapshotService(process.cwd());
|
|
206
|
+
const snapshots = new Map<string, string>();
|
|
207
|
+
snapshots.set('file1.txt', 'Hello'); // 5 bytes
|
|
208
|
+
snapshots.set('file2.txt', 'World!'); // 6 bytes
|
|
209
|
+
|
|
210
|
+
const size = service.getSnapshotSize(snapshots);
|
|
211
|
+
|
|
212
|
+
t.is(size, 11);
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
test.serial(
|
|
217
|
+
'FileSnapshotService getSnapshotSize handles empty snapshots',
|
|
218
|
+
async t => {
|
|
219
|
+
const service = new FileSnapshotService(process.cwd());
|
|
220
|
+
const snapshots = new Map<string, string>();
|
|
221
|
+
|
|
222
|
+
const size = service.getSnapshotSize(snapshots);
|
|
223
|
+
|
|
224
|
+
t.is(size, 0);
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
test.serial(
|
|
229
|
+
'FileSnapshotService getSnapshotSize handles unicode content',
|
|
230
|
+
async t => {
|
|
231
|
+
const service = new FileSnapshotService(process.cwd());
|
|
232
|
+
const snapshots = new Map<string, string>();
|
|
233
|
+
snapshots.set('unicode.txt', '日本語'); // 9 bytes in UTF-8
|
|
234
|
+
|
|
235
|
+
const size = service.getSnapshotSize(snapshots);
|
|
236
|
+
|
|
237
|
+
t.is(size, 9);
|
|
238
|
+
},
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
test.serial(
|
|
242
|
+
'FileSnapshotService validateRestorePath validates writable paths',
|
|
243
|
+
async t => {
|
|
244
|
+
const tempDir = await createTempDir();
|
|
245
|
+
try {
|
|
246
|
+
const service = new FileSnapshotService(tempDir);
|
|
247
|
+
const snapshots = new Map<string, string>();
|
|
248
|
+
snapshots.set('new-file.txt', 'Content');
|
|
249
|
+
|
|
250
|
+
const result = await service.validateRestorePath(snapshots);
|
|
251
|
+
|
|
252
|
+
t.true(result.valid);
|
|
253
|
+
t.is(result.errors.length, 0);
|
|
254
|
+
} finally {
|
|
255
|
+
await cleanupTempDir(tempDir);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
test.serial(
|
|
261
|
+
'FileSnapshotService validateRestorePath validates existing writable files',
|
|
262
|
+
async t => {
|
|
263
|
+
const tempDir = await createTempDir();
|
|
264
|
+
try {
|
|
265
|
+
await createTestFile(tempDir, 'writable.txt', 'Original');
|
|
266
|
+
|
|
267
|
+
const service = new FileSnapshotService(tempDir);
|
|
268
|
+
const snapshots = new Map<string, string>();
|
|
269
|
+
snapshots.set('writable.txt', 'New content');
|
|
270
|
+
|
|
271
|
+
const result = await service.validateRestorePath(snapshots);
|
|
272
|
+
|
|
273
|
+
t.true(result.valid);
|
|
274
|
+
t.is(result.errors.length, 0);
|
|
275
|
+
} finally {
|
|
276
|
+
await cleanupTempDir(tempDir);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
test.serial(
|
|
282
|
+
'FileSnapshotService validateRestorePath creates missing directories',
|
|
283
|
+
async t => {
|
|
284
|
+
const tempDir = await createTempDir();
|
|
285
|
+
try {
|
|
286
|
+
const service = new FileSnapshotService(tempDir);
|
|
287
|
+
const snapshots = new Map<string, string>();
|
|
288
|
+
// Directory doesn't exist yet - should be created during validation
|
|
289
|
+
snapshots.set('nested/deep/path/file.txt', 'Content');
|
|
290
|
+
|
|
291
|
+
const result = await service.validateRestorePath(snapshots);
|
|
292
|
+
|
|
293
|
+
t.true(result.valid);
|
|
294
|
+
t.is(result.errors.length, 0);
|
|
295
|
+
// Directory should now exist (created during validation)
|
|
296
|
+
const dirPath = path.join(tempDir, 'nested/deep/path');
|
|
297
|
+
t.true(existsSync(dirPath));
|
|
298
|
+
} finally {
|
|
299
|
+
await cleanupTempDir(tempDir);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
test.serial(
|
|
305
|
+
'FileSnapshotService validateRestorePath handles non-writable directories',
|
|
306
|
+
async t => {
|
|
307
|
+
const tempDir = await createTempDir();
|
|
308
|
+
try {
|
|
309
|
+
// Create a read-only directory
|
|
310
|
+
const readOnlyDir = path.join(tempDir, 'readonly-dir');
|
|
311
|
+
await fs.mkdir(readOnlyDir, {recursive: true});
|
|
312
|
+
chmodSync(readOnlyDir, 0o444); // Read-only
|
|
313
|
+
|
|
314
|
+
const service = new FileSnapshotService(tempDir);
|
|
315
|
+
const snapshots = new Map<string, string>();
|
|
316
|
+
snapshots.set('readonly-dir/file.txt', 'Content');
|
|
317
|
+
|
|
318
|
+
const result = await service.validateRestorePath(snapshots);
|
|
319
|
+
|
|
320
|
+
t.false(result.valid);
|
|
321
|
+
t.true(result.errors.length > 0);
|
|
322
|
+
t.true(
|
|
323
|
+
result.errors.some(error =>
|
|
324
|
+
error.includes('Cannot create directory') ||
|
|
325
|
+
error.includes('Cannot write to file') ||
|
|
326
|
+
error.includes('is not writable'),
|
|
327
|
+
),
|
|
328
|
+
);
|
|
329
|
+
} finally {
|
|
330
|
+
// Restore permissions for cleanup
|
|
331
|
+
try {
|
|
332
|
+
chmodSync(path.join(tempDir, 'readonly-dir'), 0o755);
|
|
333
|
+
} catch {
|
|
334
|
+
// Ignore cleanup errors
|
|
335
|
+
}
|
|
336
|
+
await cleanupTempDir(tempDir);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
test.serial(
|
|
342
|
+
'FileSnapshotService validateRestorePath handles non-writable existing files',
|
|
343
|
+
async t => {
|
|
344
|
+
const tempDir = await createTempDir();
|
|
345
|
+
try {
|
|
346
|
+
// Create a read-only file
|
|
347
|
+
const readOnlyFile = path.join(tempDir, 'readonly.txt');
|
|
348
|
+
await fs.writeFile(readOnlyFile, 'Original', 'utf-8');
|
|
349
|
+
chmodSync(readOnlyFile, 0o444); // Read-only
|
|
350
|
+
|
|
351
|
+
const service = new FileSnapshotService(tempDir);
|
|
352
|
+
const snapshots = new Map<string, string>();
|
|
353
|
+
snapshots.set('readonly.txt', 'New content');
|
|
354
|
+
|
|
355
|
+
const result = await service.validateRestorePath(snapshots);
|
|
356
|
+
|
|
357
|
+
t.false(result.valid);
|
|
358
|
+
t.true(result.errors.length > 0);
|
|
359
|
+
t.true(
|
|
360
|
+
result.errors.some(error =>
|
|
361
|
+
error.includes('Cannot write to file'),
|
|
362
|
+
),
|
|
363
|
+
);
|
|
364
|
+
} finally {
|
|
365
|
+
// Restore permissions for cleanup
|
|
366
|
+
try {
|
|
367
|
+
chmodSync(path.join(tempDir, 'readonly.txt'), 0o644);
|
|
368
|
+
} catch {
|
|
369
|
+
// Ignore cleanup errors
|
|
370
|
+
}
|
|
371
|
+
await cleanupTempDir(tempDir);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
test.serial(
|
|
377
|
+
'FileSnapshotService validateRestorePath skips file checks when directory creation fails',
|
|
378
|
+
async t => {
|
|
379
|
+
const tempDir = await createTempDir();
|
|
380
|
+
try {
|
|
381
|
+
// Create a read-only parent directory
|
|
382
|
+
const parentDir = path.join(tempDir, 'parent');
|
|
383
|
+
await fs.mkdir(parentDir, {recursive: true});
|
|
384
|
+
chmodSync(parentDir, 0o444); // Read-only
|
|
385
|
+
|
|
386
|
+
const service = new FileSnapshotService(tempDir);
|
|
387
|
+
const snapshots = new Map<string, string>();
|
|
388
|
+
// Try to create a file in a subdirectory of the read-only parent
|
|
389
|
+
snapshots.set('parent/child/file.txt', 'Content');
|
|
390
|
+
|
|
391
|
+
const result = await service.validateRestorePath(snapshots);
|
|
392
|
+
|
|
393
|
+
t.false(result.valid);
|
|
394
|
+
t.true(result.errors.length > 0);
|
|
395
|
+
// Should only have directory creation error, not file write error
|
|
396
|
+
// because we skip file checks when directory creation fails
|
|
397
|
+
const dirErrors = result.errors.filter(error =>
|
|
398
|
+
error.includes('Cannot create directory'),
|
|
399
|
+
);
|
|
400
|
+
t.true(dirErrors.length > 0);
|
|
401
|
+
} finally {
|
|
402
|
+
// Restore permissions for cleanup
|
|
403
|
+
try {
|
|
404
|
+
chmodSync(path.join(tempDir, 'parent'), 0o755);
|
|
405
|
+
} catch {
|
|
406
|
+
// Ignore cleanup errors
|
|
407
|
+
}
|
|
408
|
+
await cleanupTempDir(tempDir);
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
test.serial(
|
|
414
|
+
'FileSnapshotService validateRestorePath handles multiple files with mixed scenarios',
|
|
415
|
+
async t => {
|
|
416
|
+
const tempDir = await createTempDir();
|
|
417
|
+
try {
|
|
418
|
+
// Create one writable file
|
|
419
|
+
await createTestFile(tempDir, 'writable.txt', 'Original');
|
|
420
|
+
// Create one read-only file
|
|
421
|
+
const readOnlyFile = path.join(tempDir, 'readonly.txt');
|
|
422
|
+
await fs.writeFile(readOnlyFile, 'Original', 'utf-8');
|
|
423
|
+
chmodSync(readOnlyFile, 0o444);
|
|
424
|
+
|
|
425
|
+
const service = new FileSnapshotService(tempDir);
|
|
426
|
+
const snapshots = new Map<string, string>();
|
|
427
|
+
snapshots.set('writable.txt', 'New content');
|
|
428
|
+
snapshots.set('readonly.txt', 'New content');
|
|
429
|
+
snapshots.set('new-file.txt', 'Content');
|
|
430
|
+
snapshots.set('nested/new-file.txt', 'Content');
|
|
431
|
+
|
|
432
|
+
const result = await service.validateRestorePath(snapshots);
|
|
433
|
+
|
|
434
|
+
t.false(result.valid);
|
|
435
|
+
t.true(result.errors.length > 0);
|
|
436
|
+
// Should have error for read-only file
|
|
437
|
+
t.true(
|
|
438
|
+
result.errors.some(error =>
|
|
439
|
+
error.includes('Cannot write to file') &&
|
|
440
|
+
error.includes('readonly.txt'),
|
|
441
|
+
),
|
|
442
|
+
);
|
|
443
|
+
} finally {
|
|
444
|
+
// Restore permissions for cleanup
|
|
445
|
+
try {
|
|
446
|
+
chmodSync(path.join(tempDir, 'readonly.txt'), 0o644);
|
|
447
|
+
} catch {
|
|
448
|
+
// Ignore cleanup errors
|
|
449
|
+
}
|
|
450
|
+
await cleanupTempDir(tempDir);
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
test.serial(
|
|
456
|
+
'FileSnapshotService validateRestorePath handles deeply nested paths',
|
|
457
|
+
async t => {
|
|
458
|
+
const tempDir = await createTempDir();
|
|
459
|
+
try {
|
|
460
|
+
const service = new FileSnapshotService(tempDir);
|
|
461
|
+
const snapshots = new Map<string, string>();
|
|
462
|
+
snapshots.set(
|
|
463
|
+
'level1/level2/level3/level4/file.txt',
|
|
464
|
+
'Deeply nested content',
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
const result = await service.validateRestorePath(snapshots);
|
|
468
|
+
|
|
469
|
+
t.true(result.valid);
|
|
470
|
+
t.is(result.errors.length, 0);
|
|
471
|
+
// All nested directories should be created
|
|
472
|
+
const nestedDir = path.join(
|
|
473
|
+
tempDir,
|
|
474
|
+
'level1/level2/level3/level4',
|
|
475
|
+
);
|
|
476
|
+
t.true(existsSync(nestedDir));
|
|
477
|
+
} finally {
|
|
478
|
+
await cleanupTempDir(tempDir);
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
test.serial('FileSnapshotService getModifiedFiles returns array', async t => {
|
|
484
|
+
// Note: This test runs in a git directory, so it may return files.
|
|
485
|
+
// The important thing is that it returns an array and doesn't throw.
|
|
486
|
+
const tempDir = await createTempDir();
|
|
487
|
+
try {
|
|
488
|
+
const service = new FileSnapshotService(tempDir);
|
|
489
|
+
const files = service.getModifiedFiles();
|
|
490
|
+
|
|
491
|
+
t.true(Array.isArray(files));
|
|
492
|
+
// Files should all be strings
|
|
493
|
+
t.true(files.every(f => typeof f === 'string'));
|
|
494
|
+
} finally {
|
|
495
|
+
await cleanupTempDir(tempDir);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test.serial('FileSnapshotService captures empty files', async t => {
|
|
500
|
+
const tempDir = await createTempDir();
|
|
501
|
+
try {
|
|
502
|
+
await createTestFile(tempDir, 'empty.txt', '');
|
|
503
|
+
|
|
504
|
+
const service = new FileSnapshotService(tempDir);
|
|
505
|
+
const snapshots = await service.captureFiles(['empty.txt']);
|
|
506
|
+
|
|
507
|
+
t.is(snapshots.size, 1);
|
|
508
|
+
t.is(snapshots.get('empty.txt'), '');
|
|
509
|
+
} finally {
|
|
510
|
+
await cleanupTempDir(tempDir);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test.serial(
|
|
515
|
+
'FileSnapshotService captures files with special characters in content',
|
|
516
|
+
async t => {
|
|
517
|
+
const tempDir = await createTempDir();
|
|
518
|
+
try {
|
|
519
|
+
const specialContent = 'Special chars: \n\t\r "quotes" & <tags> 日本語';
|
|
520
|
+
await createTestFile(tempDir, 'special.txt', specialContent);
|
|
521
|
+
|
|
522
|
+
const service = new FileSnapshotService(tempDir);
|
|
523
|
+
const snapshots = await service.captureFiles(['special.txt']);
|
|
524
|
+
|
|
525
|
+
t.is(snapshots.get('special.txt'), specialContent);
|
|
526
|
+
} finally {
|
|
527
|
+
await cleanupTempDir(tempDir);
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
test.serial('FileSnapshotService uses relative paths in snapshots', async t => {
|
|
533
|
+
const tempDir = await createTempDir();
|
|
534
|
+
try {
|
|
535
|
+
await createTestFile(tempDir, 'src/file.ts', 'content');
|
|
536
|
+
|
|
537
|
+
const service = new FileSnapshotService(tempDir);
|
|
538
|
+
const snapshots = await service.captureFiles(['src/file.ts']);
|
|
539
|
+
|
|
540
|
+
// Should use relative path, not absolute
|
|
541
|
+
const keys = Array.from(snapshots.keys());
|
|
542
|
+
t.is(keys.length, 1);
|
|
543
|
+
t.is(keys[0], 'src/file.ts');
|
|
544
|
+
t.false(keys[0].startsWith('/'));
|
|
545
|
+
} finally {
|
|
546
|
+
await cleanupTempDir(tempDir);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test.serial('FileSnapshotService handles large files', async t => {
|
|
551
|
+
const tempDir = await createTempDir();
|
|
552
|
+
try {
|
|
553
|
+
const largeContent = 'x'.repeat(100000); // 100KB
|
|
554
|
+
await createTestFile(tempDir, 'large.txt', largeContent);
|
|
555
|
+
|
|
556
|
+
const service = new FileSnapshotService(tempDir);
|
|
557
|
+
const snapshots = await service.captureFiles(['large.txt']);
|
|
558
|
+
|
|
559
|
+
t.is(snapshots.get('large.txt')?.length, 100000);
|
|
560
|
+
} finally {
|
|
561
|
+
await cleanupTempDir(tempDir);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test.serial('FileSnapshotService defaults to current working directory', t => {
|
|
566
|
+
const service = new FileSnapshotService();
|
|
567
|
+
// Just verify it doesn't throw
|
|
568
|
+
t.truthy(service);
|
|
569
|
+
});
|