@vybestack/llxprt-code 0.1.20 → 0.1.21
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/README.md +31 -0
- package/dist/package.json +3 -3
- package/dist/src/config/auth.test.d.ts +6 -0
- package/dist/src/config/auth.test.js +57 -0
- package/dist/src/config/auth.test.js.map +1 -0
- package/dist/src/config/config.d.ts +1 -1
- package/dist/src/config/config.js +27 -14
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/keyBindings.js +2 -2
- package/dist/src/config/keyBindings.test.d.ts +6 -0
- package/dist/src/config/keyBindings.test.js +51 -0
- package/dist/src/config/keyBindings.test.js.map +1 -0
- package/dist/src/config/logging/loggingConfig.test.d.ts +6 -0
- package/dist/src/config/logging/loggingConfig.test.js +363 -0
- package/dist/src/config/logging/loggingConfig.test.js.map +1 -0
- package/dist/src/config/settingsSchema.d.ts +0 -9
- package/dist/src/config/settingsSchema.js +0 -9
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.d.ts +6 -0
- package/dist/src/config/settingsSchema.test.js +195 -0
- package/dist/src/config/settingsSchema.test.js.map +1 -0
- package/dist/src/config/trustedFolders.test.d.ts +6 -0
- package/dist/src/config/trustedFolders.test.js +156 -0
- package/dist/src/config/trustedFolders.test.js.map +1 -0
- package/dist/src/gemini.js +14 -4
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.d.ts +6 -0
- package/dist/src/gemini.test.js +199 -0
- package/dist/src/gemini.test.js.map +1 -0
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/integration-tests/base-url-behavior.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/base-url-behavior.integration.test.js +492 -0
- package/dist/src/integration-tests/base-url-behavior.integration.test.js.map +1 -0
- package/dist/src/integration-tests/cli-args.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/cli-args.integration.test.js +398 -0
- package/dist/src/integration-tests/cli-args.integration.test.js.map +1 -0
- package/dist/src/integration-tests/compression-settings-apply.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/compression-settings-apply.integration.test.js +436 -0
- package/dist/src/integration-tests/compression-settings-apply.integration.test.js.map +1 -0
- package/dist/src/integration-tests/ephemeral-settings.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/ephemeral-settings.integration.test.js +290 -0
- package/dist/src/integration-tests/ephemeral-settings.integration.test.js.map +1 -0
- package/dist/src/integration-tests/model-params-isolation.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/model-params-isolation.integration.test.js +564 -0
- package/dist/src/integration-tests/model-params-isolation.integration.test.js.map +1 -0
- package/dist/src/integration-tests/modelParams.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/modelParams.integration.test.js +784 -0
- package/dist/src/integration-tests/modelParams.integration.test.js.map +1 -0
- package/dist/src/integration-tests/profile-keyfile.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/profile-keyfile.integration.test.js +429 -0
- package/dist/src/integration-tests/profile-keyfile.integration.test.js.map +1 -0
- package/dist/src/integration-tests/profile-system.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/profile-system.integration.test.js +364 -0
- package/dist/src/integration-tests/profile-system.integration.test.js.map +1 -0
- package/dist/src/integration-tests/provider-switching.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/provider-switching.integration.test.js +207 -0
- package/dist/src/integration-tests/provider-switching.integration.test.js.map +1 -0
- package/dist/src/integration-tests/security.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/security.integration.test.js +319 -0
- package/dist/src/integration-tests/security.integration.test.js.map +1 -0
- package/dist/src/integration-tests/test-utils.test.d.ts +6 -0
- package/dist/src/integration-tests/test-utils.test.js +221 -0
- package/dist/src/integration-tests/test-utils.test.js.map +1 -0
- package/dist/src/integration-tests/todo-continuation.integration.test.d.ts +6 -0
- package/dist/src/integration-tests/todo-continuation.integration.test.js +559 -0
- package/dist/src/integration-tests/todo-continuation.integration.test.js.map +1 -0
- package/dist/src/providers/logging/LoggingProviderWrapper.test.d.ts +6 -0
- package/dist/src/providers/logging/LoggingProviderWrapper.test.js +305 -0
- package/dist/src/providers/logging/LoggingProviderWrapper.test.js.map +1 -0
- package/dist/src/providers/logging/git-stats.integration.test.d.ts +6 -0
- package/dist/src/providers/logging/git-stats.integration.test.js +245 -0
- package/dist/src/providers/logging/git-stats.integration.test.js.map +1 -0
- package/dist/src/providers/logging/git-stats.test.d.ts +6 -0
- package/dist/src/providers/logging/git-stats.test.js +432 -0
- package/dist/src/providers/logging/git-stats.test.js.map +1 -0
- package/dist/src/providers/logging/multi-provider-logging.integration.test.d.ts +6 -0
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js +531 -0
- package/dist/src/providers/logging/multi-provider-logging.integration.test.js.map +1 -0
- package/dist/src/providers/logging/performance.test.d.ts +6 -0
- package/dist/src/providers/logging/performance.test.js +465 -0
- package/dist/src/providers/logging/performance.test.js.map +1 -0
- package/dist/src/providers/provider-gemini-switching.test.d.ts +6 -0
- package/dist/src/providers/provider-gemini-switching.test.js +129 -0
- package/dist/src/providers/provider-gemini-switching.test.js.map +1 -0
- package/dist/src/providers/provider-switching.integration.test.d.ts +6 -0
- package/dist/src/providers/provider-switching.integration.test.js +113 -0
- package/dist/src/providers/provider-switching.integration.test.js.map +1 -0
- package/dist/src/providers/providerManagerInstance.test.d.ts +6 -0
- package/dist/src/providers/providerManagerInstance.test.js +104 -0
- package/dist/src/providers/providerManagerInstance.test.js.map +1 -0
- package/dist/src/services/BuiltinCommandLoader.test.d.ts +6 -0
- package/dist/src/services/BuiltinCommandLoader.test.js +118 -0
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -0
- package/dist/src/services/CommandService.test.d.ts +6 -0
- package/dist/src/services/CommandService.test.js +232 -0
- package/dist/src/services/CommandService.test.js.map +1 -0
- package/dist/src/storage/ConversationStorage.test.d.ts +6 -0
- package/dist/src/storage/ConversationStorage.test.js +379 -0
- package/dist/src/storage/ConversationStorage.test.js.map +1 -0
- package/dist/src/test-utils/customMatchers.d.ts +14 -0
- package/dist/src/test-utils/customMatchers.js +46 -0
- package/dist/src/test-utils/customMatchers.js.map +1 -0
- package/dist/src/test-utils/mockCommandContext.d.ts +18 -0
- package/dist/src/test-utils/mockCommandContext.js +86 -0
- package/dist/src/test-utils/mockCommandContext.js.map +1 -0
- package/dist/src/test-utils/mockCommandContext.test.d.ts +6 -0
- package/dist/src/test-utils/mockCommandContext.test.js +51 -0
- package/dist/src/test-utils/mockCommandContext.test.js.map +1 -0
- package/dist/src/test-utils/responsive-testing.d.ts +14 -0
- package/dist/src/test-utils/responsive-testing.js +35 -0
- package/dist/src/test-utils/responsive-testing.js.map +1 -0
- package/dist/src/test-utils/responsive-testing.test.d.ts +6 -0
- package/dist/src/test-utils/responsive-testing.test.js +89 -0
- package/dist/src/test-utils/responsive-testing.test.js.map +1 -0
- package/dist/src/test-utils/testProviderConfig.d.ts +18 -0
- package/dist/src/test-utils/testProviderConfig.js +19 -0
- package/dist/src/test-utils/testProviderConfig.js.map +1 -0
- package/dist/src/ui/App.js +1 -1
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +8 -6
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/diagnosticsCommand.js +0 -1
- package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +5 -5
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/keyCommand.test.js +128 -0
- package/dist/src/ui/commands/keyCommand.test.js.map +1 -0
- package/dist/src/ui/commands/profileCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/profileCommand.test.js +343 -0
- package/dist/src/ui/commands/profileCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/setCommand.test.js +431 -0
- package/dist/src/ui/commands/setCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setupGithubCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js +69 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -0
- package/dist/src/ui/commands/toolformatCommand.test.d.ts +1 -0
- package/dist/src/ui/commands/toolformatCommand.test.js +145 -0
- package/dist/src/ui/commands/toolformatCommand.test.js.map +1 -0
- package/dist/src/ui/components/AuthDialog.js +1 -1
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.test.d.ts +6 -0
- package/dist/src/ui/components/AuthDialog.test.js +252 -0
- package/dist/src/ui/components/AuthDialog.test.js.map +1 -0
- package/dist/src/ui/components/ContextIndicator.ui.test.d.ts +1 -0
- package/dist/src/ui/components/ContextIndicator.ui.test.js +89 -0
- package/dist/src/ui/components/ContextIndicator.ui.test.js.map +1 -0
- package/dist/src/ui/components/ContextSummaryDisplay.js +1 -1
- package/dist/src/ui/components/ContextUsageDisplay.semantic.test.d.ts +6 -0
- package/dist/src/ui/components/ContextUsageDisplay.semantic.test.js +75 -0
- package/dist/src/ui/components/ContextUsageDisplay.semantic.test.js.map +1 -0
- package/dist/src/ui/components/FolderTrustDialog.test.d.ts +6 -0
- package/dist/src/ui/components/FolderTrustDialog.test.js +26 -0
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -0
- package/dist/src/ui/components/Footer.d.ts +1 -0
- package/dist/src/ui/components/Footer.js +2 -2
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Footer.responsive.test.d.ts +6 -0
- package/dist/src/ui/components/Footer.responsive.test.js +262 -0
- package/dist/src/ui/components/Footer.responsive.test.js.map +1 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.js +92 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.paste.test.d.ts +6 -0
- package/dist/src/ui/components/InputPrompt.paste.test.js +263 -0
- package/dist/src/ui/components/InputPrompt.paste.test.js.map +1 -0
- package/dist/src/ui/components/LoadingIndicator.test.d.ts +6 -0
- package/dist/src/ui/components/LoadingIndicator.test.js +149 -0
- package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -0
- package/dist/src/ui/components/MemoryUsageDisplay.semantic.test.d.ts +6 -0
- package/dist/src/ui/components/MemoryUsageDisplay.semantic.test.js +127 -0
- package/dist/src/ui/components/MemoryUsageDisplay.semantic.test.js.map +1 -0
- package/dist/src/ui/components/ProviderDialog.responsive.test.d.ts +6 -0
- package/dist/src/ui/components/ProviderDialog.responsive.test.js +153 -0
- package/dist/src/ui/components/ProviderDialog.responsive.test.js.map +1 -0
- package/dist/src/ui/components/ProviderModelDialog.responsive.test.d.ts +6 -0
- package/dist/src/ui/components/ProviderModelDialog.responsive.test.js +252 -0
- package/dist/src/ui/components/ProviderModelDialog.responsive.test.js.map +1 -0
- package/dist/src/ui/components/ProviderModelDialog.test.d.ts +6 -0
- package/dist/src/ui/components/ProviderModelDialog.test.js +197 -0
- package/dist/src/ui/components/ProviderModelDialog.test.js.map +1 -0
- package/dist/src/ui/components/SettingsDialog.test.d.ts +6 -0
- package/dist/src/ui/components/SettingsDialog.test.js +555 -0
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -0
- package/dist/src/ui/components/ShellConfirmationDialog.test.d.ts +6 -0
- package/dist/src/ui/components/ShellConfirmationDialog.test.js +40 -0
- package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -0
- package/dist/src/ui/components/TodoPanel.responsive.test.d.ts +6 -0
- package/dist/src/ui/components/TodoPanel.responsive.test.js +221 -0
- package/dist/src/ui/components/TodoPanel.responsive.test.js.map +1 -0
- package/dist/src/ui/components/TodoPanel.semantic.test.d.ts +6 -0
- package/dist/src/ui/components/TodoPanel.semantic.test.js +137 -0
- package/dist/src/ui/components/TodoPanel.semantic.test.js.map +1 -0
- package/dist/src/ui/components/__tests__/LayoutManager.test.d.ts +6 -0
- package/dist/src/ui/components/__tests__/LayoutManager.test.js +94 -0
- package/dist/src/ui/components/__tests__/LayoutManager.test.js.map +1 -0
- package/dist/src/ui/components/messages/DiffRenderer.js +9 -7
- package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.test.d.ts +6 -0
- package/dist/src/ui/components/messages/DiffRenderer.test.js +239 -0
- package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +13 -4
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.responsive.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.responsive.test.js +172 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.responsive.test.js.map +1 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +37 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/ToolMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolMessage.test.js +118 -0
- package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -0
- package/dist/src/ui/components/shared/MaxSizedBox.test.d.ts +6 -0
- package/dist/src/ui/components/shared/MaxSizedBox.test.js +154 -0
- package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.js +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.test.d.ts +6 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js +112 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -0
- package/dist/src/ui/containers/SessionController.test.d.ts +6 -0
- package/dist/src/ui/containers/SessionController.test.js +440 -0
- package/dist/src/ui/containers/SessionController.test.js.map +1 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.d.ts +6 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js +830 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.d.ts +6 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +328 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.d.ts +6 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +191 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -0
- package/dist/src/ui/hooks/useEditorSettings.test.d.ts +6 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js +221 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.integration.test.d.ts +6 -0
- package/dist/src/ui/hooks/useGeminiStream.integration.test.js +800 -0
- package/dist/src/ui/hooks/useGeminiStream.integration.test.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +12 -47
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.d.ts +6 -0
- package/dist/src/ui/hooks/useGitBranchName.test.js +170 -0
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -0
- package/dist/src/ui/hooks/useHistoryManager.test.d.ts +6 -0
- package/dist/src/ui/hooks/useHistoryManager.test.js +171 -0
- package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -0
- package/dist/src/ui/hooks/useInputHistory.test.d.ts +6 -0
- package/dist/src/ui/hooks/useInputHistory.test.js +207 -0
- package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -0
- package/dist/src/ui/hooks/useKeypress.test.d.ts +6 -0
- package/dist/src/ui/hooks/useKeypress.test.js +171 -0
- package/dist/src/ui/hooks/useKeypress.test.js.map +1 -0
- package/dist/src/ui/hooks/useResponsive.test.d.ts +6 -0
- package/dist/src/ui/hooks/useResponsive.test.js +124 -0
- package/dist/src/ui/hooks/useResponsive.test.js.map +1 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +163 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useShellHistory.test.d.ts +6 -0
- package/dist/src/ui/hooks/useShellHistory.test.js +162 -0
- package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.js +929 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useStableCallback.test.d.ts +6 -0
- package/dist/src/ui/hooks/useStableCallback.test.js +57 -0
- package/dist/src/ui/hooks/useStableCallback.test.js.map +1 -0
- package/dist/src/ui/keyMatchers.test.d.ts +6 -0
- package/dist/src/ui/keyMatchers.test.js +276 -0
- package/dist/src/ui/keyMatchers.test.js.map +1 -0
- package/dist/src/ui/reducers/appReducer.test.d.ts +6 -0
- package/dist/src/ui/reducers/appReducer.test.js +519 -0
- package/dist/src/ui/reducers/appReducer.test.js.map +1 -0
- package/dist/src/ui/themes/color-utils.test.d.ts +6 -0
- package/dist/src/ui/themes/color-utils.test.js +197 -0
- package/dist/src/ui/themes/color-utils.test.js.map +1 -0
- package/dist/src/ui/themes/semantic-resolver.test.d.ts +6 -0
- package/dist/src/ui/themes/semantic-resolver.test.js +210 -0
- package/dist/src/ui/themes/semantic-resolver.test.js.map +1 -0
- package/dist/src/ui/themes/semantic-tokens.test.d.ts +6 -0
- package/dist/src/ui/themes/semantic-tokens.test.js +272 -0
- package/dist/src/ui/themes/semantic-tokens.test.js.map +1 -0
- package/dist/src/ui/themes/theme-manager.test.d.ts +6 -0
- package/dist/src/ui/themes/theme-manager.test.js +151 -0
- package/dist/src/ui/themes/theme-manager.test.js.map +1 -0
- package/dist/src/ui/utils/MarkdownDisplay.test.d.ts +6 -0
- package/dist/src/ui/utils/MarkdownDisplay.test.js +151 -0
- package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -0
- package/dist/src/ui/utils/clipboardUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/clipboardUtils.test.js +65 -0
- package/dist/src/ui/utils/clipboardUtils.test.js.map +1 -0
- package/dist/src/ui/utils/commandUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/commandUtils.test.js +294 -0
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -0
- package/dist/src/ui/utils/displayUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/displayUtils.test.js +42 -0
- package/dist/src/ui/utils/displayUtils.test.js.map +1 -0
- package/dist/src/ui/utils/formatters.test.d.ts +6 -0
- package/dist/src/ui/utils/formatters.test.js +56 -0
- package/dist/src/ui/utils/formatters.test.js.map +1 -0
- package/dist/src/ui/utils/markdownUtilities.test.d.ts +6 -0
- package/dist/src/ui/utils/markdownUtilities.test.js +42 -0
- package/dist/src/ui/utils/markdownUtilities.test.js.map +1 -0
- package/dist/src/ui/utils/responsive.test.d.ts +6 -0
- package/dist/src/ui/utils/responsive.test.js +107 -0
- package/dist/src/ui/utils/responsive.test.js.map +1 -0
- package/dist/src/ui/utils/secureInputHandler.test.d.ts +6 -0
- package/dist/src/ui/utils/secureInputHandler.test.js +274 -0
- package/dist/src/ui/utils/secureInputHandler.test.js.map +1 -0
- package/dist/src/ui/utils/updateCheck.test.d.ts +6 -0
- package/dist/src/ui/utils/updateCheck.test.js +202 -0
- package/dist/src/ui/utils/updateCheck.test.js.map +1 -0
- package/dist/src/utils/ConversationContext.test.d.ts +6 -0
- package/dist/src/utils/ConversationContext.test.js +64 -0
- package/dist/src/utils/ConversationContext.test.js.map +1 -0
- package/dist/src/utils/gitUtils.test.d.ts +6 -0
- package/dist/src/utils/gitUtils.test.js +113 -0
- package/dist/src/utils/gitUtils.test.js.map +1 -0
- package/dist/src/utils/installationInfo.test.d.ts +6 -0
- package/dist/src/utils/installationInfo.test.js +242 -0
- package/dist/src/utils/installationInfo.test.js.map +1 -0
- package/dist/src/utils/privacy/ConversationDataRedactor.test.d.ts +6 -0
- package/dist/src/utils/privacy/ConversationDataRedactor.test.js +463 -0
- package/dist/src/utils/privacy/ConversationDataRedactor.test.js.map +1 -0
- package/dist/src/utils/readStdin.js +10 -0
- package/dist/src/utils/readStdin.js.map +1 -1
- package/dist/src/utils/sandbox.js +2 -2
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/settingsUtils.test.d.ts +6 -0
- package/dist/src/utils/settingsUtils.test.js +514 -0
- package/dist/src/utils/settingsUtils.test.js.map +1 -0
- package/dist/src/utils/userStartupWarnings.js +2 -2
- package/dist/src/utils/userStartupWarnings.test.d.ts +6 -0
- package/dist/src/utils/userStartupWarnings.test.js +67 -0
- package/dist/src/utils/userStartupWarnings.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
@@ -0,0 +1,830 @@
|
|
1
|
+
/**
|
2
|
+
* @license
|
3
|
+
* Copyright 2025 Google LLC
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
5
|
+
*/
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
7
|
+
import { handleAtCommand } from './atCommandProcessor.js';
|
8
|
+
import { FileDiscoveryService, GlobTool, ReadManyFilesTool, ToolRegistry, } from '@vybestack/llxprt-code-core';
|
9
|
+
import * as os from 'os';
|
10
|
+
import { ToolCallStatus } from '../types.js';
|
11
|
+
import * as fsPromises from 'fs/promises';
|
12
|
+
import * as path from 'path';
|
13
|
+
// No mocking - use the real FileDiscoveryService
|
14
|
+
describe('handleAtCommand', () => {
|
15
|
+
let testRootDir;
|
16
|
+
let mockConfig;
|
17
|
+
const mockAddItem = vi.fn();
|
18
|
+
const mockOnDebugMessage = vi.fn();
|
19
|
+
let abortController;
|
20
|
+
async function createTestFile(fullPath, fileContents) {
|
21
|
+
await fsPromises.mkdir(path.dirname(fullPath), { recursive: true });
|
22
|
+
await fsPromises.writeFile(fullPath, fileContents);
|
23
|
+
return path.resolve(testRootDir, fullPath);
|
24
|
+
}
|
25
|
+
beforeEach(async () => {
|
26
|
+
vi.resetAllMocks();
|
27
|
+
testRootDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'folder-structure-test-'));
|
28
|
+
abortController = new AbortController();
|
29
|
+
const getToolRegistry = vi.fn();
|
30
|
+
mockConfig = {
|
31
|
+
getToolRegistry,
|
32
|
+
getTargetDir: () => testRootDir,
|
33
|
+
isSandboxed: () => false,
|
34
|
+
getFileService: () => new FileDiscoveryService(testRootDir),
|
35
|
+
getFileFilteringRespectGitIgnore: () => true,
|
36
|
+
getFileFilteringRespectLlxprtIgnore: () => true,
|
37
|
+
getFileFilteringOptions: () => ({
|
38
|
+
respectGitIgnore: true,
|
39
|
+
respectLlxprtIgnore: true,
|
40
|
+
}),
|
41
|
+
getEnableRecursiveFileSearch: vi.fn(() => true),
|
42
|
+
getWorkspaceContext: () => ({
|
43
|
+
isPathWithinWorkspace: () => true,
|
44
|
+
getDirectories: () => [testRootDir],
|
45
|
+
}),
|
46
|
+
getEphemeralSettings: () => ({}), // No disabled tools
|
47
|
+
};
|
48
|
+
const registry = new ToolRegistry(mockConfig);
|
49
|
+
registry.registerTool(new ReadManyFilesTool(mockConfig));
|
50
|
+
registry.registerTool(new GlobTool(mockConfig));
|
51
|
+
getToolRegistry.mockResolvedValue(registry);
|
52
|
+
});
|
53
|
+
afterEach(async () => {
|
54
|
+
abortController.abort();
|
55
|
+
await fsPromises.rm(testRootDir, { recursive: true, force: true });
|
56
|
+
});
|
57
|
+
it('should pass through query if no @ command is present', async () => {
|
58
|
+
const query = 'regular user query';
|
59
|
+
const result = await handleAtCommand({
|
60
|
+
query,
|
61
|
+
config: mockConfig,
|
62
|
+
addItem: mockAddItem,
|
63
|
+
onDebugMessage: mockOnDebugMessage,
|
64
|
+
messageId: 123,
|
65
|
+
signal: abortController.signal,
|
66
|
+
});
|
67
|
+
expect(result).toEqual({
|
68
|
+
processedQuery: [{ text: query }],
|
69
|
+
shouldProceed: true,
|
70
|
+
});
|
71
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: query }, 123);
|
72
|
+
});
|
73
|
+
it('should pass through original query if only a lone @ symbol is present', async () => {
|
74
|
+
const queryWithSpaces = ' @ ';
|
75
|
+
const result = await handleAtCommand({
|
76
|
+
query: queryWithSpaces,
|
77
|
+
config: mockConfig,
|
78
|
+
addItem: mockAddItem,
|
79
|
+
onDebugMessage: mockOnDebugMessage,
|
80
|
+
messageId: 124,
|
81
|
+
signal: abortController.signal,
|
82
|
+
});
|
83
|
+
expect(result).toEqual({
|
84
|
+
processedQuery: [{ text: queryWithSpaces }],
|
85
|
+
shouldProceed: true,
|
86
|
+
});
|
87
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: queryWithSpaces }, 124);
|
88
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith('Lone @ detected, will be treated as text in the modified query.');
|
89
|
+
});
|
90
|
+
it('tool registry should be properly configured', async () => {
|
91
|
+
const registry = await mockConfig.getToolRegistry();
|
92
|
+
expect(registry).toBeDefined();
|
93
|
+
expect(registry.getTool('read_many_files')).toBeDefined();
|
94
|
+
expect(registry.getTool('glob')).toBeDefined();
|
95
|
+
});
|
96
|
+
it('should process a valid text file path', async () => {
|
97
|
+
const fileContent = 'This is the file content.';
|
98
|
+
// Create file in the test directory
|
99
|
+
const relativePath = path.join('path', 'to', 'file.txt');
|
100
|
+
const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
|
101
|
+
// Use relative path in the query
|
102
|
+
const query = `@${relativePath}`;
|
103
|
+
const result = await handleAtCommand({
|
104
|
+
query,
|
105
|
+
config: mockConfig,
|
106
|
+
addItem: mockAddItem,
|
107
|
+
onDebugMessage: mockOnDebugMessage,
|
108
|
+
messageId: 125,
|
109
|
+
signal: abortController.signal,
|
110
|
+
});
|
111
|
+
expect(result).toEqual({
|
112
|
+
processedQuery: [
|
113
|
+
{ text: `@${relativePath}` },
|
114
|
+
{ text: '\n--- Content from referenced files ---' },
|
115
|
+
{ text: `\nContent from @${filePath}:\n` },
|
116
|
+
{ text: fileContent },
|
117
|
+
{ text: '\n--- End of content ---' },
|
118
|
+
],
|
119
|
+
shouldProceed: true,
|
120
|
+
});
|
121
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: query }, 125);
|
122
|
+
expect(mockAddItem).toHaveBeenCalledWith(expect.objectContaining({
|
123
|
+
type: 'tool_group',
|
124
|
+
tools: [expect.objectContaining({ status: ToolCallStatus.Success })],
|
125
|
+
}), 125);
|
126
|
+
});
|
127
|
+
it('should process a valid directory path and convert to glob', async () => {
|
128
|
+
const fileContent = 'This is the file content.';
|
129
|
+
const relativeDirPath = path.join('path', 'to');
|
130
|
+
const relativeFilePath = path.join(relativeDirPath, 'file.txt');
|
131
|
+
const filePath = await createTestFile(path.join(testRootDir, relativeFilePath), fileContent);
|
132
|
+
const query = `@${relativeDirPath}`;
|
133
|
+
const resolvedGlob = `${relativeDirPath}/**`;
|
134
|
+
const result = await handleAtCommand({
|
135
|
+
query,
|
136
|
+
config: mockConfig,
|
137
|
+
addItem: mockAddItem,
|
138
|
+
onDebugMessage: mockOnDebugMessage,
|
139
|
+
messageId: 126,
|
140
|
+
signal: abortController.signal,
|
141
|
+
});
|
142
|
+
expect(result).toEqual({
|
143
|
+
processedQuery: [
|
144
|
+
{ text: `@${resolvedGlob}` },
|
145
|
+
{ text: '\n--- Content from referenced files ---' },
|
146
|
+
{ text: `\nContent from @${filePath}:\n` },
|
147
|
+
{ text: fileContent },
|
148
|
+
{ text: '\n--- End of content ---' },
|
149
|
+
],
|
150
|
+
shouldProceed: true,
|
151
|
+
});
|
152
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: query }, 126);
|
153
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${relativeDirPath} resolved to directory, using glob: ${resolvedGlob}`);
|
154
|
+
});
|
155
|
+
it('should handle query with text before and after @command', async () => {
|
156
|
+
const fileContent = 'Markdown content.';
|
157
|
+
const relativePath = 'doc.md';
|
158
|
+
const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
|
159
|
+
const textBefore = 'Explain this: ';
|
160
|
+
const textAfter = ' in detail.';
|
161
|
+
const query = `${textBefore}@${relativePath}${textAfter}`;
|
162
|
+
const result = await handleAtCommand({
|
163
|
+
query,
|
164
|
+
config: mockConfig,
|
165
|
+
addItem: mockAddItem,
|
166
|
+
onDebugMessage: mockOnDebugMessage,
|
167
|
+
messageId: 128,
|
168
|
+
signal: abortController.signal,
|
169
|
+
});
|
170
|
+
expect(result).toEqual({
|
171
|
+
processedQuery: [
|
172
|
+
{ text: `${textBefore}@${relativePath}${textAfter}` },
|
173
|
+
{ text: '\n--- Content from referenced files ---' },
|
174
|
+
{ text: `\nContent from @${filePath}:\n` },
|
175
|
+
{ text: fileContent },
|
176
|
+
{ text: '\n--- End of content ---' },
|
177
|
+
],
|
178
|
+
shouldProceed: true,
|
179
|
+
});
|
180
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: query }, 128);
|
181
|
+
});
|
182
|
+
it('should correctly unescape paths with escaped spaces', async () => {
|
183
|
+
const fileContent = 'This is the file content.';
|
184
|
+
const relativePath = path.join('path', 'to', 'my file.txt');
|
185
|
+
const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
|
186
|
+
const escapedPath = path.join('path', 'to', 'my\\ file.txt');
|
187
|
+
const query = `@${escapedPath}`;
|
188
|
+
const result = await handleAtCommand({
|
189
|
+
query,
|
190
|
+
config: mockConfig,
|
191
|
+
addItem: mockAddItem,
|
192
|
+
onDebugMessage: mockOnDebugMessage,
|
193
|
+
messageId: 125,
|
194
|
+
signal: abortController.signal,
|
195
|
+
});
|
196
|
+
expect(result).toEqual({
|
197
|
+
processedQuery: [
|
198
|
+
{ text: `@${relativePath}` },
|
199
|
+
{ text: '\n--- Content from referenced files ---' },
|
200
|
+
{ text: `\nContent from @${filePath}:\n` },
|
201
|
+
{ text: fileContent },
|
202
|
+
{ text: '\n--- End of content ---' },
|
203
|
+
],
|
204
|
+
shouldProceed: true,
|
205
|
+
});
|
206
|
+
expect(mockAddItem).toHaveBeenCalledWith({ type: 'user', text: query }, 125);
|
207
|
+
expect(mockAddItem).toHaveBeenCalledWith(expect.objectContaining({
|
208
|
+
type: 'tool_group',
|
209
|
+
tools: [expect.objectContaining({ status: ToolCallStatus.Success })],
|
210
|
+
}), 125);
|
211
|
+
});
|
212
|
+
it('should handle multiple @file references', async () => {
|
213
|
+
const content1 = 'Content file1';
|
214
|
+
const relativePath1 = 'file1.txt';
|
215
|
+
const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
|
216
|
+
const content2 = 'Content file2';
|
217
|
+
const relativePath2 = 'file2.md';
|
218
|
+
const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
|
219
|
+
const query = `@${relativePath1} @${relativePath2}`;
|
220
|
+
const result = await handleAtCommand({
|
221
|
+
query,
|
222
|
+
config: mockConfig,
|
223
|
+
addItem: mockAddItem,
|
224
|
+
onDebugMessage: mockOnDebugMessage,
|
225
|
+
messageId: 130,
|
226
|
+
signal: abortController.signal,
|
227
|
+
});
|
228
|
+
expect(result).toEqual({
|
229
|
+
processedQuery: [
|
230
|
+
{ text: query },
|
231
|
+
{ text: '\n--- Content from referenced files ---' },
|
232
|
+
{ text: `\nContent from @${file1Path}:\n` },
|
233
|
+
{ text: content1 },
|
234
|
+
{ text: `\nContent from @${file2Path}:\n` },
|
235
|
+
{ text: content2 },
|
236
|
+
{ text: '\n--- End of content ---' },
|
237
|
+
],
|
238
|
+
shouldProceed: true,
|
239
|
+
});
|
240
|
+
});
|
241
|
+
it('should handle multiple @file references with interleaved text', async () => {
|
242
|
+
const text1 = 'Check ';
|
243
|
+
const content1 = 'C1';
|
244
|
+
const relativePath1 = 'f1.txt';
|
245
|
+
const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
|
246
|
+
const text2 = ' and ';
|
247
|
+
const content2 = 'C2';
|
248
|
+
const relativePath2 = 'f2.md';
|
249
|
+
const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
|
250
|
+
const text3 = ' please.';
|
251
|
+
const query = `${text1}@${relativePath1}${text2}@${relativePath2}${text3}`;
|
252
|
+
const result = await handleAtCommand({
|
253
|
+
query,
|
254
|
+
config: mockConfig,
|
255
|
+
addItem: mockAddItem,
|
256
|
+
onDebugMessage: mockOnDebugMessage,
|
257
|
+
messageId: 131,
|
258
|
+
signal: abortController.signal,
|
259
|
+
});
|
260
|
+
expect(result).toEqual({
|
261
|
+
processedQuery: [
|
262
|
+
{ text: query },
|
263
|
+
{ text: '\n--- Content from referenced files ---' },
|
264
|
+
{ text: `\nContent from @${file1Path}:\n` },
|
265
|
+
{ text: content1 },
|
266
|
+
{ text: `\nContent from @${file2Path}:\n` },
|
267
|
+
{ text: content2 },
|
268
|
+
{ text: '\n--- End of content ---' },
|
269
|
+
],
|
270
|
+
shouldProceed: true,
|
271
|
+
});
|
272
|
+
});
|
273
|
+
it('should handle a mix of valid, invalid, and lone @ references', async () => {
|
274
|
+
const content1 = 'Valid content 1';
|
275
|
+
const relativePath1 = 'valid1.txt';
|
276
|
+
const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
|
277
|
+
const invalidFile = 'nonexistent.txt';
|
278
|
+
const content2 = 'Globbed content';
|
279
|
+
const relativePath2 = path.join('resolved', 'valid2.actual');
|
280
|
+
const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
|
281
|
+
const query = `Look at @${relativePath1} then @${invalidFile} and also just @ symbol, then @${relativePath2}`;
|
282
|
+
const result = await handleAtCommand({
|
283
|
+
query,
|
284
|
+
config: mockConfig,
|
285
|
+
addItem: mockAddItem,
|
286
|
+
onDebugMessage: mockOnDebugMessage,
|
287
|
+
messageId: 132,
|
288
|
+
signal: abortController.signal,
|
289
|
+
});
|
290
|
+
expect(result.shouldProceed).toBe(true);
|
291
|
+
expect(result.processedQuery).toBeDefined();
|
292
|
+
const processedQuery = result.processedQuery;
|
293
|
+
expect(processedQuery[0]).toEqual({
|
294
|
+
text: `Look at @${relativePath1} then @${invalidFile} and also just @ symbol, then @${relativePath2}`,
|
295
|
+
});
|
296
|
+
// Check that both files are included but don't enforce order
|
297
|
+
const queryText = (Array.isArray(processedQuery) ? processedQuery : [processedQuery])
|
298
|
+
.map((p) => p.text)
|
299
|
+
.join('');
|
300
|
+
expect(queryText).toContain('--- Content from referenced files ---');
|
301
|
+
expect(queryText).toContain(`Content from @${file1Path}:`);
|
302
|
+
expect(queryText).toContain(content1);
|
303
|
+
expect(queryText).toContain(`Content from @${file2Path}:`);
|
304
|
+
expect(queryText).toContain(content2);
|
305
|
+
expect(queryText).toContain('--- End of content ---');
|
306
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${invalidFile} not found directly, attempting glob search.`);
|
307
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Glob search for '**/*${invalidFile}*' found no files or an error. Path ${invalidFile} will be skipped.`);
|
308
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith('Lone @ detected, will be treated as text in the modified query.');
|
309
|
+
});
|
310
|
+
it('should return original query if all @paths are invalid or lone @', async () => {
|
311
|
+
const query = 'Check @nonexistent.txt and @ also';
|
312
|
+
const result = await handleAtCommand({
|
313
|
+
query,
|
314
|
+
config: mockConfig,
|
315
|
+
addItem: mockAddItem,
|
316
|
+
onDebugMessage: mockOnDebugMessage,
|
317
|
+
messageId: 133,
|
318
|
+
signal: abortController.signal,
|
319
|
+
});
|
320
|
+
expect(result).toEqual({
|
321
|
+
processedQuery: [{ text: 'Check @nonexistent.txt and @ also' }],
|
322
|
+
shouldProceed: true,
|
323
|
+
});
|
324
|
+
});
|
325
|
+
describe('git-aware filtering', () => {
|
326
|
+
beforeEach(async () => {
|
327
|
+
await fsPromises.mkdir(path.join(testRootDir, '.git'), {
|
328
|
+
recursive: true,
|
329
|
+
});
|
330
|
+
});
|
331
|
+
it('should skip git-ignored files in @ commands', async () => {
|
332
|
+
await createTestFile(path.join(testRootDir, '.gitignore'), 'node_modules/package.json');
|
333
|
+
const gitIgnoredFile = await createTestFile(path.join(testRootDir, 'node_modules', 'package.json'), 'the file contents');
|
334
|
+
const query = `@${gitIgnoredFile}`;
|
335
|
+
const result = await handleAtCommand({
|
336
|
+
query,
|
337
|
+
config: mockConfig,
|
338
|
+
addItem: mockAddItem,
|
339
|
+
onDebugMessage: mockOnDebugMessage,
|
340
|
+
messageId: 200,
|
341
|
+
signal: abortController.signal,
|
342
|
+
});
|
343
|
+
expect(result).toEqual({
|
344
|
+
processedQuery: [{ text: query }],
|
345
|
+
shouldProceed: true,
|
346
|
+
});
|
347
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${gitIgnoredFile} is git-ignored and will be skipped.`);
|
348
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${gitIgnoredFile}`);
|
349
|
+
});
|
350
|
+
it('should process non-git-ignored files normally', async () => {
|
351
|
+
await createTestFile(path.join(testRootDir, '.gitignore'), 'node_modules/package.json');
|
352
|
+
const relativePath = path.join('src', 'index.ts');
|
353
|
+
const validFile = await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
|
354
|
+
const query = `@${relativePath}`;
|
355
|
+
const result = await handleAtCommand({
|
356
|
+
query,
|
357
|
+
config: mockConfig,
|
358
|
+
addItem: mockAddItem,
|
359
|
+
onDebugMessage: mockOnDebugMessage,
|
360
|
+
messageId: 201,
|
361
|
+
signal: abortController.signal,
|
362
|
+
});
|
363
|
+
expect(result).toEqual({
|
364
|
+
processedQuery: [
|
365
|
+
{ text: `@${relativePath}` },
|
366
|
+
{ text: '\n--- Content from referenced files ---' },
|
367
|
+
{ text: `\nContent from @${validFile}:\n` },
|
368
|
+
{ text: 'console.log("Hello world");' },
|
369
|
+
{ text: '\n--- End of content ---' },
|
370
|
+
],
|
371
|
+
shouldProceed: true,
|
372
|
+
});
|
373
|
+
});
|
374
|
+
it('should handle mixed git-ignored and valid files', async () => {
|
375
|
+
await createTestFile(path.join(testRootDir, '.gitignore'), '.env');
|
376
|
+
const relativePath1 = 'README.md';
|
377
|
+
const validFile = await createTestFile(path.join(testRootDir, relativePath1), '# Project README');
|
378
|
+
const relativePath2 = '.env';
|
379
|
+
await createTestFile(path.join(testRootDir, relativePath2), 'SECRET=123');
|
380
|
+
const query = `@${relativePath1} @${relativePath2}`;
|
381
|
+
const result = await handleAtCommand({
|
382
|
+
query,
|
383
|
+
config: mockConfig,
|
384
|
+
addItem: mockAddItem,
|
385
|
+
onDebugMessage: mockOnDebugMessage,
|
386
|
+
messageId: 202,
|
387
|
+
signal: abortController.signal,
|
388
|
+
});
|
389
|
+
expect(result).toEqual({
|
390
|
+
processedQuery: [
|
391
|
+
{ text: `@${relativePath1} @${relativePath2}` },
|
392
|
+
{ text: '\n--- Content from referenced files ---' },
|
393
|
+
{ text: `\nContent from @${validFile}:\n` },
|
394
|
+
{ text: '# Project README' },
|
395
|
+
{ text: '\n--- End of content ---' },
|
396
|
+
],
|
397
|
+
shouldProceed: true,
|
398
|
+
});
|
399
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${relativePath2} is git-ignored and will be skipped.`);
|
400
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${relativePath2}`);
|
401
|
+
});
|
402
|
+
it('should always ignore .git directory files', async () => {
|
403
|
+
const gitFile = await createTestFile(path.join(testRootDir, '.git', 'config'), '[core]\n\trepositoryformatversion = 0\n');
|
404
|
+
const query = `@${gitFile}`;
|
405
|
+
const result = await handleAtCommand({
|
406
|
+
query,
|
407
|
+
config: mockConfig,
|
408
|
+
addItem: mockAddItem,
|
409
|
+
onDebugMessage: mockOnDebugMessage,
|
410
|
+
messageId: 203,
|
411
|
+
signal: abortController.signal,
|
412
|
+
});
|
413
|
+
expect(result).toEqual({
|
414
|
+
processedQuery: [{ text: query }],
|
415
|
+
shouldProceed: true,
|
416
|
+
});
|
417
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${gitFile} is git-ignored and will be skipped.`);
|
418
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${gitFile}`);
|
419
|
+
});
|
420
|
+
});
|
421
|
+
describe('when recursive file search is disabled', () => {
|
422
|
+
beforeEach(() => {
|
423
|
+
vi.mocked(mockConfig.getEnableRecursiveFileSearch).mockReturnValue(false);
|
424
|
+
});
|
425
|
+
it('should not use glob search for a nonexistent file', async () => {
|
426
|
+
const invalidFile = 'nonexistent.txt';
|
427
|
+
const query = `@${invalidFile}`;
|
428
|
+
const result = await handleAtCommand({
|
429
|
+
query,
|
430
|
+
config: mockConfig,
|
431
|
+
addItem: mockAddItem,
|
432
|
+
onDebugMessage: mockOnDebugMessage,
|
433
|
+
messageId: 300,
|
434
|
+
signal: abortController.signal,
|
435
|
+
});
|
436
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Glob tool not found. Path ${invalidFile} will be skipped.`);
|
437
|
+
expect(result.processedQuery).toEqual([{ text: query }]);
|
438
|
+
expect(result.shouldProceed).toBe(true);
|
439
|
+
});
|
440
|
+
});
|
441
|
+
describe('llxprt-ignore filtering', () => {
|
442
|
+
it('should skip llxprt-ignored files in @ commands', async () => {
|
443
|
+
await createTestFile(path.join(testRootDir, '.llxprtignore'), 'build/output.js');
|
444
|
+
const llxprtIgnoredFile = await createTestFile(path.join(testRootDir, 'build', 'output.js'), 'console.log("Hello");');
|
445
|
+
const query = `@${llxprtIgnoredFile}`;
|
446
|
+
const result = await handleAtCommand({
|
447
|
+
query,
|
448
|
+
config: mockConfig,
|
449
|
+
addItem: mockAddItem,
|
450
|
+
onDebugMessage: mockOnDebugMessage,
|
451
|
+
messageId: 204,
|
452
|
+
signal: abortController.signal,
|
453
|
+
});
|
454
|
+
expect(result).toEqual({
|
455
|
+
processedQuery: [{ text: query }],
|
456
|
+
shouldProceed: true,
|
457
|
+
});
|
458
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${llxprtIgnoredFile} is llxprt-ignored and will be skipped.`);
|
459
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nLlxprt-ignored: ${llxprtIgnoredFile}`);
|
460
|
+
});
|
461
|
+
});
|
462
|
+
it('should process non-ignored files when .geminiignore is present', async () => {
|
463
|
+
await createTestFile(path.join(testRootDir, '.llxprtignore'), 'build/output.js');
|
464
|
+
const relativePath = path.join('src', 'index.ts');
|
465
|
+
const validFile = await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
|
466
|
+
const query = `@${relativePath}`;
|
467
|
+
const result = await handleAtCommand({
|
468
|
+
query,
|
469
|
+
config: mockConfig,
|
470
|
+
addItem: mockAddItem,
|
471
|
+
onDebugMessage: mockOnDebugMessage,
|
472
|
+
messageId: 205,
|
473
|
+
signal: abortController.signal,
|
474
|
+
});
|
475
|
+
expect(result).toEqual({
|
476
|
+
processedQuery: [
|
477
|
+
{ text: `@${relativePath}` },
|
478
|
+
{ text: '\n--- Content from referenced files ---' },
|
479
|
+
{ text: `\nContent from @${validFile}:\n` },
|
480
|
+
{ text: 'console.log("Hello world");' },
|
481
|
+
{ text: '\n--- End of content ---' },
|
482
|
+
],
|
483
|
+
shouldProceed: true,
|
484
|
+
});
|
485
|
+
});
|
486
|
+
it('should handle mixed llxprt-ignored and valid files', async () => {
|
487
|
+
await createTestFile(path.join(testRootDir, '.llxprtignore'), 'dist/bundle.js');
|
488
|
+
const relativePath1 = path.join('src', 'main.ts');
|
489
|
+
const validFile = await createTestFile(path.join(testRootDir, relativePath1), '// Main application entry');
|
490
|
+
const relativePath2 = path.join('dist', 'bundle.js');
|
491
|
+
await createTestFile(path.join(testRootDir, relativePath2), 'console.log("bundle");');
|
492
|
+
const query = `@${relativePath1} @${relativePath2}`;
|
493
|
+
const result = await handleAtCommand({
|
494
|
+
query,
|
495
|
+
config: mockConfig,
|
496
|
+
addItem: mockAddItem,
|
497
|
+
onDebugMessage: mockOnDebugMessage,
|
498
|
+
messageId: 206,
|
499
|
+
signal: abortController.signal,
|
500
|
+
});
|
501
|
+
expect(result).toEqual({
|
502
|
+
processedQuery: [
|
503
|
+
{ text: `@${relativePath1} @${relativePath2}` },
|
504
|
+
{ text: '\n--- Content from referenced files ---' },
|
505
|
+
{ text: `\nContent from @${validFile}:\n` },
|
506
|
+
{ text: '// Main application entry' },
|
507
|
+
{ text: '\n--- End of content ---' },
|
508
|
+
],
|
509
|
+
shouldProceed: true,
|
510
|
+
});
|
511
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${relativePath2} is llxprt-ignored and will be skipped.`);
|
512
|
+
expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nLlxprt-ignored: ${relativePath2}`);
|
513
|
+
});
|
514
|
+
describe('punctuation termination in @ commands', () => {
|
515
|
+
const punctuationTestCases = [
|
516
|
+
{
|
517
|
+
name: 'comma',
|
518
|
+
fileName: 'test.txt',
|
519
|
+
fileContent: 'File content here',
|
520
|
+
queryTemplate: (filePath) => `Look at @${filePath}, then explain it.`,
|
521
|
+
messageId: 400,
|
522
|
+
},
|
523
|
+
{
|
524
|
+
name: 'period',
|
525
|
+
fileName: 'readme.md',
|
526
|
+
fileContent: 'File content here',
|
527
|
+
queryTemplate: (filePath) => `Check @${filePath}. What does it say?`,
|
528
|
+
messageId: 401,
|
529
|
+
},
|
530
|
+
{
|
531
|
+
name: 'semicolon',
|
532
|
+
fileName: 'example.js',
|
533
|
+
fileContent: 'Code example',
|
534
|
+
queryTemplate: (filePath) => `Review @${filePath}; check for bugs.`,
|
535
|
+
messageId: 402,
|
536
|
+
},
|
537
|
+
{
|
538
|
+
name: 'exclamation mark',
|
539
|
+
fileName: 'important.txt',
|
540
|
+
fileContent: 'Important content',
|
541
|
+
queryTemplate: (filePath) => `Look at @${filePath}! This is critical.`,
|
542
|
+
messageId: 403,
|
543
|
+
},
|
544
|
+
{
|
545
|
+
name: 'question mark',
|
546
|
+
fileName: 'config.json',
|
547
|
+
fileContent: 'Config settings',
|
548
|
+
queryTemplate: (filePath) => `What is in @${filePath}? Please explain.`,
|
549
|
+
messageId: 404,
|
550
|
+
},
|
551
|
+
{
|
552
|
+
name: 'opening parenthesis',
|
553
|
+
fileName: 'func.ts',
|
554
|
+
fileContent: 'Function definition',
|
555
|
+
queryTemplate: (filePath) => `Analyze @${filePath}(the main function).`,
|
556
|
+
messageId: 405,
|
557
|
+
},
|
558
|
+
{
|
559
|
+
name: 'closing parenthesis',
|
560
|
+
fileName: 'data.json',
|
561
|
+
fileContent: 'Test data',
|
562
|
+
queryTemplate: (filePath) => `Use data from @${filePath}) for testing.`,
|
563
|
+
messageId: 406,
|
564
|
+
},
|
565
|
+
{
|
566
|
+
name: 'opening square bracket',
|
567
|
+
fileName: 'array.js',
|
568
|
+
fileContent: 'Array data',
|
569
|
+
queryTemplate: (filePath) => `Check @${filePath}[0] for the first element.`,
|
570
|
+
messageId: 407,
|
571
|
+
},
|
572
|
+
{
|
573
|
+
name: 'closing square bracket',
|
574
|
+
fileName: 'list.md',
|
575
|
+
fileContent: 'List content',
|
576
|
+
queryTemplate: (filePath) => `Review item @${filePath}] from the list.`,
|
577
|
+
messageId: 408,
|
578
|
+
},
|
579
|
+
{
|
580
|
+
name: 'opening curly brace',
|
581
|
+
fileName: 'object.ts',
|
582
|
+
fileContent: 'Object definition',
|
583
|
+
queryTemplate: (filePath) => `Parse @${filePath}{prop1: value1}.`,
|
584
|
+
messageId: 409,
|
585
|
+
},
|
586
|
+
{
|
587
|
+
name: 'closing curly brace',
|
588
|
+
fileName: 'config.yaml',
|
589
|
+
fileContent: 'Configuration',
|
590
|
+
queryTemplate: (filePath) => `Use settings from @${filePath}} for deployment.`,
|
591
|
+
messageId: 410,
|
592
|
+
},
|
593
|
+
];
|
594
|
+
it.each(punctuationTestCases)('should terminate @path at $name', async ({ fileName, fileContent, queryTemplate, messageId }) => {
|
595
|
+
const filePath = await createTestFile(path.join(testRootDir, fileName), fileContent);
|
596
|
+
const query = queryTemplate(filePath);
|
597
|
+
const result = await handleAtCommand({
|
598
|
+
query,
|
599
|
+
config: mockConfig,
|
600
|
+
addItem: mockAddItem,
|
601
|
+
onDebugMessage: mockOnDebugMessage,
|
602
|
+
messageId,
|
603
|
+
signal: abortController.signal,
|
604
|
+
});
|
605
|
+
expect(result).toEqual({
|
606
|
+
processedQuery: [
|
607
|
+
{ text: query },
|
608
|
+
{ text: '\n--- Content from referenced files ---' },
|
609
|
+
{ text: `\nContent from @${filePath}:\n` },
|
610
|
+
{ text: fileContent },
|
611
|
+
{ text: '\n--- End of content ---' },
|
612
|
+
],
|
613
|
+
shouldProceed: true,
|
614
|
+
});
|
615
|
+
});
|
616
|
+
it('should handle multiple @paths terminated by different punctuation', async () => {
|
617
|
+
const content1 = 'First file';
|
618
|
+
const file1Path = await createTestFile(path.join(testRootDir, 'first.txt'), content1);
|
619
|
+
const content2 = 'Second file';
|
620
|
+
const file2Path = await createTestFile(path.join(testRootDir, 'second.txt'), content2);
|
621
|
+
const query = `Compare @${file1Path}, @${file2Path}; what's different?`;
|
622
|
+
const result = await handleAtCommand({
|
623
|
+
query,
|
624
|
+
config: mockConfig,
|
625
|
+
addItem: mockAddItem,
|
626
|
+
onDebugMessage: mockOnDebugMessage,
|
627
|
+
messageId: 411,
|
628
|
+
signal: abortController.signal,
|
629
|
+
});
|
630
|
+
expect(result).toEqual({
|
631
|
+
processedQuery: [
|
632
|
+
{ text: `Compare @${file1Path}, @${file2Path}; what's different?` },
|
633
|
+
{ text: '\n--- Content from referenced files ---' },
|
634
|
+
{ text: `\nContent from @${file1Path}:\n` },
|
635
|
+
{ text: content1 },
|
636
|
+
{ text: `\nContent from @${file2Path}:\n` },
|
637
|
+
{ text: content2 },
|
638
|
+
{ text: '\n--- End of content ---' },
|
639
|
+
],
|
640
|
+
shouldProceed: true,
|
641
|
+
});
|
642
|
+
});
|
643
|
+
it('should still handle escaped spaces in paths before punctuation', async () => {
|
644
|
+
const fileContent = 'Spaced file content';
|
645
|
+
const filePath = await createTestFile(path.join(testRootDir, 'spaced file.txt'), fileContent);
|
646
|
+
const escapedPath = path.join(testRootDir, 'spaced\\ file.txt');
|
647
|
+
const query = `Check @${escapedPath}, it has spaces.`;
|
648
|
+
const result = await handleAtCommand({
|
649
|
+
query,
|
650
|
+
config: mockConfig,
|
651
|
+
addItem: mockAddItem,
|
652
|
+
onDebugMessage: mockOnDebugMessage,
|
653
|
+
messageId: 412,
|
654
|
+
signal: abortController.signal,
|
655
|
+
});
|
656
|
+
expect(result).toEqual({
|
657
|
+
processedQuery: [
|
658
|
+
{ text: `Check @${filePath}, it has spaces.` },
|
659
|
+
{ text: '\n--- Content from referenced files ---' },
|
660
|
+
{ text: `\nContent from @${filePath}:\n` },
|
661
|
+
{ text: fileContent },
|
662
|
+
{ text: '\n--- End of content ---' },
|
663
|
+
],
|
664
|
+
shouldProceed: true,
|
665
|
+
});
|
666
|
+
});
|
667
|
+
it('should not break file paths with periods in extensions', async () => {
|
668
|
+
const fileContent = 'TypeScript content';
|
669
|
+
const filePath = await createTestFile(path.join(testRootDir, 'example.d.ts'), fileContent);
|
670
|
+
const query = `Analyze @${filePath} for type definitions.`;
|
671
|
+
const result = await handleAtCommand({
|
672
|
+
query,
|
673
|
+
config: mockConfig,
|
674
|
+
addItem: mockAddItem,
|
675
|
+
onDebugMessage: mockOnDebugMessage,
|
676
|
+
messageId: 413,
|
677
|
+
signal: abortController.signal,
|
678
|
+
});
|
679
|
+
expect(result).toEqual({
|
680
|
+
processedQuery: [
|
681
|
+
{ text: `Analyze @${filePath} for type definitions.` },
|
682
|
+
{ text: '\n--- Content from referenced files ---' },
|
683
|
+
{ text: `\nContent from @${filePath}:\n` },
|
684
|
+
{ text: fileContent },
|
685
|
+
{ text: '\n--- End of content ---' },
|
686
|
+
],
|
687
|
+
shouldProceed: true,
|
688
|
+
});
|
689
|
+
});
|
690
|
+
it('should handle file paths ending with period followed by space', async () => {
|
691
|
+
const fileContent = 'Config content';
|
692
|
+
const filePath = await createTestFile(path.join(testRootDir, 'config.json'), fileContent);
|
693
|
+
const query = `Check @${filePath}. This file contains settings.`;
|
694
|
+
const result = await handleAtCommand({
|
695
|
+
query,
|
696
|
+
config: mockConfig,
|
697
|
+
addItem: mockAddItem,
|
698
|
+
onDebugMessage: mockOnDebugMessage,
|
699
|
+
messageId: 414,
|
700
|
+
signal: abortController.signal,
|
701
|
+
});
|
702
|
+
expect(result).toEqual({
|
703
|
+
processedQuery: [
|
704
|
+
{ text: `Check @${filePath}. This file contains settings.` },
|
705
|
+
{ text: '\n--- Content from referenced files ---' },
|
706
|
+
{ text: `\nContent from @${filePath}:\n` },
|
707
|
+
{ text: fileContent },
|
708
|
+
{ text: '\n--- End of content ---' },
|
709
|
+
],
|
710
|
+
shouldProceed: true,
|
711
|
+
});
|
712
|
+
});
|
713
|
+
it('should handle comma termination with complex file paths', async () => {
|
714
|
+
const fileContent = 'Package info';
|
715
|
+
const filePath = await createTestFile(path.join(testRootDir, 'package.json'), fileContent);
|
716
|
+
const query = `Review @${filePath}, then check dependencies.`;
|
717
|
+
const result = await handleAtCommand({
|
718
|
+
query,
|
719
|
+
config: mockConfig,
|
720
|
+
addItem: mockAddItem,
|
721
|
+
onDebugMessage: mockOnDebugMessage,
|
722
|
+
messageId: 415,
|
723
|
+
signal: abortController.signal,
|
724
|
+
});
|
725
|
+
expect(result).toEqual({
|
726
|
+
processedQuery: [
|
727
|
+
{ text: `Review @${filePath}, then check dependencies.` },
|
728
|
+
{ text: '\n--- Content from referenced files ---' },
|
729
|
+
{ text: `\nContent from @${filePath}:\n` },
|
730
|
+
{ text: fileContent },
|
731
|
+
{ text: '\n--- End of content ---' },
|
732
|
+
],
|
733
|
+
shouldProceed: true,
|
734
|
+
});
|
735
|
+
});
|
736
|
+
it('should not terminate at period within file name', async () => {
|
737
|
+
const fileContent = 'Version info';
|
738
|
+
const filePath = await createTestFile(path.join(testRootDir, 'version.1.2.3.txt'), fileContent);
|
739
|
+
const query = `Check @${filePath} contains version information.`;
|
740
|
+
const result = await handleAtCommand({
|
741
|
+
query,
|
742
|
+
config: mockConfig,
|
743
|
+
addItem: mockAddItem,
|
744
|
+
onDebugMessage: mockOnDebugMessage,
|
745
|
+
messageId: 416,
|
746
|
+
signal: abortController.signal,
|
747
|
+
});
|
748
|
+
expect(result).toEqual({
|
749
|
+
processedQuery: [
|
750
|
+
{ text: `Check @${filePath} contains version information.` },
|
751
|
+
{ text: '\n--- Content from referenced files ---' },
|
752
|
+
{ text: `\nContent from @${filePath}:\n` },
|
753
|
+
{ text: fileContent },
|
754
|
+
{ text: '\n--- End of content ---' },
|
755
|
+
],
|
756
|
+
shouldProceed: true,
|
757
|
+
});
|
758
|
+
});
|
759
|
+
it('should handle end of string termination for period and comma', async () => {
|
760
|
+
const fileContent = 'End file content';
|
761
|
+
const filePath = await createTestFile(path.join(testRootDir, 'end.txt'), fileContent);
|
762
|
+
const query = `Show me @${filePath}.`;
|
763
|
+
const result = await handleAtCommand({
|
764
|
+
query,
|
765
|
+
config: mockConfig,
|
766
|
+
addItem: mockAddItem,
|
767
|
+
onDebugMessage: mockOnDebugMessage,
|
768
|
+
messageId: 417,
|
769
|
+
signal: abortController.signal,
|
770
|
+
});
|
771
|
+
expect(result).toEqual({
|
772
|
+
processedQuery: [
|
773
|
+
{ text: `Show me @${filePath}.` },
|
774
|
+
{ text: '\n--- Content from referenced files ---' },
|
775
|
+
{ text: `\nContent from @${filePath}:\n` },
|
776
|
+
{ text: fileContent },
|
777
|
+
{ text: '\n--- End of content ---' },
|
778
|
+
],
|
779
|
+
shouldProceed: true,
|
780
|
+
});
|
781
|
+
});
|
782
|
+
it('should handle files with special characters in names', async () => {
|
783
|
+
const fileContent = 'File with special chars content';
|
784
|
+
const filePath = await createTestFile(path.join(testRootDir, 'file$with&special#chars.txt'), fileContent);
|
785
|
+
const query = `Check @${filePath} for content.`;
|
786
|
+
const result = await handleAtCommand({
|
787
|
+
query,
|
788
|
+
config: mockConfig,
|
789
|
+
addItem: mockAddItem,
|
790
|
+
onDebugMessage: mockOnDebugMessage,
|
791
|
+
messageId: 418,
|
792
|
+
signal: abortController.signal,
|
793
|
+
});
|
794
|
+
expect(result).toEqual({
|
795
|
+
processedQuery: [
|
796
|
+
{ text: `Check @${filePath} for content.` },
|
797
|
+
{ text: '\n--- Content from referenced files ---' },
|
798
|
+
{ text: `\nContent from @${filePath}:\n` },
|
799
|
+
{ text: fileContent },
|
800
|
+
{ text: '\n--- End of content ---' },
|
801
|
+
],
|
802
|
+
shouldProceed: true,
|
803
|
+
});
|
804
|
+
});
|
805
|
+
it('should handle basic file names without special characters', async () => {
|
806
|
+
const fileContent = 'Basic file content';
|
807
|
+
const filePath = await createTestFile(path.join(testRootDir, 'basicfile.txt'), fileContent);
|
808
|
+
const query = `Check @${filePath} please.`;
|
809
|
+
const result = await handleAtCommand({
|
810
|
+
query,
|
811
|
+
config: mockConfig,
|
812
|
+
addItem: mockAddItem,
|
813
|
+
onDebugMessage: mockOnDebugMessage,
|
814
|
+
messageId: 421,
|
815
|
+
signal: abortController.signal,
|
816
|
+
});
|
817
|
+
expect(result).toEqual({
|
818
|
+
processedQuery: [
|
819
|
+
{ text: `Check @${filePath} please.` },
|
820
|
+
{ text: '\n--- Content from referenced files ---' },
|
821
|
+
{ text: `\nContent from @${filePath}:\n` },
|
822
|
+
{ text: fileContent },
|
823
|
+
{ text: '\n--- End of content ---' },
|
824
|
+
],
|
825
|
+
shouldProceed: true,
|
826
|
+
});
|
827
|
+
});
|
828
|
+
});
|
829
|
+
});
|
830
|
+
//# sourceMappingURL=atCommandProcessor.test.js.map
|