From 81a1e127edc02b14d36e5925b99d633fd69b95f8 Mon Sep 17 00:00:00 2001 From: jacob314 Date: Wed, 4 Mar 2026 00:06:46 -0800 Subject: [PATCH] foo foo checkpoint --- GEMINI.md | 4 + eslint.config.js | 208 ++++++++++++++++++ packages/a2a-server/src/agent/task.ts | 4 +- packages/a2a-server/src/config/config.ts | 8 +- packages/a2a-server/src/types.ts | 2 +- .../a2a-server/src/utils/testing_utils.ts | 2 +- .../cli/src/commands/extensions/configure.ts | 2 +- .../cli/src/commands/extensions/update.ts | 2 +- packages/cli/src/commands/extensions/utils.ts | 20 +- packages/cli/src/commands/hooks/migrate.ts | 5 +- packages/cli/src/commands/mcp/add.ts | 11 +- .../cli/src/commands/mcp/enableDisable.ts | 6 +- packages/cli/src/commands/mcp/list.ts | 8 +- packages/cli/src/commands/mcp/remove.ts | 2 +- packages/cli/src/config/config.ts | 80 +++---- packages/cli/src/config/extension-manager.ts | 22 +- .../config/extensions/extensionEnablement.ts | 2 +- .../config/extensions/extensionSettings.ts | 12 +- packages/cli/src/config/extensions/github.ts | 6 +- packages/cli/src/config/extensions/update.ts | 2 +- .../cli/src/config/mcp/mcpServerEnablement.ts | 2 +- packages/cli/src/config/sandboxConfig.ts | 2 +- packages/cli/src/config/settings.test.ts | 100 ++++----- packages/cli/src/config/settings.ts | 40 ++-- .../cli/src/config/settings_repro.test.ts | 4 +- packages/cli/src/deferred.ts | 6 +- packages/cli/src/gemini.tsx | 10 +- packages/cli/src/nonInteractiveCli.ts | 8 +- packages/cli/src/nonInteractiveCliCommands.ts | 4 +- .../cli/src/services/BuiltinCommandLoader.ts | 2 +- .../cli/src/services/FileCommandLoader.ts | 2 +- packages/cli/src/services/McpPromptLoader.ts | 4 +- .../prompt-processors/shellProcessor.ts | 2 +- packages/cli/src/test-utils/AppRig.tsx | 8 +- packages/cli/src/test-utils/async.ts | 2 +- packages/cli/src/test-utils/svg.ts | 2 +- packages/cli/src/ui/IdeIntegrationNudge.tsx | 10 +- packages/cli/src/ui/auth/ApiAuthDialog.tsx | 2 +- packages/cli/src/ui/commands/aboutCommand.ts | 2 +- packages/cli/src/ui/commands/agentsCommand.ts | 8 +- packages/cli/src/ui/commands/authCommand.ts | 2 +- packages/cli/src/ui/commands/bugCommand.ts | 6 +- packages/cli/src/ui/commands/chatCommand.ts | 10 +- packages/cli/src/ui/commands/clearCommand.ts | 2 +- .../cli/src/ui/commands/compressCommand.ts | 2 +- packages/cli/src/ui/commands/copyCommand.ts | 2 +- .../cli/src/ui/commands/extensionsCommand.ts | 4 +- packages/cli/src/ui/commands/ideCommand.ts | 4 +- packages/cli/src/ui/commands/mcpCommand.ts | 12 +- .../cli/src/ui/commands/restoreCommand.ts | 2 +- .../cli/src/ui/commands/setupGithubCommand.ts | 2 +- packages/cli/src/ui/components/AboutBox.tsx | 2 +- .../src/ui/components/AgentConfigDialog.tsx | 14 +- .../cli/src/ui/components/AskUserDialog.tsx | 2 +- packages/cli/src/ui/components/CliSpinner.tsx | 2 +- packages/cli/src/ui/components/Composer.tsx | 2 +- .../ui/components/DetailedMessagesDisplay.tsx | 2 +- .../cli/src/ui/components/DialogManager.tsx | 2 +- .../cli/src/ui/components/InputPrompt.tsx | 17 +- .../src/ui/components/LoadingIndicator.tsx | 2 +- .../cli/src/ui/components/MainContent.tsx | 2 +- .../cli/src/ui/components/ModelDialog.tsx | 2 +- .../src/ui/components/ModelStatsDisplay.tsx | 2 +- .../cli/src/ui/components/SessionBrowser.tsx | 2 +- .../cli/src/ui/components/StatsDisplay.tsx | 2 +- .../ui/components/ToolConfirmationQueue.tsx | 4 +- .../components/shared/BaseSettingsDialog.tsx | 2 +- .../src/ui/components/shared/text-buffer.ts | 12 +- .../ui/components/triage/TriageDuplicates.tsx | 8 +- .../src/ui/components/triage/TriageIssues.tsx | 4 +- .../cli/src/ui/contexts/KeypressContext.tsx | 10 +- .../src/ui/contexts/SettingsContext.test.tsx | 4 +- .../cli/src/ui/hooks/atCommandProcessor.ts | 4 +- .../cli/src/ui/hooks/slashCommandProcessor.ts | 10 +- packages/cli/src/ui/hooks/useAtCompletion.ts | 22 +- packages/cli/src/ui/hooks/useFocus.ts | 8 +- packages/cli/src/ui/hooks/useFolderTrust.ts | 15 +- packages/cli/src/ui/hooks/useGeminiStream.ts | 14 +- .../cli/src/ui/hooks/useHistoryManager.ts | 6 +- .../cli/src/ui/hooks/useQuotaAndFallback.ts | 4 +- .../cli/src/ui/hooks/useSessionBrowser.ts | 2 +- packages/cli/src/ui/hooks/useSessionResume.ts | 2 +- .../src/ui/hooks/useTurnActivityMonitor.ts | 3 +- packages/cli/src/ui/privacy/PrivacyNotice.tsx | 2 +- packages/cli/src/ui/themes/theme.ts | 4 +- packages/cli/src/ui/utils/CodeColorizer.tsx | 6 +- packages/cli/src/ui/utils/commandUtils.ts | 8 +- .../cli/src/ui/utils/inlineThinkingMode.ts | 2 +- .../src/ui/utils/terminalCapabilityManager.ts | 2 +- packages/cli/src/ui/utils/terminalSetup.ts | 6 +- packages/cli/src/utils/activityLogger.ts | 2 +- packages/cli/src/utils/cleanup.ts | 2 +- packages/cli/src/utils/envVarResolver.ts | 6 +- packages/cli/src/utils/errors.ts | 2 +- packages/cli/src/utils/installationInfo.ts | 2 +- packages/cli/src/utils/relaunch.ts | 2 +- packages/cli/src/utils/sandbox.ts | 6 +- packages/cli/src/utils/settingsUtils.ts | 10 +- .../zed-integration/zedIntegration.test.ts | 2 +- .../cli/src/zed-integration/zedIntegration.ts | 4 +- .../src/agents/browser/browserAgentFactory.ts | 2 +- packages/core/src/agents/local-executor.ts | 2 +- .../core/src/availability/policyHelpers.ts | 12 +- packages/core/src/code_assist/setup.ts | 2 +- packages/core/src/code_assist/telemetry.ts | 4 +- packages/core/src/config/config.ts | 2 +- packages/core/src/config/projectRegistry.ts | 2 +- .../core/src/core/apiKeyCredentialStorage.ts | 2 +- packages/core/src/core/baseLlmClient.ts | 4 +- packages/core/src/core/client.test.ts | 6 +- packages/core/src/core/client.ts | 30 +-- packages/core/src/core/contentGenerator.ts | 4 +- packages/core/src/core/coreToolScheduler.ts | 47 +--- packages/core/src/core/geminiChat.test.ts | 3 + packages/core/src/core/geminiChat.ts | 23 +- packages/core/src/core/localLiteRtLmClient.ts | 2 +- .../core/src/core/loggingContentGenerator.ts | 6 +- packages/core/src/core/prompts.test.ts | 6 +- packages/core/src/core/turn.ts | 2 +- packages/core/src/hooks/hookEventHandler.ts | 2 +- packages/core/src/hooks/hookRunner.ts | 4 +- packages/core/src/hooks/hookSystem.ts | 4 +- packages/core/src/hooks/types.ts | 2 +- packages/core/src/ide/ide-client.ts | 2 +- packages/core/src/ide/ide-installer.ts | 8 +- .../core/src/mcp/google-auth-provider.test.ts | 1 + packages/core/src/mcp/google-auth-provider.ts | 4 +- .../mcp/token-storage/file-token-storage.ts | 6 +- packages/core/src/policy/policy-engine.ts | 4 +- packages/core/src/prompts/promptProvider.ts | 6 +- .../core/src/routing/modelRouterService.ts | 26 ++- .../routing/strategies/classifierStrategy.ts | 2 +- .../routing/strategies/fallbackStrategy.ts | 5 +- .../strategies/gemmaClassifierStrategy.ts | 2 +- .../strategies/numericalClassifierStrategy.ts | 2 +- packages/core/src/safety/context-builder.ts | 2 +- .../src/services/chatCompressionService.ts | 14 +- .../core/src/services/loopDetectionService.ts | 5 +- .../src/services/shellExecutionService.ts | 2 +- packages/core/src/skills/skillLoader.ts | 2 +- .../clearcut-logger/clearcut-logger.ts | 8 +- .../core/src/telemetry/gcp-exporters.test.ts | 18 ++ packages/core/src/telemetry/gcp-exporters.ts | 2 +- packages/core/src/telemetry/types.ts | 2 +- packages/core/src/tools/activate-skill.ts | 4 - packages/core/src/tools/ask-user.ts | 9 +- packages/core/src/tools/diff-utils.ts | 6 +- packages/core/src/tools/edit.ts | 10 +- packages/core/src/tools/get-internal-docs.ts | 2 +- packages/core/src/tools/glob.ts | 13 +- packages/core/src/tools/ls.ts | 20 +- packages/core/src/tools/mcp-client-manager.ts | 2 +- packages/core/src/tools/mcp-client.ts | 23 +- packages/core/src/tools/mcp-tool.ts | 16 +- packages/core/src/tools/modifiable-tool.ts | 4 +- packages/core/src/tools/read-many-files.ts | 11 +- packages/core/src/tools/ripGrep.ts | 2 +- packages/core/src/tools/shell.ts | 2 +- packages/core/src/tools/tool-registry.ts | 33 ++- packages/core/src/tools/tools.ts | 20 +- packages/core/src/tools/web-fetch.ts | 2 +- packages/core/src/tools/web-search.ts | 2 +- packages/core/src/tools/write-file.ts | 11 +- packages/core/src/tools/write-todos.ts | 14 +- packages/core/src/utils/bfsFileSearch.ts | 4 +- packages/core/src/utils/compatibility.ts | 4 +- packages/core/src/utils/errors.ts | 16 +- packages/core/src/utils/fileUtils.ts | 2 +- .../core/src/utils/filesearch/result-cache.ts | 2 +- .../utils/generateContentResponseUtilities.ts | 8 +- packages/core/src/utils/getFolderStructure.ts | 4 +- packages/core/src/utils/gitUtils.ts | 4 +- packages/core/src/utils/googleErrors.ts | 4 +- packages/core/src/utils/googleQuotaErrors.ts | 11 +- packages/core/src/utils/ignorePatterns.ts | 9 +- packages/core/src/utils/memoryDiscovery.ts | 4 +- .../core/src/utils/memoryImportProcessor.ts | 2 +- packages/core/src/utils/nextSpeakerChecker.ts | 9 +- packages/core/src/utils/schemaValidator.ts | 5 +- packages/core/src/utils/shell-utils.ts | 13 +- packages/sdk/src/session.ts | 2 +- .../vscode-ide-companion/src/diff-manager.ts | 6 +- .../vscode-ide-companion/src/extension.ts | 8 +- .../vscode-ide-companion/src/ide-server.ts | 4 +- .../src/open-files-manager.ts | 10 +- 185 files changed, 818 insertions(+), 748 deletions(-) diff --git a/GEMINI.md b/GEMINI.md index f7017eab40..81939aedbd 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -73,6 +73,10 @@ powerful tool for developers. and `packages/core` (Backend logic). - **Imports:** Use specific imports and avoid restricted relative imports between packages (enforced by ESLint). +- **Linting:** Never suppress the `@typescript-eslint/no-unnecessary-condition` + warning (e.g., via `eslint-disable`). This rule ensures that optional chaining + and truthiness checks are only used when truly necessary according to the type + system, keeping the code clean and predictable. - **License Headers:** For all new source code files (`.ts`, `.tsx`, `.js`), include the Apache-2.0 license header with the current year. (e.g., `Copyright 2026 Google LLC`). This is enforced by ESLint. diff --git a/eslint.config.js b/eslint.config.js index d305f75f87..459e1f31a5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -202,6 +202,7 @@ export default tseslint.config( '@typescript-eslint/no-unsafe-type-assertion': 'error', '@typescript-eslint/no-unsafe-assignment': 'error', '@typescript-eslint/no-unsafe-return': 'error', + '@typescript-eslint/no-unnecessary-condition': 'error', }, }, { @@ -377,6 +378,213 @@ export default tseslint.config( }, // Prettier config must be last prettierConfig, + { + // Legacy files with many @typescript-eslint/no-unnecessary-condition issues + files: [ + 'packages/a2a-server/src/agent/executor.ts', + 'packages/a2a-server/src/agent/task.ts', + 'packages/a2a-server/src/config/settings.ts', + 'packages/a2a-server/src/http/app.ts', + 'packages/cli/src/commands/extensions/configure.ts', + 'packages/cli/src/config/config.ts', + 'packages/cli/src/config/extension-manager.ts', + 'packages/cli/src/config/extensions/extensionEnablement.ts', + 'packages/cli/src/config/extensions/github.ts', + 'packages/cli/src/config/mcp/mcpServerEnablement.ts', + 'packages/cli/src/config/settings-validation.ts', + 'packages/cli/src/config/settings.ts', + 'packages/cli/src/config/trustedFolders.ts', + 'packages/cli/src/nonInteractiveCli.ts', + 'packages/cli/src/services/McpPromptLoader.ts', + 'packages/cli/src/test-utils/AppRig.tsx', + 'packages/cli/src/test-utils/mockConfig.ts', + 'packages/cli/src/test-utils/render.tsx', + 'packages/cli/src/ui/AppContainer.tsx', + 'packages/cli/src/ui/commands/agentsCommand.ts', + 'packages/cli/src/ui/commands/chatCommand.ts', + 'packages/cli/src/ui/commands/directoryCommand.tsx', + 'packages/cli/src/ui/commands/hooksCommand.ts', + 'packages/cli/src/ui/commands/mcpCommand.ts', + 'packages/cli/src/ui/commands/restoreCommand.ts', + 'packages/cli/src/ui/commands/rewindCommand.tsx', + 'packages/cli/src/ui/commands/setupGithubCommand.ts', + 'packages/cli/src/ui/commands/skillsCommand.ts', + 'packages/cli/src/ui/commands/statsCommand.ts', + 'packages/cli/src/ui/components/AskUserDialog.tsx', + 'packages/cli/src/ui/components/ColorsDisplay.tsx', + 'packages/cli/src/ui/components/Composer.tsx', + 'packages/cli/src/ui/components/ContextUsageDisplay.tsx', + 'packages/cli/src/ui/components/DetailedMessagesDisplay.tsx', + 'packages/cli/src/ui/components/DialogManager.tsx', + 'packages/cli/src/ui/components/ExitPlanModeDialog.tsx', + 'packages/cli/src/ui/components/FolderTrustDialog.tsx', + 'packages/cli/src/ui/components/HooksDialog.tsx', + 'packages/cli/src/ui/components/IdeTrustChangeDialog.tsx', + 'packages/cli/src/ui/components/ModelStatsDisplay.tsx', + 'packages/cli/src/ui/components/MultiFolderTrustDialog.tsx', + 'packages/cli/src/ui/components/Notifications.tsx', + 'packages/cli/src/ui/components/QuotaDisplay.tsx', + 'packages/cli/src/ui/components/RewindViewer.tsx', + 'packages/cli/src/ui/components/SessionBrowser.tsx', + 'packages/cli/src/ui/components/SettingsDialog.tsx', + 'packages/cli/src/ui/components/ShowMoreLines.tsx', + 'packages/cli/src/ui/components/StatsDisplay.tsx', + 'packages/cli/src/ui/components/ThemeDialog.tsx', + 'packages/cli/src/ui/components/UserIdentity.tsx', + 'packages/cli/src/ui/components/messages/SubagentProgressDisplay.tsx', + 'packages/cli/src/ui/components/messages/Todo.tsx', + 'packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx', + 'packages/cli/src/ui/components/messages/ToolGroupMessage.tsx', + 'packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx', + 'packages/cli/src/ui/components/shared/EnumSelector.tsx', + 'packages/cli/src/ui/components/shared/MaxSizedBox.tsx', + 'packages/cli/src/ui/components/shared/Scrollable.tsx', + 'packages/cli/src/ui/components/shared/VirtualizedList.tsx', + 'packages/cli/src/ui/components/shared/text-buffer.ts', + 'packages/cli/src/ui/components/triage/TriageDuplicates.tsx', + 'packages/cli/src/ui/components/triage/TriageIssues.tsx', + 'packages/cli/src/ui/components/views/McpStatus.tsx', + 'packages/cli/src/ui/contexts/KeypressContext.tsx', + 'packages/cli/src/ui/contexts/ScrollProvider.tsx', + 'packages/cli/src/ui/contexts/SessionContext.tsx', + 'packages/cli/src/ui/hooks/slashCommandProcessor.ts', + 'packages/cli/src/ui/hooks/useAtCompletion.ts', + 'packages/cli/src/ui/hooks/useCommandCompletion.tsx', + 'packages/cli/src/ui/hooks/useConsoleMessages.ts', + 'packages/cli/src/ui/hooks/useExtensionUpdates.ts', + 'packages/cli/src/ui/hooks/useGeminiStream.ts', + 'packages/cli/src/ui/hooks/useIncludeDirsTrust.tsx', + 'packages/cli/src/ui/hooks/useInputHistory.ts', + 'packages/cli/src/ui/hooks/useInputHistoryStore.ts', + 'packages/cli/src/ui/hooks/usePermissionsModifyTrust.ts', + 'packages/cli/src/ui/hooks/usePromptCompletion.ts', + 'packages/cli/src/ui/hooks/useQuotaAndFallback.ts', + 'packages/cli/src/ui/hooks/useSelectionList.ts', + 'packages/cli/src/ui/hooks/useShellCompletion.ts', + 'packages/cli/src/ui/hooks/useSlashCompletion.ts', + 'packages/cli/src/ui/hooks/useThemeCommand.ts', + 'packages/cli/src/ui/hooks/useToolScheduler.ts', + 'packages/cli/src/ui/hooks/vim.ts', + 'packages/cli/src/ui/themes/theme-manager.ts', + 'packages/cli/src/ui/utils/CodeColorizer.tsx', + 'packages/cli/src/ui/utils/MarkdownDisplay.tsx', + 'packages/cli/src/ui/utils/borderStyles.ts', + 'packages/cli/src/ui/utils/clipboardUtils.ts', + 'packages/cli/src/ui/utils/highlight.ts', + 'packages/cli/src/ui/utils/inlineThinkingMode.ts', + 'packages/cli/src/ui/utils/keybindingUtils.ts', + 'packages/cli/src/ui/utils/terminalCapabilityManager.ts', + 'packages/cli/src/ui/utils/terminalSetup.ts', + 'packages/cli/src/ui/utils/terminalUtils.ts', + 'packages/cli/src/utils/activityLogger.ts', + 'packages/cli/src/utils/commentJson.ts', + 'packages/cli/src/utils/deepMerge.ts', + 'packages/cli/src/utils/devtoolsService.ts', + 'packages/cli/src/utils/envVarResolver.ts', + 'packages/cli/src/utils/sandbox.ts', + 'packages/cli/src/utils/sessionUtils.ts', + 'packages/cli/src/utils/settingsUtils.ts', + 'packages/cli/src/zed-integration/zedIntegration.ts', + 'packages/core/src/agents/a2a-client-manager.ts', + 'packages/core/src/agents/a2aUtils.ts', + 'packages/core/src/agents/acknowledgedAgents.ts', + 'packages/core/src/agents/browser/browserManager.ts', + 'packages/core/src/agents/browser/mcpToolWrapper.ts', + 'packages/core/src/agents/local-executor.ts', + 'packages/core/src/agents/local-invocation.ts', + 'packages/core/src/agents/registry.ts', + 'packages/core/src/agents/subagent-tool.ts', + 'packages/core/src/availability/modelAvailabilityService.ts', + 'packages/core/src/availability/policyHelpers.ts', + 'packages/core/src/billing/billing.ts', + 'packages/core/src/code_assist/admin/mcpUtils.ts', + 'packages/core/src/code_assist/converter.ts', + 'packages/core/src/code_assist/oauth2.ts', + 'packages/core/src/code_assist/server.ts', + 'packages/core/src/code_assist/setup.ts', + 'packages/core/src/code_assist/telemetry.ts', + 'packages/core/src/commands/memory.ts', + 'packages/core/src/config/config.ts', + 'packages/core/src/confirmation-bus/message-bus.ts', + 'packages/core/src/core/baseLlmClient.ts', + 'packages/core/src/core/contentGenerator.ts', + 'packages/core/src/core/coreToolHookTriggers.ts', + 'packages/core/src/core/fakeContentGenerator.ts', + 'packages/core/src/core/geminiChat.ts', + 'packages/core/src/core/logger.ts', + 'packages/core/src/core/loggingContentGenerator.ts', + 'packages/core/src/core/turn.ts', + 'packages/core/src/hooks/hookRegistry.ts', + 'packages/core/src/hooks/hookRunner.ts', + 'packages/core/src/hooks/trustedHooks.ts', + 'packages/core/src/hooks/types.ts', + 'packages/core/src/ide/ide-client.ts', + 'packages/core/src/ide/ide-connection-utils.ts', + 'packages/core/src/ide/process-utils.ts', + 'packages/core/src/mcp/oauth-provider.ts', + 'packages/core/src/mcp/token-storage/base-token-storage.ts', + 'packages/core/src/policy/config.ts', + 'packages/core/src/prompts/mcp-prompts.ts', + 'packages/core/src/prompts/promptProvider.ts', + 'packages/core/src/routing/strategies/classifierStrategy.ts', + 'packages/core/src/routing/strategies/defaultStrategy.ts', + 'packages/core/src/routing/strategies/fallbackStrategy.ts', + 'packages/core/src/routing/strategies/numericalClassifierStrategy.ts', + 'packages/core/src/routing/strategies/overrideStrategy.ts', + 'packages/core/src/safety/checker-runner.ts', + 'packages/core/src/safety/conseca/conseca.ts', + 'packages/core/src/safety/conseca/policy-enforcer.ts', + 'packages/core/src/safety/conseca/policy-generator.ts', + 'packages/core/src/safety/context-builder.ts', + 'packages/core/src/scheduler/confirmation.ts', + 'packages/core/src/scheduler/scheduler.ts', + 'packages/core/src/scheduler/state-manager.ts', + 'packages/core/src/scheduler/tool-executor.ts', + 'packages/core/src/services/environmentSanitization.ts', + 'packages/core/src/services/modelConfigService.ts', + 'packages/core/src/services/sessionSummaryUtils.ts', + 'packages/core/src/services/shellExecutionService.ts', + 'packages/core/src/services/toolOutputMaskingService.ts', + 'packages/core/src/skills/skillLoader.ts', + 'packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts', + 'packages/core/src/telemetry/startupProfiler.ts', + 'packages/core/src/telemetry/telemetryAttributes.ts', + 'packages/core/src/telemetry/types.ts', + 'packages/core/src/telemetry/uiTelemetry.ts', + 'packages/core/src/tools/grep-utils.ts', + 'packages/core/src/tools/mcp-client.ts', + 'packages/core/src/tools/shell.ts', + 'packages/core/src/utils/bfsFileSearch.ts', + 'packages/core/src/utils/editCorrector.ts', + 'packages/core/src/utils/errors.ts', + 'packages/core/src/utils/fileDiffUtils.ts', + 'packages/core/src/utils/fileUtils.ts', + 'packages/core/src/utils/filesearch/crawler.ts', + 'packages/core/src/utils/filesearch/fileSearch.ts', + 'packages/core/src/utils/filesearch/result-cache.ts', + 'packages/core/src/utils/headless.ts', + 'packages/core/src/utils/ignoreFileParser.ts', + 'packages/core/src/utils/ignorePatterns.ts', + 'packages/core/src/utils/llm-edit-fixer.ts', + 'packages/core/src/utils/nextSpeakerChecker.ts', + 'packages/core/src/utils/partUtils.ts', + 'packages/core/src/utils/retry.ts', + 'packages/core/src/utils/safeJsonStringify.ts', + 'packages/core/src/utils/sessionUtils.ts', + 'packages/core/src/utils/shell-utils.ts', + 'packages/sdk/src/session.ts', + ], + rules: { + '@typescript-eslint/no-unnecessary-condition': 'off', + }, + }, + { + // Legacy files with many @typescript-eslint/no-unnecessary-type-assertion issues + files: ['packages/core/src/core/client.ts', 'packages/core/src/core/geminiChat.ts'], + rules: { + '@typescript-eslint/no-unnecessary-type-assertion': 'off', + }, + }, // extra settings for scripts that we run directly with node { files: ['./integration-tests/**/*.js'], diff --git a/packages/a2a-server/src/agent/task.ts b/packages/a2a-server/src/agent/task.ts index c969e601c3..918a794761 100644 --- a/packages/a2a-server/src/agent/task.ts +++ b/packages/a2a-server/src/agent/task.ts @@ -793,7 +793,7 @@ export class Task { ) { errorEvent = event; } - const errorMessage = errorEvent?.value?.error + const errorMessage = errorEvent?.value.error ? getErrorMessage(errorEvent.value.error) : 'Unknown error from LLM stream'; logger.error( @@ -802,7 +802,7 @@ export class Task { ); let errMessage = `Unknown error from LLM stream: ${JSON.stringify(event)}`; - if (errorEvent?.value?.error) { + if (errorEvent?.value.error) { errMessage = parseAndFormatApiError(errorEvent.value.error); } this.cancelPendingTools(`LLM stream error: ${errorMessage}`); diff --git a/packages/a2a-server/src/config/config.ts b/packages/a2a-server/src/config/config.ts index f3100bce4d..78553d5794 100644 --- a/packages/a2a-server/src/config/config.ts +++ b/packages/a2a-server/src/config/config.ts @@ -109,9 +109,9 @@ export async function loadConfig( }; const fileService = new FileDiscoveryService(workspaceDir, { - respectGitIgnore: configParams?.fileFiltering?.respectGitIgnore, - respectGeminiIgnore: configParams?.fileFiltering?.respectGeminiIgnore, - customIgnoreFilePaths: configParams?.fileFiltering?.customIgnoreFilePaths, + respectGitIgnore: configParams.fileFiltering?.respectGitIgnore, + respectGeminiIgnore: configParams.fileFiltering?.respectGeminiIgnore, + customIgnoreFilePaths: configParams.fileFiltering?.customIgnoreFilePaths, }); const { memoryContent, fileCount, filePaths } = await loadServerHierarchicalMemory( @@ -212,7 +212,7 @@ export function loadEnvironment(): void { function findEnvFile(startDir: string): string | null { let currentDir = path.resolve(startDir); - while (true) { + for (;;) { // prefer gemini-specific .env under GEMINI_DIR const geminiEnvPath = path.join(currentDir, GEMINI_DIR, '.env'); if (fs.existsSync(geminiEnvPath)) { diff --git a/packages/a2a-server/src/types.ts b/packages/a2a-server/src/types.ts index bce233c9dd..e616132bfa 100644 --- a/packages/a2a-server/src/types.ts +++ b/packages/a2a-server/src/types.ts @@ -148,7 +148,7 @@ function isPersistedStateMetadata( export function getPersistedState( metadata: PersistedTaskMetadata, ): PersistedStateMetadata | undefined { - const state = metadata?.[METADATA_KEY]; + const state = metadata[METADATA_KEY]; if (isPersistedStateMetadata(state)) { return state; } diff --git a/packages/a2a-server/src/utils/testing_utils.ts b/packages/a2a-server/src/utils/testing_utils.ts index 977daedf16..8a7b824e77 100644 --- a/packages/a2a-server/src/utils/testing_utils.ts +++ b/packages/a2a-server/src/utils/testing_utils.ts @@ -157,7 +157,7 @@ export function assertUniqueFinalEventIsLast( expect(finalEvent.metadata?.['coderAgent']).toMatchObject({ kind: 'state-change', }); - expect(finalEvent.status?.state).toBe('input-required'); + expect(finalEvent.status.state).toBe('input-required'); expect(finalEvent.final).toBe(true); // There is only one event with final and its the last diff --git a/packages/cli/src/commands/extensions/configure.ts b/packages/cli/src/commands/extensions/configure.ts index a2136968b3..8871f73d07 100644 --- a/packages/cli/src/commands/extensions/configure.ts +++ b/packages/cli/src/commands/extensions/configure.ts @@ -45,7 +45,7 @@ export const configureCommand: CommandModule = { const { name, setting, scope } = args; const settings = loadSettings(process.cwd()).merged; - if (!(settings.experimental?.extensionConfig ?? true)) { + if (!(settings.experimental.extensionConfig ?? true)) { coreEvents.emitFeedback( 'error', 'Extension configuration is currently disabled. Enable it by setting "experimental.extensionConfig" to true.', diff --git a/packages/cli/src/commands/extensions/update.ts b/packages/cli/src/commands/extensions/update.ts index 4e5f593518..46b1e314bd 100644 --- a/packages/cli/src/commands/extensions/update.ts +++ b/packages/cli/src/commands/extensions/update.ts @@ -82,7 +82,7 @@ export async function handleUpdate(args: UpdateArgs) { extensionManager, updateState, () => {}, - settings.experimental?.extensionReloading, + settings.experimental.extensionReloading, ))!; if ( updatedExtensionInfo.originalVersion !== diff --git a/packages/cli/src/commands/extensions/utils.ts b/packages/cli/src/commands/extensions/utils.ts index 78bad54502..7ff03defe0 100644 --- a/packages/cli/src/commands/extensions/utils.ts +++ b/packages/cli/src/commands/extensions/utils.ts @@ -99,12 +99,6 @@ export async function configureSpecificSetting( const extensionConfig = await extensionManager.loadExtensionConfig( extension.path, ); - if (!extensionConfig) { - logger.error( - `Could not find configuration for extension "${extensionName}".`, - ); - return; - } await updateSetting( extensionConfig, @@ -137,11 +131,7 @@ export async function configureExtension( const extensionConfig = await extensionManager.loadExtensionConfig( extension.path, ); - if ( - !extensionConfig || - !extensionConfig.settings || - extensionConfig.settings.length === 0 - ) { + if (!extensionConfig.settings || extensionConfig.settings.length === 0) { logger.log(`Extension "${extensionName}" has no settings to configure.`); return; } @@ -175,11 +165,7 @@ export async function configureAllExtensions( const extensionConfig = await extensionManager.loadExtensionConfig( extension.path, ); - if ( - extensionConfig && - extensionConfig.settings && - extensionConfig.settings.length > 0 - ) { + if (extensionConfig.settings && extensionConfig.settings.length > 0) { logger.log(`\nConfiguring settings for "${extension.name}"...`); await configureExtensionSettings( extensionConfig, @@ -208,7 +194,7 @@ export async function configureExtensionSettings( process.cwd(), ); - let workspaceSettings: Record = {}; + let workspaceSettings: Record = {}; if (scope === ExtensionSettingScope.USER) { workspaceSettings = await getScopedEnvContents( extensionConfig, diff --git a/packages/cli/src/commands/hooks/migrate.ts b/packages/cli/src/commands/hooks/migrate.ts index 47cc8660d7..d176bc64dd 100644 --- a/packages/cli/src/commands/hooks/migrate.ts +++ b/packages/cli/src/commands/hooks/migrate.ts @@ -236,10 +236,7 @@ export async function handleMigrateFromClaude() { const settings = loadSettings(workingDir); // Merge migrated hooks with existing hooks - const existingHooks = (settings.merged?.hooks || {}) as Record< - string, - unknown - >; + const existingHooks = settings.merged.hooks as Record; const mergedHooks = { ...existingHooks, ...migratedHooks }; // Update settings (setValue automatically saves) diff --git a/packages/cli/src/commands/mcp/add.ts b/packages/cli/src/commands/mcp/add.ts index 98e6a70879..61562c267f 100644 --- a/packages/cli/src/commands/mcp/add.ts +++ b/packages/cli/src/commands/mcp/add.ts @@ -117,7 +117,7 @@ async function addMcpServer( const existingSettings = settings.forScope(settingsScope).settings; const mcpServers = existingSettings.mcpServers || {}; - const isExistingServer = !!mcpServers[name]; + const isExistingServer = Object.hasOwn(mcpServers, name); if (isExistingServer) { debugLogger.log( `MCP server "${name}" is already configured within ${scope} settings.`, @@ -211,11 +211,12 @@ export const addCommand: CommandModule = { }) .middleware((argv) => { // Handle -- separator args as server args if present - if (argv['--']) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + const dashArgs = argv['--'] as string[] | undefined; + if (dashArgs && dashArgs.length > 0) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const existingArgs = (argv['args'] as Array) || []; - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - argv['args'] = [...existingArgs, ...(argv['--'] as string[])]; + const existingArgs = argv['args'] as Array; + argv['args'] = [...existingArgs, ...dashArgs]; } }), handler: async (argv) => { diff --git a/packages/cli/src/commands/mcp/enableDisable.ts b/packages/cli/src/commands/mcp/enableDisable.ts index b47e259eca..13f0fdcb8e 100644 --- a/packages/cli/src/commands/mcp/enableDisable.ts +++ b/packages/cli/src/commands/mcp/enableDisable.ts @@ -43,9 +43,9 @@ async function handleEnable(args: Args): Promise { } const result = await canLoadServer(name, { - adminMcpEnabled: settings.merged.admin?.mcp?.enabled ?? true, - allowedList: settings.merged.mcp?.allowed, - excludedList: settings.merged.mcp?.excluded, + adminMcpEnabled: settings.merged.admin.mcp.enabled, + allowedList: settings.merged.mcp.allowed, + excludedList: settings.merged.mcp.excluded, }); if ( diff --git a/packages/cli/src/commands/mcp/list.ts b/packages/cli/src/commands/mcp/list.ts index 421c822a55..1d1d295d03 100644 --- a/packages/cli/src/commands/mcp/list.ts +++ b/packages/cli/src/commands/mcp/list.ts @@ -39,10 +39,12 @@ export async function getMcpServersFromConfig( requestSetting: promptForSetting, }); const extensions = await extensionManager.loadExtensions(); - const mcpServers = { ...settings.mcpServers }; + const mcpServers: Record = { + ...settings.mcpServers, + }; for (const extension of extensions) { Object.entries(extension.mcpServers || {}).forEach(([key, server]) => { - if (mcpServers[key]) { + if (key in mcpServers) { return; } mcpServers[key] = { @@ -52,7 +54,7 @@ export async function getMcpServersFromConfig( }); } - const adminAllowlist = settings.admin?.mcp?.config; + const adminAllowlist = settings.admin.mcp.config; const filteredResult = applyAdminAllowlist(mcpServers, adminAllowlist); return filteredResult; diff --git a/packages/cli/src/commands/mcp/remove.ts b/packages/cli/src/commands/mcp/remove.ts index 8c5bd1efab..ad1f421fee 100644 --- a/packages/cli/src/commands/mcp/remove.ts +++ b/packages/cli/src/commands/mcp/remove.ts @@ -24,7 +24,7 @@ async function removeMcpServer( const existingSettings = settings.forScope(settingsScope).settings; const mcpServers = existingSettings.mcpServers || {}; - if (!mcpServers[name]) { + if (!(name in mcpServers)) { debugLogger.log(`Server "${name}" not found in ${scope} settings.`); return; } diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 4f48c696b4..97f9626111 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -327,11 +327,11 @@ export async function parseArguments( return true; }); - if (settings.experimental?.extensionManagement) { + if (settings.experimental.extensionManagement) { yargsInstance.command(extensionsCommand); } - if (settings.skills?.enabled ?? true) { + if (settings.skills.enabled) { yargsInstance.command(skillsCommand); } // Register hooks command if hooks are enabled @@ -456,16 +456,16 @@ export async function loadCliConfig( process.env['GEMINI_SANDBOX'] = 'true'; } - const memoryImportFormat = settings.context?.importFormat || 'tree'; - const includeDirectoryTree = settings.context?.includeDirectoryTree ?? true; + const memoryImportFormat = settings.context.importFormat || 'tree'; + const includeDirectoryTree = settings.context.includeDirectoryTree; - const ideMode = settings.ide?.enabled ?? false; + const ideMode = settings.ide.enabled; const folderTrust = process.env['GEMINI_CLI_INTEGRATION_TEST'] === 'true' || process.env['VITEST'] === 'true' ? false - : (settings.security?.folderTrust?.enabled ?? false); + : settings.security.folderTrust.enabled; const trustedFolder = isWorkspaceTrusted(settings, cwd, undefined, { prompt: argv.prompt, @@ -476,7 +476,7 @@ export async function loadCliConfig( // TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed // directly to the Config constructor in core, and have core handle setGeminiMdFilename. // However, loadHierarchicalGeminiMemory is called *before* createServerConfig. - if (settings.context?.fileName) { + if (settings.context.fileName) { setServerGeminiMdFilename(settings.context.fileName); } else { // Reset to default if not provided in settings. @@ -487,15 +487,15 @@ export async function loadCliConfig( const memoryFileFiltering = { ...DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, - ...settings.context?.fileFiltering, + ...settings.context.fileFiltering, }; const fileFiltering = { ...DEFAULT_FILE_FILTERING_OPTIONS, - ...settings.context?.fileFiltering, + ...settings.context.fileFiltering, }; - const includeDirectories = (settings.context?.includeDirectories || []) + const includeDirectories = settings.context.includeDirectories .map(resolvePath) .concat((argv.includeDirectories || []).map(resolvePath)); @@ -515,7 +515,7 @@ export async function loadCliConfig( .getExtensions() .find((ext) => ext.isActive && ext.plan?.directory)?.plan; - const experimentalJitContext = settings.experimental?.jitContext ?? false; + const experimentalJitContext = settings.experimental.jitContext; let memoryContent: string | HierarchicalMemory = ''; let fileCount = 0; @@ -525,7 +525,7 @@ export async function loadCliConfig( // Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version const result = await loadServerHierarchicalMemory( cwd, - settings.context?.loadMemoryFromIncludeDirectories || false + settings.context.loadMemoryFromIncludeDirectories ? includeDirectories : [], debugMode, @@ -534,7 +534,7 @@ export async function loadCliConfig( trustedFolder, memoryImportFormat, memoryFileFiltering, - settings.context?.discoveryMaxDirs, + settings.context.discoveryMaxDirs, ); memoryContent = result.memoryContent; fileCount = result.fileCount; @@ -548,8 +548,8 @@ export async function loadCliConfig( const rawApprovalMode = argv.approvalMode || (argv.yolo ? 'yolo' : undefined) || - ((settings.general?.defaultApprovalMode as string) !== 'yolo' - ? settings.general?.defaultApprovalMode + ((settings.general.defaultApprovalMode as string) !== 'yolo' + ? settings.general.defaultApprovalMode : undefined); if (rawApprovalMode) { @@ -561,7 +561,7 @@ export async function loadCliConfig( approvalMode = ApprovalMode.AUTO_EDIT; break; case 'plan': - if (!(settings.experimental?.plan ?? false)) { + if (!settings.experimental.plan) { debugLogger.warn( 'Approval mode "plan" is only available when experimental.plan is enabled. Falling back to "default".', ); @@ -583,9 +583,9 @@ export async function loadCliConfig( } // Override approval mode if disableYoloMode is set. - if (settings.security?.disableYoloMode || settings.admin?.secureModeEnabled) { + if (settings.security.disableYoloMode || settings.admin.secureModeEnabled) { if (approvalMode === ApprovalMode.YOLO) { - if (settings.admin?.secureModeEnabled) { + if (settings.admin.secureModeEnabled) { debugLogger.error( 'YOLO mode is disabled by "secureModeEnabled" setting.', ); @@ -636,7 +636,7 @@ export async function loadCliConfig( (!isHeadlessMode({ prompt: argv.prompt, query: argv.query }) && !argv.isCommand); - const allowedTools = argv.allowedTools || settings.tools?.allowed || []; + const allowedTools = argv.allowedTools || settings.tools.allowed || []; const allowedToolsSet = new Set(allowedTools); // In non-interactive mode, exclude tools that require a prompt. @@ -694,7 +694,7 @@ export async function loadCliConfig( }, mcp: { ...settings.mcp, - allowed: argv.allowedMcpServerNames ?? settings.mcp?.allowed, + allowed: argv.allowedMcpServerNames ?? settings.mcp.allowed, }, policyPaths: argv.policy, }; @@ -715,7 +715,7 @@ export async function loadCliConfig( const defaultModel = PREVIEW_GEMINI_MODEL_AUTO; const specifiedModel = - argv.model || process.env['GEMINI_MODEL'] || settings.model?.name; + argv.model || process.env['GEMINI_MODEL'] || settings.model.name; const resolvedModel = specifiedModel === GEMINI_MODEL_ALIAS_AUTO @@ -729,9 +729,9 @@ export async function loadCliConfig( const ptyInfo = await getPty(); - const mcpEnabled = settings.admin?.mcp?.enabled ?? true; - const extensionsEnabled = settings.admin?.extensions?.enabled ?? true; - const adminSkillsEnabled = settings.admin?.skills?.enabled ?? true; + const mcpEnabled = settings.admin.mcp.enabled; + const extensionsEnabled = settings.admin.extensions.enabled; + const adminSkillsEnabled = settings.admin.skills.enabled; // Create MCP enablement manager and callbacks const mcpEnablementManager = McpServerEnablementManager.getInstance(); @@ -739,8 +739,8 @@ export async function loadCliConfig( ? mcpEnablementManager.getEnablementCallbacks() : undefined; - const adminAllowlist = settings.admin?.mcp?.config; - let mcpServerCommand = mcpEnabled ? settings.mcp?.serverCommand : undefined; + const adminAllowlist = settings.admin.mcp.config; + let mcpServerCommand = mcpEnabled ? settings.mcp.serverCommand : undefined; let mcpServers = mcpEnabled ? settings.mcpServers : {}; if (mcpEnabled && adminAllowlist && Object.keys(adminAllowlist).length > 0) { @@ -748,7 +748,7 @@ export async function loadCliConfig( mcpServers = result.mcpServers; mcpServerCommand = undefined; - if (result.blockedServerNames && result.blockedServerNames.length > 0) { + if (result.blockedServerNames.length > 0) { const message = getAdminBlockedMcpServersMessage( result.blockedServerNames, undefined, @@ -766,17 +766,17 @@ export async function loadCliConfig( includeDirectoryTree, includeDirectories, loadMemoryFromIncludeDirectories: - settings.context?.loadMemoryFromIncludeDirectories || false, + settings.context.loadMemoryFromIncludeDirectories, debugMode, question, - coreTools: settings.tools?.core || undefined, + coreTools: settings.tools.core || undefined, allowedTools: allowedTools.length > 0 ? allowedTools : undefined, policyEngineConfig, policyUpdateConfirmationRequest, excludeTools, - toolDiscoveryCommand: settings.tools?.discoveryCommand, - toolCallCommand: settings.tools?.callCommand, + toolDiscoveryCommand: settings.tools.discoveryCommand, + toolCallCommand: settings.tools.callCommand, mcpServerCommand, mcpServers, mcpEnablementCallbacks, @@ -785,32 +785,32 @@ export async function loadCliConfig( agents: settings.agents, adminSkillsEnabled, allowedMcpServers: mcpEnabled - ? (argv.allowedMcpServerNames ?? settings.mcp?.allowed) + ? (argv.allowedMcpServerNames ?? settings.mcp.allowed) : undefined, blockedMcpServers: mcpEnabled ? argv.allowedMcpServerNames ? undefined - : settings.mcp?.excluded + : settings.mcp.excluded : undefined, blockedEnvironmentVariables: - settings.security?.environmentVariableRedaction?.blocked, + settings.security.environmentVariableRedaction.blocked, enableEnvironmentVariableRedaction: - settings.security?.environmentVariableRedaction?.enabled, + settings.security.environmentVariableRedaction.enabled, userMemory: memoryContent, geminiMdFileCount: fileCount, geminiMdFilePaths: filePaths, approvalMode, disableYoloMode: - settings.security?.disableYoloMode || settings.admin?.secureModeEnabled, - showMemoryUsage: settings.ui?.showMemoryUsage || false, + settings.security.disableYoloMode || settings.admin.secureModeEnabled, + showMemoryUsage: settings.ui.showMemoryUsage, accessibility: { - ...settings.ui?.accessibility, + ...settings.ui.accessibility, screenReader, }, telemetry: telemetrySettings, - usageStatisticsEnabled: settings.privacy?.usageStatisticsEnabled, + usageStatisticsEnabled: settings.privacy.usageStatisticsEnabled, fileFiltering, - checkpointing: settings.general?.checkpointing?.enabled, + checkpointing: settings.general.checkpointing.enabled, proxy: process.env['HTTPS_PROXY'] || process.env['https_proxy'] || diff --git a/packages/cli/src/config/extension-manager.ts b/packages/cli/src/config/extension-manager.ts index a9fce44635..283d967ac4 100644 --- a/packages/cli/src/config/extension-manager.ts +++ b/packages/cli/src/config/extension-manager.ts @@ -154,11 +154,8 @@ export class ExtensionManager extends ExtensionLoader { installMetadata: ExtensionInstallMetadata, previousExtensionConfig?: ExtensionConfig, ): Promise { - if ( - this.settings.security?.allowedExtensions && - this.settings.security?.allowedExtensions.length > 0 - ) { - const extensionAllowed = this.settings.security?.allowedExtensions.some( + if (this.settings.security.allowedExtensions.length > 0) { + const extensionAllowed = this.settings.security.allowedExtensions.some( (pattern) => { try { return new RegExp(pattern).test(installMetadata.source); @@ -312,7 +309,7 @@ Would you like to attempt to install via "git clone" instead?`, const destinationPath = new ExtensionStorage( newExtensionName, ).getExtensionDir(); - let previousSettings: Record | undefined; + let previousSettings: Record | undefined; if (isUpdate) { previousSettings = await getEnvContents( previousExtensionConfig, @@ -626,19 +623,16 @@ Would you like to attempt to install via "git clone" instead?`, const installMetadata = loadInstallMetadata(extensionDir); let effectiveExtensionPath = extensionDir; - if ( - this.settings.security?.allowedExtensions && - this.settings.security?.allowedExtensions.length > 0 - ) { + if (this.settings.security.allowedExtensions.length > 0) { if (!installMetadata?.source) { throw new Error( `Failed to load extension ${extensionDir}. The ${INSTALL_METADATA_FILENAME} file is missing or misconfigured.`, ); } - const extensionAllowed = this.settings.security?.allowedExtensions.some( + const extensionAllowed = this.settings.security.allowedExtensions.some( (pattern) => { try { - return new RegExp(pattern).test(installMetadata?.source); + return new RegExp(pattern).test(installMetadata.source); } catch (e) { throw new Error( `Invalid regex pattern in allowedExtensions setting: "${pattern}. Error: ${getErrorMessage(e)}`, @@ -672,8 +666,8 @@ Would you like to attempt to install via "git clone" instead?`, const extensionId = getExtensionId(config, installMetadata); - let userSettings: Record = {}; - let workspaceSettings: Record = {}; + let userSettings: Record = {}; + let workspaceSettings: Record = {}; if (this.settings.experimental.extensionConfig) { userSettings = await getScopedEnvContents( diff --git a/packages/cli/src/config/extensions/extensionEnablement.ts b/packages/cli/src/config/extensions/extensionEnablement.ts index 7ae2431ee9..e3042a216f 100644 --- a/packages/cli/src/config/extensions/extensionEnablement.ts +++ b/packages/cli/src/config/extensions/extensionEnablement.ts @@ -166,7 +166,7 @@ export class ExtensionEnablementManager { const extensionConfig = config[extensionName]; // Extensions are enabled by default. let enabled = true; - const allOverrides = extensionConfig?.overrides ?? []; + const allOverrides = extensionConfig.overrides ?? []; for (const rule of allOverrides) { const override = Override.fromFileRule(rule); if (override.matchesPath(ensureLeadingAndTrailingSlash(currentPath))) { diff --git a/packages/cli/src/config/extensions/extensionSettings.ts b/packages/cli/src/config/extensions/extensionSettings.ts index 700d854e20..a329589d15 100644 --- a/packages/cli/src/config/extensions/extensionSettings.ts +++ b/packages/cli/src/config/extensions/extensionSettings.ts @@ -64,7 +64,7 @@ export async function maybePromptForSettings( extensionId: string, requestSetting: (setting: ExtensionSetting) => Promise, previousExtensionConfig?: ExtensionConfig, - previousSettings?: Record, + previousSettings?: Record, ): Promise { const { name: extensionName, settings } = extensionConfig; if ( @@ -92,7 +92,9 @@ export async function maybePromptForSettings( previousExtensionConfig?.settings ?? [], ); - const allSettings: Record = { ...previousSettings }; + const allSettings: Record = { + ...previousSettings, + }; for (const removedEnvSetting of settingsChanges.removeEnv) { delete allSettings[removedEnvSetting.envVar]; @@ -174,13 +176,13 @@ export async function getScopedEnvContents( extensionId: string, scope: ExtensionSettingScope, workspaceDir?: string, -): Promise> { +): Promise> { const { name: extensionName } = extensionConfig; const keychain = new KeychainTokenStorage( getKeychainStorageName(extensionName, extensionId, scope, workspaceDir), ); const envFilePath = getEnvFilePath(extensionName, scope, workspaceDir); - let customEnv: Record = {}; + let customEnv: Record = {}; if (fsSync.existsSync(envFilePath)) { const stat = fsSync.statSync(envFilePath); if (!stat.isDirectory()) { @@ -206,7 +208,7 @@ export async function getEnvContents( extensionConfig: ExtensionConfig, extensionId: string, workspaceDir: string, -): Promise> { +): Promise> { if (!extensionConfig.settings || extensionConfig.settings.length === 0) { return Promise.resolve({}); } diff --git a/packages/cli/src/config/extensions/github.ts b/packages/cli/src/config/extensions/github.ts index e8b35a6184..719f6b9a26 100644 --- a/packages/cli/src/config/extensions/github.ts +++ b/packages/cli/src/config/extensions/github.ts @@ -109,16 +109,16 @@ export function tryParseGithubUrl(source: string): GithubRepoInfo | null { if (!parsedUrl) { throw new Error(`Invalid repo URL: ${source}`); } - if (parsedUrl?.host !== 'github.com') { + if (parsedUrl.host !== 'github.com') { return null; } // The pathname should be "/owner/repo". - const parts = parsedUrl?.pathname + const parts = parsedUrl.pathname .split('/') // Remove the empty segments, fixes trailing and leading slashes .filter((part) => part !== ''); - if (parts?.length !== 2) { + if (parts.length !== 2) { throw new Error( `Invalid GitHub repository source: ${source}. Expected "owner/repo" or a github repo uri.`, ); diff --git a/packages/cli/src/config/extensions/update.ts b/packages/cli/src/config/extensions/update.ts index bdb43e0975..803d2f24be 100644 --- a/packages/cli/src/config/extensions/update.ts +++ b/packages/cli/src/config/extensions/update.ts @@ -48,7 +48,7 @@ export async function updateExtension( `Extension ${extension.name} cannot be updated, type is unknown.`, ); } - if (installMetadata?.type === 'link') { + if (installMetadata.type === 'link') { dispatchExtensionStateUpdate({ type: 'SET_STATE', payload: { name: extension.name, state: ExtensionUpdateState.UP_TO_DATE }, diff --git a/packages/cli/src/config/mcp/mcpServerEnablement.ts b/packages/cli/src/config/mcp/mcpServerEnablement.ts index 1a6c445604..d662a7ae9f 100644 --- a/packages/cli/src/config/mcp/mcpServerEnablement.ts +++ b/packages/cli/src/config/mcp/mcpServerEnablement.ts @@ -226,7 +226,7 @@ export class McpServerEnablementManager { async isFileEnabled(serverName: string): Promise { const config = await this.readConfig(); const state = config[normalizeServerId(serverName)]; - return state?.enabled ?? true; + return state.enabled ?? true; } /** diff --git a/packages/cli/src/config/sandboxConfig.ts b/packages/cli/src/config/sandboxConfig.ts index 57430becae..bfb5118d54 100644 --- a/packages/cli/src/config/sandboxConfig.ts +++ b/packages/cli/src/config/sandboxConfig.ts @@ -45,7 +45,7 @@ function getSandboxCommand( const environmentConfiguredSandbox = process.env['GEMINI_SANDBOX']?.toLowerCase().trim() ?? ''; sandbox = - environmentConfiguredSandbox?.length > 0 + environmentConfiguredSandbox.length > 0 ? environmentConfiguredSandbox : sandbox; if (sandbox === '1' || sandbox === 'true') sandbox = true; diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts index 8fd0bd81b0..5f89f7aa1b 100644 --- a/packages/cli/src/config/settings.test.ts +++ b/packages/cli/src/config/settings.test.ts @@ -460,7 +460,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.security?.folderTrust?.enabled).toBe(false); // Workspace setting should be used + expect(settings.merged.security.folderTrust?.enabled).toBe(false); // Workspace setting should be used }); it('should use system folderTrust over user setting', () => { @@ -500,7 +500,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.security?.folderTrust?.enabled).toBe(true); // System setting should be used + expect(settings.merged.security.folderTrust?.enabled).toBe(true); // System setting should be used }); it('should not allow user or workspace to override system disableYoloMode', () => { @@ -534,7 +534,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.security?.disableYoloMode).toBe(true); // System setting should be used + expect(settings.merged.security.disableYoloMode).toBe(true); // System setting should be used }); it.each([ @@ -630,7 +630,7 @@ describe('Settings Loading and Merging', () => { 'WORKSPACE_DEBUG', 'WORKSPACE_VAR', ]); - expect(settings.merged.advanced?.excludedEnvVars).toEqual([ + expect(settings.merged.advanced.excludedEnvVars).toEqual([ 'DEBUG', 'DEBUG_MODE', 'NODE_ENV', @@ -658,7 +658,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.context?.fileName).toBeUndefined(); + expect(settings.merged.context.fileName).toBeUndefined(); }); it.each([ @@ -991,7 +991,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.model?.compressionThreshold).toEqual(expected); + expect(settings.merged.model.compressionThreshold).toEqual(expected); }); }); @@ -1018,7 +1018,7 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.model?.compressionThreshold).toEqual(0.5); + expect(settings.merged.model.compressionThreshold).toEqual(0.5); }); it('should merge includeDirectories from all scopes', () => { @@ -1052,7 +1052,7 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.context?.includeDirectories).toEqual([ + expect(settings.merged.context.includeDirectories).toEqual([ '/system/defaults/dir', '/user/dir1', '/user/dir2', @@ -1247,7 +1247,7 @@ describe('Settings Loading and Merging', () => { expect((settings.merged as TestSettings)['workspaceOnly']).toBe( 'workspace_value', ); - expect(settings.merged.ui?.theme).toBe('light'); // workspace overrides user + expect(settings.merged.ui.theme).toBe('light'); // workspace overrides user delete process.env['SYSTEM_VAR']; delete process.env['USER_VAR']; @@ -1275,7 +1275,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.advanced?.dnsResolutionOrder).toBe('verbatim'); + expect(settings.merged.advanced.dnsResolutionOrder).toBe('verbatim'); }); it('should use user dnsResolutionOrder if workspace is not defined', () => { @@ -1294,7 +1294,7 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.advanced?.dnsResolutionOrder).toBe('verbatim'); + expect(settings.merged.advanced.dnsResolutionOrder).toBe('verbatim'); }); it('should leave unresolved environment variables as is', () => { @@ -1599,7 +1599,7 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); // Verify the settings were loaded correctly - expect(settings.merged.advanced?.excludedEnvVars).toEqual([ + expect(settings.merged.advanced.excludedEnvVars).toEqual([ 'DEBUG', 'DEBUG_MODE', ]); @@ -1637,7 +1637,7 @@ describe('Settings Loading and Merging', () => { 'NODE_ENV', 'DEBUG', ]); - expect(settings.merged.advanced?.excludedEnvVars).toEqual([ + expect(settings.merged.advanced.excludedEnvVars).toEqual([ 'DEBUG', 'DEBUG_MODE', 'NODE_ENV', @@ -1677,7 +1677,7 @@ describe('Settings Loading and Merging', () => { 'WORKSPACE_DEBUG', 'WORKSPACE_VAR', ]); - expect(settings.merged.advanced?.excludedEnvVars).toEqual([ + expect(settings.merged.advanced.excludedEnvVars).toEqual([ 'DEBUG', 'DEBUG_MODE', 'NODE_ENV', @@ -1711,9 +1711,9 @@ describe('Settings Loading and Merging', () => { ); const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.tools?.sandbox).toBe(true); - expect(settings.merged.context?.fileName).toBe('WORKSPACE.md'); - expect(settings.merged.ui?.theme).toBe('dark'); + expect(settings.merged.tools.sandbox).toBe(true); + expect(settings.merged.context.fileName).toBe('WORKSPACE.md'); + expect(settings.merged.ui.theme).toBe('dark'); }); it('should NOT merge workspace settings when workspace is not trusted', () => { @@ -1744,9 +1744,9 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.tools?.sandbox).toBe(false); // User setting - expect(settings.merged.context?.fileName).toBe('USER.md'); // User setting - expect(settings.merged.ui?.theme).toBe('dark'); // User setting + expect(settings.merged.tools.sandbox).toBe(false); // User setting + expect(settings.merged.context.fileName).toBe('USER.md'); // User setting + expect(settings.merged.ui.theme).toBe('dark'); // User setting }); it('should NOT merge workspace settings when workspace trust is undefined', () => { @@ -1777,8 +1777,8 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); - expect(settings.merged.tools?.sandbox).toBe(false); // User setting - expect(settings.merged.context?.fileName).toBe('USER.md'); // User setting + expect(settings.merged.tools.sandbox).toBe(false); // User setting + expect(settings.merged.context.fileName).toBe('USER.md'); // User setting }); }); @@ -2229,7 +2229,7 @@ describe('Settings Loading and Merging', () => { const settings = loadSettings(MOCK_WORKSPACE_DIR); // Verify it was migrated in the merged settings - expect(settings.merged.general?.enableAutoUpdate).toBe(false); + expect(settings.merged.general.enableAutoUpdate).toBe(false); // Verify it was saved back to disk (via setValue calling updateSettingsFilePreservingFormat) expect(updateSettingsFilePreservingFormat).toHaveBeenCalledWith( @@ -2289,7 +2289,7 @@ describe('Settings Loading and Merging', () => { ).toBe(true); // Merged should also reflect it (system overrides defaults, but both are migrated) - expect(settings.merged.general?.enableAutoUpdateNotification).toBe(false); + expect(settings.merged.general.enableAutoUpdateNotification).toBe(false); // Verify it was NOT saved back to disk expect(updateSettingsFilePreservingFormat).not.toHaveBeenCalledWith( @@ -2487,10 +2487,10 @@ describe('Settings Loading and Merging', () => { // 1. Verify that on initial load, file-based admin settings are ignored // and schema defaults are used instead. - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); // default: false - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); // default: true - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); // default: true - expect(loadedSettings.merged.ui?.theme).toBe('system-theme'); // non-admin setting should be loaded + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(false); // default: false + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(true); // default: true + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(true); // default: true + expect(loadedSettings.merged.ui.theme).toBe('system-theme'); // non-admin setting should be loaded // 2. Now, set remote admin settings. loadedSettings.setRemoteAdminSettings({ @@ -2503,11 +2503,11 @@ describe('Settings Loading and Merging', () => { }); // 3. Verify that remote admin settings take precedence. - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(true); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(false); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(false); // non-admin setting should remain unchanged - expect(loadedSettings.merged.ui?.theme).toBe('system-theme'); + expect(loadedSettings.merged.ui.theme).toBe('system-theme'); }); it('should set remote admin settings and recompute merged settings', () => { @@ -2532,10 +2532,10 @@ describe('Settings Loading and Merging', () => { const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR); // Ensure initial state from defaults (as file-based admin settings are ignored) - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); - expect(loadedSettings.merged.ui?.theme).toBe('initial-theme'); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(false); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(true); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(true); + expect(loadedSettings.merged.ui.theme).toBe('initial-theme'); const newRemoteSettings = { strictModeDisabled: false, @@ -2549,11 +2549,11 @@ describe('Settings Loading and Merging', () => { loadedSettings.setRemoteAdminSettings(newRemoteSettings); // Verify that remote admin settings are applied - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(true); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(false); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(false); // Non-admin settings should remain untouched - expect(loadedSettings.merged.ui?.theme).toBe('initial-theme'); + expect(loadedSettings.merged.ui.theme).toBe('initial-theme'); }); it('should correctly handle undefined remote admin settings', () => { @@ -2573,16 +2573,16 @@ describe('Settings Loading and Merging', () => { const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR); // Should have default admin settings - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(false); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(true); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(true); loadedSettings.setRemoteAdminSettings({}); // Set empty remote settings // Admin settings should revert to defaults because there are no remote overrides - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(false); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(true); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(true); }); it('should un-nest MCP configuration from remote settings', () => { @@ -2604,7 +2604,7 @@ describe('Settings Loading and Merging', () => { }, }); - expect(loadedSettings.merged.admin?.mcp?.config).toEqual(mcpServers); + expect(loadedSettings.merged.admin.mcp?.config).toEqual(mcpServers); }); it('should set skills based on unmanagedCapabilitiesEnabled', () => { @@ -2630,9 +2630,9 @@ describe('Settings Loading and Merging', () => { loadedSettings.setRemoteAdminSettings({}); // Should default to schema defaults (standard defaults) - expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); - expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); - expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); + expect(loadedSettings.merged.admin.secureModeEnabled).toBe(false); + expect(loadedSettings.merged.admin.mcp?.enabled).toBe(true); + expect(loadedSettings.merged.admin.extensions?.enabled).toBe(true); }); }); diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index 4e9faf5767..9717ad4e41 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -54,24 +54,20 @@ import { export function getMergeStrategyForPath( path: string[], ): MergeStrategy | undefined { - let current: SettingDefinition | undefined = undefined; let currentSchema: SettingsSchema | undefined = getSettingsSchema(); let parent: SettingDefinition | undefined = undefined; for (const key of path) { - if (!currentSchema || !currentSchema[key]) { + const current: SettingDefinition | undefined = currentSchema?.[key]; + if (!current) { // Key not found in schema - check if parent has additionalProperties - if (parent?.additionalProperties?.mergeStrategy) { - return parent.additionalProperties.mergeStrategy; - } - return undefined; + return parent?.additionalProperties?.mergeStrategy; } parent = current; - current = currentSchema[key]; currentSchema = current.properties; } - return current?.mergeStrategy; + return parent?.mergeStrategy; } export const USER_SETTINGS_PATH = Storage.getGlobalSettingsPath(); @@ -372,19 +368,17 @@ export class LoadedSettings { // Remote admin settings always take precedence and file-based admin settings // are ignored. const adminSettingSchema = getSettingsSchema().admin; - if (adminSettingSchema?.properties) { - const adminSchema = adminSettingSchema.properties; - const adminDefaults = getDefaultsFromSchema(adminSchema); + const adminSchema = adminSettingSchema.properties; + const adminDefaults = getDefaultsFromSchema(adminSchema); - // The final admin settings are the defaults overridden by remote settings. - // Any admin settings from files are ignored. - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - merged.admin = customDeepMerge( - (path: string[]) => getMergeStrategyForPath(['admin', ...path]), - adminDefaults, - this._remoteAdminSettings?.admin ?? {}, - ) as MergedSettings['admin']; - } + // The final admin settings are the defaults overridden by remote settings. + // Any admin settings from files are ignored. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + merged.admin = customDeepMerge( + (path: string[]) => getMergeStrategyForPath(['admin', ...path]), + adminDefaults, + this._remoteAdminSettings?.admin ?? {}, + ) as MergedSettings['admin']; return merged; } @@ -493,7 +487,7 @@ export class LoadedSettings { function findEnvFile(startDir: string): string | null { let currentDir = path.resolve(startDir); - while (true) { + for (;;) { // prefer gemini-specific .env under GEMINI_DIR const geminiEnvPath = path.join(currentDir, GEMINI_DIR, '.env'); if (fs.existsSync(geminiEnvPath)) { @@ -583,7 +577,7 @@ export function loadEnvironment( const parsedEnv = dotenv.parse(envFileContent); const excludedVars = - settings?.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS; + settings.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS; const isProjectEnvFile = !envFilePath.includes(GEMINI_DIR); for (const key in parsedEnv) { @@ -1085,7 +1079,7 @@ function migrateExperimentalSettings( }; const agentsOverrides = { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - ...((agentsSettings['overrides'] as Record) || {}), + ...(agentsSettings['overrides'] as Record), }; let modified = false; diff --git a/packages/cli/src/config/settings_repro.test.ts b/packages/cli/src/config/settings_repro.test.ts index 36495a99c4..d819e879ac 100644 --- a/packages/cli/src/config/settings_repro.test.ts +++ b/packages/cli/src/config/settings_repro.test.ts @@ -195,7 +195,7 @@ describe('Settings Repro', () => { // If it doesn't throw, check if it merged correctly. // The model.compressionThreshold should be present. // And model.name should probably be undefined or default, but certainly NOT { compressionThreshold: 0.8 } - expect(settings.merged.model?.compressionThreshold).toBe(0.8); - expect(typeof settings.merged.model?.name).not.toBe('object'); + expect(settings.merged.model.compressionThreshold).toBe(0.8); + expect(typeof settings.merged.model.name).not.toBe('object'); }); }); diff --git a/packages/cli/src/deferred.ts b/packages/cli/src/deferred.ts index 1864ec2cb5..9c1e41bc9d 100644 --- a/packages/cli/src/deferred.ts +++ b/packages/cli/src/deferred.ts @@ -33,7 +33,7 @@ export async function runDeferredCommand(settings: MergedSettings) { const adminSettings = settings.admin; const commandName = deferredCommand.commandName; - if (commandName === 'mcp' && adminSettings?.mcp?.enabled === false) { + if (commandName === 'mcp' && adminSettings.mcp.enabled === false) { coreEvents.emitFeedback( 'error', getAdminErrorMessage('MCP', undefined /* config */), @@ -44,7 +44,7 @@ export async function runDeferredCommand(settings: MergedSettings) { if ( commandName === 'extensions' && - adminSettings?.extensions?.enabled === false + adminSettings.extensions.enabled === false ) { coreEvents.emitFeedback( 'error', @@ -54,7 +54,7 @@ export async function runDeferredCommand(settings: MergedSettings) { process.exit(ExitCodes.FATAL_CONFIG_ERROR); } - if (commandName === 'skills' && adminSettings?.skills?.enabled === false) { + if (commandName === 'skills' && adminSettings.skills.enabled === false) { coreEvents.emitFeedback( 'error', getAdminErrorMessage('Agent skills', undefined /* config */), diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 88f9f404cd..5e8afe6f89 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -378,7 +378,7 @@ export async function main() { if ( (argv.allowedTools && argv.allowedTools.length > 0) || - (settings.merged.tools?.allowed && settings.merged.tools.allowed.length > 0) + (settings.merged.tools.allowed && settings.merged.tools.allowed.length > 0) ) { coreEvents.emitFeedback( 'warning', @@ -387,7 +387,7 @@ export async function main() { } if ( - settings.merged.tools?.exclude && + settings.merged.tools.exclude && settings.merged.tools.exclude.length > 0 ) { coreEvents.emitFeedback( @@ -748,7 +748,7 @@ export async function main() { ? SessionStartSource.Resume : SessionStartSource.Startup; - const hookSystem = config?.getHookSystem(); + const hookSystem = config.getHookSystem(); if (hookSystem) { const result = await hookSystem.fireSessionStartEvent(sessionStartSource); @@ -784,7 +784,7 @@ export async function main() { new UserPromptEvent( input.length, prompt_id, - config.getContentGeneratorConfig()?.authType, + config.getContentGeneratorConfig().authType, input, ), ); @@ -881,7 +881,7 @@ function setupAdminControlsListener() { type?: string; settings?: AdminControlsSettings; }; - if (message?.type === 'admin-settings' && message.settings) { + if (message.type === 'admin-settings' && message.settings) { if (config) { config.setRemoteAdminSettings(message.settings); } else { diff --git a/packages/cli/src/nonInteractiveCli.ts b/packages/cli/src/nonInteractiveCli.ts index c2cab72353..e332b3fe5e 100644 --- a/packages/cli/src/nonInteractiveCli.ts +++ b/packages/cli/src/nonInteractiveCli.ts @@ -135,7 +135,7 @@ export async function runNonInteractive({ key: { name?: string; ctrl?: boolean }, ) => { // Detect Ctrl+C: either ctrl+c key combo or raw character code 3 - if ((key && key.ctrl && key.name === 'c') || str === '\u0003') { + if ((key.ctrl && key.name === 'c') || str === '\u0003') { // Only handle once if (isAborting) { return; @@ -289,7 +289,7 @@ export async function runNonInteractive({ let currentMessages: Content[] = [{ role: 'user', parts: query }]; let turnCount = 0; - while (true) { + for (;;) { turnCount++; if ( config.getMaxSessionTurns() >= 0 && @@ -461,8 +461,8 @@ export async function runNonInteractive({ (tc) => tc.response.errorType === ToolErrorType.STOP_EXECUTION, ); - if (stopExecutionTool && stopExecutionTool.response.error) { - const stopMessage = `Agent execution stopped: ${stopExecutionTool.response.error.message}`; + if (stopExecutionTool) { + const stopMessage = `Agent execution stopped: ${stopExecutionTool.response.error?.message}`; if (config.getOutputFormat() === OutputFormat.TEXT) { process.stderr.write(`${stopMessage}\n`); diff --git a/packages/cli/src/nonInteractiveCliCommands.ts b/packages/cli/src/nonInteractiveCliCommands.ts index e09db71312..2e46a810f2 100644 --- a/packages/cli/src/nonInteractiveCliCommands.ts +++ b/packages/cli/src/nonInteractiveCliCommands.ts @@ -56,14 +56,14 @@ export const handleSlashCommand = async ( if (commandToExecute.action) { // Not used by custom commands but may be in the future. const sessionStats: SessionStatsState = { - sessionId: config?.getSessionId(), + sessionId: config.getSessionId(), sessionStartTime: new Date(), metrics: uiTelemetryService.getMetrics(), lastPromptTokenCount: 0, promptCount: 1, }; - const logger = new Logger(config?.getSessionId() || '', config?.storage); + const logger = new Logger(config.getSessionId() || '', config.storage); const context: CommandContext = { services: { diff --git a/packages/cli/src/services/BuiltinCommandLoader.ts b/packages/cli/src/services/BuiltinCommandLoader.ts index 31673e921a..fddb130a09 100644 --- a/packages/cli/src/services/BuiltinCommandLoader.ts +++ b/packages/cli/src/services/BuiltinCommandLoader.ts @@ -158,7 +158,7 @@ export class BuiltinCommandLoader implements ICommandLoader { themeCommand, toolsCommand, ...(this.config?.isSkillsSupportEnabled() - ? this.config?.getSkillManager()?.isAdminEnabled() === false + ? this.config.getSkillManager().isAdminEnabled() === false ? [ { name: 'skills', diff --git a/packages/cli/src/services/FileCommandLoader.ts b/packages/cli/src/services/FileCommandLoader.ts index fb27327ead..472955fab2 100644 --- a/packages/cli/src/services/FileCommandLoader.ts +++ b/packages/cli/src/services/FileCommandLoader.ts @@ -126,7 +126,7 @@ export class FileCommandLoader implements ICommandLoader { if ( !signal.aborted && // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - (error as { code?: string })?.code !== 'ENOENT' + (error as { code?: string }).code !== 'ENOENT' ) { coreEvents.emitFeedback( 'error', diff --git a/packages/cli/src/services/McpPromptLoader.ts b/packages/cli/src/services/McpPromptLoader.ts index f61eed9184..9e79d79fb7 100644 --- a/packages/cli/src/services/McpPromptLoader.ts +++ b/packages/cli/src/services/McpPromptLoader.ts @@ -62,7 +62,7 @@ export class McpPromptLoader implements ICommandLoader { let helpMessage = `Arguments for "${prompt.name}":\n\n`; if (prompt.arguments && prompt.arguments.length > 0) { helpMessage += `You can provide arguments by name (e.g., --argName="value") or by position.\n\n`; - helpMessage += `e.g., ${prompt.name} ${prompt.arguments?.map((_) => `"foo"`)} is equivalent to ${prompt.name} ${prompt.arguments?.map((arg) => `--${arg.name}="foo"`)}\n\n`; + helpMessage += `e.g., ${prompt.name} ${prompt.arguments.map((_) => `"foo"`)} is equivalent to ${prompt.name} ${prompt.arguments.map((arg) => `--${arg.name}="foo"`)}\n\n`; } for (const arg of prompt.arguments) { helpMessage += ` --${arg.name}\n`; @@ -123,7 +123,7 @@ export class McpPromptLoader implements ICommandLoader { }; } - const maybeContent = result.messages?.[0]?.content; + const maybeContent = result.messages[0]?.content; if (maybeContent.type !== 'text') { return { type: 'message', diff --git a/packages/cli/src/services/prompt-processors/shellProcessor.ts b/packages/cli/src/services/prompt-processors/shellProcessor.ts index 4c8369f664..507803ab67 100644 --- a/packages/cli/src/services/prompt-processors/shellProcessor.ts +++ b/packages/cli/src/services/prompt-processors/shellProcessor.ts @@ -119,7 +119,7 @@ export class ShellProcessor implements IPromptProcessor { if (!command) continue; - if (context.session.sessionShellAllowlist?.has(command)) { + if (context.session.sessionShellAllowlist.has(command)) { continue; } diff --git a/packages/cli/src/test-utils/AppRig.tsx b/packages/cli/src/test-utils/AppRig.tsx index 3ff65c4067..a0668caaca 100644 --- a/packages/cli/src/test-utils/AppRig.tsx +++ b/packages/cli/src/test-utils/AppRig.tsx @@ -312,7 +312,7 @@ export class AppRig { const details = call.confirmationDetails; const title = 'title' in details ? details.title : ''; const toolDisplayName = - call.tool?.displayName || title.replace(/^Confirm:\s*/, ''); + call.tool.displayName || title.replace(/^Confirm:\s*/, ''); if (!this.pendingConfirmations.has(call.correlationId)) { this.pendingConfirmations.set(call.correlationId, { toolName: call.request.name, @@ -469,7 +469,7 @@ export class AppRig { } = options; const start = Date.now(); - while (true) { + for (;;) { if (await predicate()) return; if (Date.now() - start > timeout) { @@ -622,7 +622,7 @@ export class AppRig { onConfirmation?: (confirmation: PendingConfirmation) => void | boolean, timeout = 60000, ) { - while (true) { + for (;;) { const event = await this.waitForNextEvent(timeout); if (event.type === 'idle') { break; @@ -710,7 +710,7 @@ export class AppRig { if (this.config) { const recordingService = this.config .getGeminiClient() - ?.getChatRecordingService(); + .getChatRecordingService(); if (recordingService) { // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion (recordingService as any).conversationFile = null; diff --git a/packages/cli/src/test-utils/async.ts b/packages/cli/src/test-utils/async.ts index 3069c3f41a..f04e5d7dc0 100644 --- a/packages/cli/src/test-utils/async.ts +++ b/packages/cli/src/test-utils/async.ts @@ -18,7 +18,7 @@ export async function waitFor( ): Promise { const startTime = Date.now(); - while (true) { + for (;;) { try { await assertion(); return; diff --git a/packages/cli/src/test-utils/svg.ts b/packages/cli/src/test-utils/svg.ts index 92d3f53c2f..4169706696 100644 --- a/packages/cli/src/test-utils/svg.ts +++ b/packages/cli/src/test-utils/svg.ts @@ -47,7 +47,7 @@ export const generateSvgForTerminal = (terminal: Terminal): string => { const b = v[c % 6]; const g = v[Math.floor(c / 6) % 6]; const r = v[Math.floor(c / 36) % 6]; - return `#${[r, g, b].map((x) => x?.toString(16).padStart(2, '0')).join('')}`; + return `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`; } else if (colorCode >= 232 && colorCode <= 255) { const gray = 8 + (colorCode - 232) * 10; const hex = gray.toString(16).padStart(2, '0'); diff --git a/packages/cli/src/ui/IdeIntegrationNudge.tsx b/packages/cli/src/ui/IdeIntegrationNudge.tsx index 409a6469f6..1895bca656 100644 --- a/packages/cli/src/ui/IdeIntegrationNudge.tsx +++ b/packages/cli/src/ui/IdeIntegrationNudge.tsx @@ -73,12 +73,8 @@ export function IdeIntegrationNudge({ ]; const installText = isExtensionPreInstalled - ? `If you select Yes, the CLI will have access to your open files and display diffs directly in ${ - ideName ?? 'your editor' - }.` - : `If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in ${ - ideName ?? 'your editor' - }.`; + ? `If you select Yes, the CLI will have access to your open files and display diffs directly in ${ideName}.` + : `If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in ${ideName}.`; return ( {'> '} - {`Do you want to connect ${ideName ?? 'your editor'} to Gemini CLI?`} + {`Do you want to connect ${ideName} to Gemini CLI?`} {installText} diff --git a/packages/cli/src/ui/auth/ApiAuthDialog.tsx b/packages/cli/src/ui/auth/ApiAuthDialog.tsx index 2caad6fd27..f2aaba7b0f 100644 --- a/packages/cli/src/ui/auth/ApiAuthDialog.tsx +++ b/packages/cli/src/ui/auth/ApiAuthDialog.tsx @@ -44,7 +44,7 @@ export function ApiAuthDialog({ const buffer = useTextBuffer({ initialText: initialApiKey || '', - initialCursorOffset: initialApiKey?.length || 0, + initialCursorOffset: initialApiKey.length || 0, viewport: { width: viewportWidth, height: 4, diff --git a/packages/cli/src/ui/commands/aboutCommand.ts b/packages/cli/src/ui/commands/aboutCommand.ts index cf21d9b0d5..fc7f03ddd0 100644 --- a/packages/cli/src/ui/commands/aboutCommand.ts +++ b/packages/cli/src/ui/commands/aboutCommand.ts @@ -68,5 +68,5 @@ async function getIdeClientName(context: CommandContext) { return ''; } const ideClient = await IdeClient.getInstance(); - return ideClient?.getDetectedIdeDisplayName() ?? ''; + return ideClient.getDetectedIdeDisplayName() ?? ''; } diff --git a/packages/cli/src/ui/commands/agentsCommand.ts b/packages/cli/src/ui/commands/agentsCommand.ts index a7161dfb77..52053d1892 100644 --- a/packages/cli/src/ui/commands/agentsCommand.ts +++ b/packages/cli/src/ui/commands/agentsCommand.ts @@ -91,7 +91,7 @@ async function enableAction( const allAgents = agentRegistry.getAllAgentNames(); const overrides = settings.merged.agents.overrides; const disabledAgents = Object.keys(overrides).filter( - (name) => overrides[name]?.enabled === false, + (name) => overrides[name].enabled === false, ); if (allAgents.includes(agentName) && !disabledAgents.includes(agentName)) { @@ -167,7 +167,7 @@ async function disableAction( const allAgents = agentRegistry.getAllAgentNames(); const overrides = settings.merged.agents.overrides; const disabledAgents = Object.keys(overrides).filter( - (name) => overrides[name]?.enabled === false, + (name) => overrides[name].enabled === false, ); if (disabledAgents.includes(agentName)) { @@ -271,7 +271,7 @@ function completeAgentsToEnable(context: CommandContext, partialArg: string) { const overrides = settings.merged.agents.overrides; const disabledAgents = Object.entries(overrides) - .filter(([_, override]) => override?.enabled === false) + .filter(([_, override]) => override.enabled === false) .map(([name]) => name); return disabledAgents.filter((name) => name.startsWith(partialArg)); @@ -291,7 +291,7 @@ function completeAllAgents(context: CommandContext, partialArg: string) { if (!config) return []; const agentRegistry = config.getAgentRegistry(); - const allAgents = agentRegistry?.getAllDiscoveredAgentNames() ?? []; + const allAgents = agentRegistry.getAllDiscoveredAgentNames() ?? []; return allAgents.filter((name: string) => name.startsWith(partialArg)); } diff --git a/packages/cli/src/ui/commands/authCommand.ts b/packages/cli/src/ui/commands/authCommand.ts index 0314555baf..eb4cfb7ac8 100644 --- a/packages/cli/src/ui/commands/authCommand.ts +++ b/packages/cli/src/ui/commands/authCommand.ts @@ -37,7 +37,7 @@ const authLogoutCommand: SlashCommand = { undefined, ); // Strip thoughts from history instead of clearing completely - context.services.config?.getGeminiClient()?.stripThoughtsFromHistory(); + context.services.config?.getGeminiClient().stripThoughtsFromHistory(); // Return logout action to signal explicit state change return { type: 'logout', diff --git a/packages/cli/src/ui/commands/bugCommand.ts b/packages/cli/src/ui/commands/bugCommand.ts index 26ddb7e850..0c3833cb9f 100644 --- a/packages/cli/src/ui/commands/bugCommand.ts +++ b/packages/cli/src/ui/commands/bugCommand.ts @@ -54,7 +54,7 @@ export const bugCommand: SlashCommand = { const kittyProtocol = terminalCapabilityManager.isKittyProtocolEnabled() ? 'Supported' : 'Unsupported'; - const authType = config?.getContentGeneratorConfig()?.authType || 'Unknown'; + const authType = config?.getContentGeneratorConfig().authType || 'Unknown'; let info = ` * **CLI Version:** ${cliVersion} @@ -73,13 +73,13 @@ export const bugCommand: SlashCommand = { info += `* **IDE Client:** ${ideClient}\n`; } - const chat = config?.getGeminiClient()?.getChat(); + const chat = config?.getGeminiClient().getChat(); const history = chat?.getHistory() || []; let historyFileMessage = ''; let problemValue = bugDescription; if (history.length > INITIAL_HISTORY_LENGTH) { - const tempDir = config?.storage?.getProjectTempDir(); + const tempDir = config?.storage.getProjectTempDir(); if (tempDir) { const historyFileName = `bug-report-history-${Date.now()}.json`; const historyFilePath = path.join(tempDir, historyFileName); diff --git a/packages/cli/src/ui/commands/chatCommand.ts b/packages/cli/src/ui/commands/chatCommand.ts index e1969fff67..a8fca0a369 100644 --- a/packages/cli/src/ui/commands/chatCommand.ts +++ b/packages/cli/src/ui/commands/chatCommand.ts @@ -34,7 +34,7 @@ const getSavedChatTags = async ( mtSortDesc: boolean, ): Promise => { const cfg = context.services.config; - const geminiDir = cfg?.storage?.getProjectTempDir(); + const geminiDir = cfg?.storage.getProjectTempDir(); if (!geminiDir) { return []; } @@ -123,7 +123,7 @@ const saveCommand: SlashCommand = { } } - const chat = config?.getGeminiClient()?.getChat(); + const chat = config?.getGeminiClient().getChat(); if (!chat) { return { type: 'message', @@ -134,7 +134,7 @@ const saveCommand: SlashCommand = { const history = chat.getHistory(); if (history.length > INITIAL_HISTORY_LENGTH) { - const authType = config?.getContentGeneratorConfig()?.authType; + const authType = config?.getContentGeneratorConfig().authType; await logger.saveCheckpoint({ history, authType }, tag); return { type: 'message', @@ -183,7 +183,7 @@ const resumeCommand: SlashCommand = { }; } - const currentAuthType = config?.getContentGeneratorConfig()?.authType; + const currentAuthType = config?.getContentGeneratorConfig().authType; if ( checkpoint.authType && currentAuthType && @@ -296,7 +296,7 @@ const shareCommand: SlashCommand = { }; } - const chat = context.services.config?.getGeminiClient()?.getChat(); + const chat = context.services.config?.getGeminiClient().getChat(); if (!chat) { return { type: 'message', diff --git a/packages/cli/src/ui/commands/clearCommand.ts b/packages/cli/src/ui/commands/clearCommand.ts index 385d3f9540..85efedc9ce 100644 --- a/packages/cli/src/ui/commands/clearCommand.ts +++ b/packages/cli/src/ui/commands/clearCommand.ts @@ -25,7 +25,7 @@ export const clearCommand: SlashCommand = { const config = context.services.config; const chatRecordingService = context.services.config ?.getGeminiClient() - ?.getChat() + .getChat() .getChatRecordingService(); // Fire SessionEnd hook before clearing diff --git a/packages/cli/src/ui/commands/compressCommand.ts b/packages/cli/src/ui/commands/compressCommand.ts index 3bb5b34383..7221ebad88 100644 --- a/packages/cli/src/ui/commands/compressCommand.ts +++ b/packages/cli/src/ui/commands/compressCommand.ts @@ -43,7 +43,7 @@ export const compressCommand: SlashCommand = { const promptId = `compress-${Date.now()}`; const compressed = await context.services.config ?.getGeminiClient() - ?.tryCompressChat(promptId, true); + .tryCompressChat(promptId, true); if (compressed) { ui.addItem( { diff --git a/packages/cli/src/ui/commands/copyCommand.ts b/packages/cli/src/ui/commands/copyCommand.ts index c2c6ab13d1..394b6eb557 100644 --- a/packages/cli/src/ui/commands/copyCommand.ts +++ b/packages/cli/src/ui/commands/copyCommand.ts @@ -15,7 +15,7 @@ export const copyCommand: SlashCommand = { kind: CommandKind.BUILT_IN, autoExecute: true, action: async (context, _args): Promise => { - const chat = context.services.config?.getGeminiClient()?.getChat(); + const chat = context.services.config?.getGeminiClient().getChat(); const history = chat?.getHistory(); // Get the last message from the AI (model role) diff --git a/packages/cli/src/ui/commands/extensionsCommand.ts b/packages/cli/src/ui/commands/extensionsCommand.ts index 842a680a14..93ff45d3ce 100644 --- a/packages/cli/src/ui/commands/extensionsCommand.ts +++ b/packages/cli/src/ui/commands/extensionsCommand.ts @@ -236,7 +236,7 @@ async function restartAction( if (failures.length < extensionsToRestart.length) { try { await context.services.config?.reloadSkills(); - await context.services.config?.getAgentRegistry()?.reload(); + await context.services.config?.getAgentRegistry().reload(); } catch (error) { context.ui.addItem({ type: MessageType.ERROR, @@ -271,7 +271,7 @@ async function exploreAction( context: CommandContext, ): Promise { const settings = context.services.settings.merged; - const useRegistryUI = settings.experimental?.extensionRegistry; + const useRegistryUI = settings.experimental.extensionRegistry; if (useRegistryUI) { const extensionManager = context.services.config?.getExtensionLoader(); diff --git a/packages/cli/src/ui/commands/ideCommand.ts b/packages/cli/src/ui/commands/ideCommand.ts index 1f726f90e5..94b63c3cef 100644 --- a/packages/cli/src/ui/commands/ideCommand.ts +++ b/packages/cli/src/ui/commands/ideCommand.ts @@ -45,7 +45,7 @@ function getIdeStatusMessage(ideClient: IdeClient): { }; default: { let content = `🔴 Disconnected`; - if (connection?.details) { + if (connection.details) { content += `: ${connection.details}`; } return { @@ -107,7 +107,7 @@ async function getIdeStatusMessageWithFiles(ideClient: IdeClient): Promise<{ }; default: { let content = `🔴 Disconnected`; - if (connection?.details) { + if (connection.details) { content += `: ${connection.details}`; } return { diff --git a/packages/cli/src/ui/commands/mcpCommand.ts b/packages/cli/src/ui/commands/mcpCommand.ts index e488db780f..b8037aa3b8 100644 --- a/packages/cli/src/ui/commands/mcpCommand.ts +++ b/packages/cli/src/ui/commands/mcpCommand.ts @@ -139,7 +139,7 @@ const authCommand: SlashCommand = { } // Update the client with the new tools const geminiClient = config.getGeminiClient(); - if (geminiClient?.isInitialized()) { + if (geminiClient.isInitialized()) { await geminiClient.setTools(); } @@ -361,7 +361,7 @@ const refreshCommand: SlashCommand = { // Update the client with the new tools const geminiClient = config.getGeminiClient(); - if (geminiClient?.isInitialized()) { + if (geminiClient.isInitialized()) { await geminiClient.setTools(); } @@ -419,9 +419,9 @@ async function handleEnableDisable( if (enable) { const settings = loadSettings(); const result = await canLoadServer(name, { - adminMcpEnabled: settings.merged.admin?.mcp?.enabled ?? true, - allowedList: settings.merged.mcp?.allowed, - excludedList: settings.merged.mcp?.excluded, + adminMcpEnabled: settings.merged.admin.mcp.enabled ?? true, + allowedList: settings.merged.mcp.allowed, + excludedList: settings.merged.mcp.excluded, }); if ( !result.allowed && @@ -465,7 +465,7 @@ async function handleEnableDisable( ); await mcpClientManager.restart(); } - if (config.getGeminiClient()?.isInitialized()) + if (config.getGeminiClient().isInitialized()) await config.getGeminiClient().setTools(); context.ui.reloadCommands(); diff --git a/packages/cli/src/ui/commands/restoreCommand.ts b/packages/cli/src/ui/commands/restoreCommand.ts index 3051588e7c..ec20bb17c5 100644 --- a/packages/cli/src/ui/commands/restoreCommand.ts +++ b/packages/cli/src/ui/commands/restoreCommand.ts @@ -116,7 +116,7 @@ async function restoreAction( } else if (action.type === 'load_history' && loadHistory) { loadHistory(action.history); if (action.clientHistory) { - config?.getGeminiClient()?.setHistory(action.clientHistory); + config?.getGeminiClient().setHistory(action.clientHistory); } } } diff --git a/packages/cli/src/ui/commands/setupGithubCommand.ts b/packages/cli/src/ui/commands/setupGithubCommand.ts index a125b1eda4..24babdbb20 100644 --- a/packages/cli/src/ui/commands/setupGithubCommand.ts +++ b/packages/cli/src/ui/commands/setupGithubCommand.ts @@ -227,7 +227,7 @@ export const setupGithubCommand: SlashCommand = { } // Get the latest release tag from GitHub - const proxy = context?.services?.config?.getProxy(); + const proxy = context.services.config?.getProxy(); const releaseTag = await getLatestGitHubRelease(proxy); const readmeUrl = `https://github.com/google-github-actions/run-gemini-cli/blob/${releaseTag}/README.md#quick-start`; diff --git a/packages/cli/src/ui/components/AboutBox.tsx b/packages/cli/src/ui/components/AboutBox.tsx index 7ea744b0fe..3158338e73 100644 --- a/packages/cli/src/ui/components/AboutBox.tsx +++ b/packages/cli/src/ui/components/AboutBox.tsx @@ -61,7 +61,7 @@ export const AboutBox: React.FC = ({ {cliVersion} - {GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && ( + {!['N/A'].includes(GIT_COMMIT_INFO) && ( diff --git a/packages/cli/src/ui/components/AgentConfigDialog.tsx b/packages/cli/src/ui/components/AgentConfigDialog.tsx index 4079c6df77..213b81acd9 100644 --- a/packages/cli/src/ui/components/AgentConfigDialog.tsx +++ b/packages/cli/src/ui/components/AgentConfigDialog.tsx @@ -161,25 +161,25 @@ function getFieldDefaultFromDefinition( return !definition.experimental; // Experimental agents default to disabled } if (field.key === 'model') { - return definition.modelConfig?.model ?? 'inherit'; + return definition.modelConfig.model ?? 'inherit'; } if (field.key === 'temperature') { - return definition.modelConfig?.generateContentConfig?.temperature; + return definition.modelConfig.generateContentConfig?.temperature; } if (field.key === 'topP') { - return definition.modelConfig?.generateContentConfig?.topP; + return definition.modelConfig.generateContentConfig?.topP; } if (field.key === 'topK') { - return definition.modelConfig?.generateContentConfig?.topK; + return definition.modelConfig.generateContentConfig?.topK; } if (field.key === 'maxOutputTokens') { - return definition.modelConfig?.generateContentConfig?.maxOutputTokens; + return definition.modelConfig.generateContentConfig?.maxOutputTokens; } if (field.key === 'maxTimeMinutes') { - return definition.runConfig?.maxTimeMinutes; + return definition.runConfig.maxTimeMinutes; } if (field.key === 'maxTurns') { - return definition.runConfig?.maxTurns; + return definition.runConfig.maxTurns; } return field.defaultValue; diff --git a/packages/cli/src/ui/components/AskUserDialog.tsx b/packages/cli/src/ui/components/AskUserDialog.tsx index 9606513510..23cce05fda 100644 --- a/packages/cli/src/ui/components/AskUserDialog.tsx +++ b/packages/cli/src/ui/components/AskUserDialog.tsx @@ -1083,7 +1083,7 @@ export const AskUserDialog: React.FC = ({ const currentQuestion = questions[currentQuestionIndex]; const effectiveQuestion = useMemo(() => { - if (currentQuestion?.type === 'yesno') { + if (currentQuestion.type === 'yesno') { return { ...currentQuestion, options: [ diff --git a/packages/cli/src/ui/components/CliSpinner.tsx b/packages/cli/src/ui/components/CliSpinner.tsx index 66cb7a0281..e14d22c39c 100644 --- a/packages/cli/src/ui/components/CliSpinner.tsx +++ b/packages/cli/src/ui/components/CliSpinner.tsx @@ -13,7 +13,7 @@ export type SpinnerProps = ComponentProps; export const CliSpinner = (props: SpinnerProps) => { const settings = useSettings(); - const shouldShow = settings.merged.ui?.showSpinner !== false; + const shouldShow = settings.merged.ui.showSpinner !== false; useEffect(() => { if (shouldShow) { diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 51c879e772..1b55ee4159 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -79,7 +79,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { hasPendingToolConfirmation || Boolean(uiState.commandConfirmationRequest) || Boolean(uiState.authConsentRequest) || - (uiState.confirmUpdateExtensionRequests?.length ?? 0) > 0 || + (uiState.confirmUpdateExtensionRequests.length ?? 0) > 0 || Boolean(uiState.loopDetectionConfirmationRequest) || Boolean(uiState.quota.proQuotaRequest) || Boolean(uiState.quota.validationRequest) || diff --git a/packages/cli/src/ui/components/DetailedMessagesDisplay.tsx b/packages/cli/src/ui/components/DetailedMessagesDisplay.tsx index ff88afa888..2e2b57db8c 100644 --- a/packages/cli/src/ui/components/DetailedMessagesDisplay.tsx +++ b/packages/cli/src/ui/components/DetailedMessagesDisplay.tsx @@ -40,7 +40,7 @@ export const DetailedMessagesDisplay: React.FC< if (textWidth <= 0) { return 1; } - const lines = Math.ceil((msg.content?.length || 1) / textWidth); + const lines = Math.ceil((msg.content.length || 1) / textWidth); return Math.max(1, lines); }, [width, messages], diff --git a/packages/cli/src/ui/components/DialogManager.tsx b/packages/cli/src/ui/components/DialogManager.tsx index 5119c1b343..1080aa65d8 100644 --- a/packages/cli/src/ui/components/DialogManager.tsx +++ b/packages/cli/src/ui/components/DialogManager.tsx @@ -255,7 +255,7 @@ export const DialogManager = ({ onClose={uiActions.closeAgentConfigDialog} onSave={async () => { // Reload agent registry to pick up changes - const agentRegistry = config?.getAgentRegistry(); + const agentRegistry = config.getAgentRegistry(); if (agentRegistry) { await agentRegistry.reload(); } diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index ad057ca8c2..fc0a2351fa 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -148,7 +148,7 @@ const DOUBLE_TAB_CLEAN_UI_TOGGLE_WINDOW_MS = 350; * Returns true if a toggle action was performed or hint was shown, false otherwise. */ export function tryTogglePasteExpansion(buffer: TextBuffer): boolean { - if (!buffer.pastedContent || Object.keys(buffer.pastedContent).length === 0) { + if (Object.keys(buffer.pastedContent).length === 0) { return false; } @@ -344,13 +344,11 @@ export const InputPrompt: React.FC = ({ const handleSubmitAndClear = useCallback( (submittedValue: string) => { let processedValue = submittedValue; - if (buffer.pastedContent) { - // Replace placeholders like [Pasted Text: 6 lines] with actual content - processedValue = processedValue.replace( - PASTED_TEXT_PLACEHOLDER_REGEX, - (match) => buffer.pastedContent[match] || match, - ); - } + // Replace placeholders like [Pasted Text: 6 lines] with actual content + processedValue = processedValue.replace( + PASTED_TEXT_PLACEHOLDER_REGEX, + (match) => buffer.pastedContent[match] || match, + ); if (shellModeActive) { shellHistory.addCommandToHistory(processedValue); @@ -487,7 +485,7 @@ export const InputPrompt: React.FC = ({ } } - if (settings.experimental?.useOSC52Paste) { + if (settings.experimental.useOSC52Paste) { stdout.write('\x1b]52;c;?\x07'); } else { const textToInsert = await clipboardy.read(); @@ -1537,7 +1535,6 @@ export const InputPrompt: React.FC = ({ const absoluteVisualIdx = scrollVisualRow + visualIdxInRenderedSet; const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx]; - if (!mapEntry) return null; const cursorVisualRow = cursorVisualRowAbsolute - scrollVisualRow; diff --git a/packages/cli/src/ui/components/LoadingIndicator.tsx b/packages/cli/src/ui/components/LoadingIndicator.tsx index f9fff9fa9b..9989ed8837 100644 --- a/packages/cli/src/ui/components/LoadingIndicator.tsx +++ b/packages/cli/src/ui/components/LoadingIndicator.tsx @@ -57,7 +57,7 @@ export const LoadingIndicator: React.FC = ({ : currentLoadingPhrase; const hasThoughtIndicator = currentLoadingPhrase !== INTERACTIVE_SHELL_WAITING_PHRASE && - Boolean(thought?.subject?.trim()); + Boolean(thought?.subject.trim()); const thinkingIndicator = hasThoughtIndicator ? '💬 ' : ''; const cancelAndTimerContent = diff --git a/packages/cli/src/ui/components/MainContent.tsx b/packages/cli/src/ui/components/MainContent.tsx index 7386a246e7..10a2eb280b 100644 --- a/packages/cli/src/ui/components/MainContent.tsx +++ b/packages/cli/src/ui/components/MainContent.tsx @@ -118,7 +118,7 @@ export const MainContent = () => { isExpandable={true} /> ))} - {showConfirmationQueue && confirmingTool && ( + {showConfirmationQueue && ( )} diff --git a/packages/cli/src/ui/components/ModelDialog.tsx b/packages/cli/src/ui/components/ModelDialog.tsx index 7d7fea4d86..3ba06606ba 100644 --- a/packages/cli/src/ui/components/ModelDialog.tsx +++ b/packages/cli/src/ui/components/ModelDialog.tsx @@ -42,7 +42,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element { const preferredModel = config?.getModel() || DEFAULT_GEMINI_MODEL_AUTO; const shouldShowPreviewModels = config?.getHasAccessToPreviewModel(); - const useGemini31 = config?.getGemini31LaunchedSync?.() ?? false; + const useGemini31 = config?.getGemini31LaunchedSync() ?? false; const selectedAuthType = settings.merged.security.auth.selectedType; const useCustomToolModel = useGemini31 && selectedAuthType === AuthType.USE_GEMINI; diff --git a/packages/cli/src/ui/components/ModelStatsDisplay.tsx b/packages/cli/src/ui/components/ModelStatsDisplay.tsx index eec58e9968..840b5c29b6 100644 --- a/packages/cli/src/ui/components/ModelStatsDisplay.tsx +++ b/packages/cli/src/ui/components/ModelStatsDisplay.tsx @@ -254,7 +254,7 @@ export const ModelStatsDisplay: React.FC = ({ isSubtle: true, }; activeModels.forEach(([name, metrics]) => { - const roleMetrics = metrics.roles?.[role]; + const roleMetrics = metrics.roles[role]; if (roleMetrics) { row[name] = getValue(roleMetrics); } else { diff --git a/packages/cli/src/ui/components/SessionBrowser.tsx b/packages/cli/src/ui/components/SessionBrowser.tsx index 154ad62522..85d8471dd7 100644 --- a/packages/cli/src/ui/components/SessionBrowser.tsx +++ b/packages/cli/src/ui/components/SessionBrowser.tsx @@ -201,7 +201,7 @@ const findTextMatches = ( const lowerContent = m.toLowerCase(); let startIndex = 0; - while (true) { + for (;;) { const matchIndex = lowerContent.indexOf(lowerQuery, startIndex); if (matchIndex === -1) break; diff --git a/packages/cli/src/ui/components/StatsDisplay.tsx b/packages/cli/src/ui/components/StatsDisplay.tsx index 65169f6d74..96ae286b5b 100644 --- a/packages/cli/src/ui/components/StatsDisplay.tsx +++ b/packages/cli/src/ui/components/StatsDisplay.tsx @@ -416,7 +416,7 @@ export const StatsDisplay: React.FC = ({ const computed = computeSessionStats(metrics); const settings = useSettings(); const config = useConfig(); - const useGemini3_1 = config.getGemini31LaunchedSync?.() ?? false; + const useGemini3_1 = config.getGemini31LaunchedSync() ?? false; const useCustomToolModel = useGemini3_1 && config.getContentGeneratorConfig().authType === AuthType.USE_GEMINI; diff --git a/packages/cli/src/ui/components/ToolConfirmationQueue.tsx b/packages/cli/src/ui/components/ToolConfirmationQueue.tsx index 3fb1cc8c6f..d46c2e5a3f 100644 --- a/packages/cli/src/ui/components/ToolConfirmationQueue.tsx +++ b/packages/cli/src/ui/components/ToolConfirmationQueue.tsx @@ -63,8 +63,8 @@ export const ToolConfirmationQueue: React.FC = ({ : Math.floor(terminalHeight * 0.5); const isRoutine = - tool.confirmationDetails?.type === 'ask_user' || - tool.confirmationDetails?.type === 'exit_plan_mode'; + tool.confirmationDetails.type === 'ask_user' || + tool.confirmationDetails.type === 'exit_plan_mode'; const borderColor = isRoutine ? theme.status.success : theme.status.warning; const hideToolIdentity = isRoutine; diff --git a/packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx b/packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx index c10104591d..0545c182bc 100644 --- a/packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx +++ b/packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx @@ -404,7 +404,7 @@ export function BaseSettingsDialog({ } // Number keys for quick edit on number fields - if (currentItem?.type === 'number' && /^[0-9]$/.test(key.sequence)) { + if (currentItem.type === 'number' && /^[0-9]$/.test(key.sequence)) { startEditing(currentItem.key, key.sequence); return true; } diff --git a/packages/cli/src/ui/components/shared/text-buffer.ts b/packages/cli/src/ui/components/shared/text-buffer.ts index 71ee40b642..d0535aab51 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.ts @@ -2041,7 +2041,7 @@ function textBufferReducerLogic( if (visualToLogicalMap[newVisualRow]) { const [logRow, logicalStartCol] = visualToLogicalMap[newVisualRow]; const transformedToLogicalMap = - visualLayout.transformedToLogicalMaps?.[logRow] ?? []; + visualLayout.transformedToLogicalMaps[logRow] ?? []; let transformedStartCol = 0; while ( transformedStartCol < transformedToLogicalMap.length && @@ -3324,12 +3324,11 @@ export function useTextBuffer({ if (visualToLogicalMap[clampedVisRow]) { const [logRow] = visualToLogicalMap[clampedVisRow]; - const transformedToLogicalMap = - transformedToLogicalMaps?.[logRow] ?? []; + const transformedToLogicalMap = transformedToLogicalMaps[logRow] ?? []; // Where does this visual line begin within the transformed line? const startColInTransformed = - visualToTransformedMap?.[clampedVisRow] ?? 0; + visualToTransformedMap[clampedVisRow] ?? 0; // Handle wide characters: convert visual X position to character offset const codePoints = toCodePoints(visualLine); @@ -3397,11 +3396,10 @@ export function useTextBuffer({ } const [logRow] = visualToLogicalMap[clampedVisRow]; - const transformedToLogicalMap = transformedToLogicalMaps?.[logRow] ?? []; + const transformedToLogicalMap = transformedToLogicalMaps[logRow] ?? []; // Where does this visual line begin within the transformed line? - const startColInTransformed = - visualToTransformedMap?.[clampedVisRow] ?? 0; + const startColInTransformed = visualToTransformedMap[clampedVisRow] ?? 0; // Handle wide characters: convert visual X position to character offset const codePoints = toCodePoints(visualLine); diff --git a/packages/cli/src/ui/components/triage/TriageDuplicates.tsx b/packages/cli/src/ui/components/triage/TriageDuplicates.tsx index 878cacfed0..096ba990b7 100644 --- a/packages/cli/src/ui/components/triage/TriageDuplicates.tsx +++ b/packages/cli/src/ui/components/triage/TriageDuplicates.tsx @@ -208,7 +208,7 @@ I am triaging a GitHub issue labeled as 'possible-duplicate'. I need to decide i ID: #${issue.number} Title: ${issue.title} -Author: ${issue.author?.login} +Author: ${issue.author.login} Reactions: ${getReactionCount(issue)} Body: ${issue.body.slice(0, 8000)} @@ -221,7 +221,7 @@ ${candidates ID: #${c.number} Title: ${c.title} -Author: ${c.author?.login} +Author: ${c.author.login} Reactions: ${getReactionCount(c)} Body: ${c.body.slice(0, 4000)} @@ -815,7 +815,7 @@ Return a JSON object with: {selectedCandidate.title} - Author: {selectedCandidate.author?.login} | 👍{' '} + Author: {selectedCandidate.author.login} | 👍{' '} {getReactionCount(selectedCandidate)} {selectedCandidate.url} @@ -874,7 +874,7 @@ Return a JSON object with: - {currentIssue.title} - Author: {currentIssue.author?.login} | 👍{' '} + Author: {currentIssue.author.login} | 👍{' '} {getReactionCount(currentIssue)} diff --git a/packages/cli/src/ui/components/triage/TriageIssues.tsx b/packages/cli/src/ui/components/triage/TriageIssues.tsx index 595384a124..e4b195edc4 100644 --- a/packages/cli/src/ui/components/triage/TriageIssues.tsx +++ b/packages/cli/src/ui/components/triage/TriageIssues.tsx @@ -183,7 +183,7 @@ I am triaging GitHub issues for the Gemini CLI project. I need to identify issue ID: #${issue.number} Title: ${issue.title} -Author: ${issue.author?.login} +Author: ${issue.author.login} Labels: ${issue.labels.map((l) => l.name).join(', ')} Body: ${issue.body.slice(0, 8000)} @@ -566,7 +566,7 @@ Return a JSON object with: - {currentIssue.title} - Author: {currentIssue.author?.login} | 👍{' '} + Author: {currentIssue.author.login} | 👍{' '} {getReactionCount(currentIssue)} diff --git a/packages/cli/src/ui/contexts/KeypressContext.tsx b/packages/cli/src/ui/contexts/KeypressContext.tsx index 7d1881644d..cb4d35bee4 100644 --- a/packages/cli/src/ui/contexts/KeypressContext.tsx +++ b/packages/cli/src/ui/contexts/KeypressContext.tsx @@ -214,7 +214,7 @@ function bufferBackslashEnter( keypressHandler: KeypressHandler, ): KeypressHandler { const bufferer = (function* (): Generator { - while (true) { + for (;;) { const key = yield; if (key == null) { @@ -260,7 +260,7 @@ function bufferBackslashEnter( */ function bufferPaste(keypressHandler: KeypressHandler): KeypressHandler { const bufferer = (function* (): Generator { - while (true) { + for (;;) { let key = yield; if (key === null) { @@ -271,7 +271,7 @@ function bufferPaste(keypressHandler: KeypressHandler): KeypressHandler { } let buffer = ''; - while (true) { + for (;;) { const timeoutId = setTimeout(() => bufferer.next(null), PASTE_TIMEOUT); key = yield; clearTimeout(timeoutId); @@ -340,7 +340,7 @@ function* emitKeys( const lcAll = process.env['LC_ALL'] || ''; const isGreek = lang.startsWith('el') || lcAll.startsWith('el'); - while (true) { + for (;;) { let ch = yield; let sequence = ch; let escaped = false; @@ -376,7 +376,7 @@ function* emitKeys( let buffer = ''; // Read until BEL, `ESC \`, or timeout (empty string) - while (true) { + for (;;) { const next = yield; if (next === '' || next === '\u0007') { break; diff --git a/packages/cli/src/ui/contexts/SettingsContext.test.tsx b/packages/cli/src/ui/contexts/SettingsContext.test.tsx index 3124108f90..00d955b060 100644 --- a/packages/cli/src/ui/contexts/SettingsContext.test.tsx +++ b/packages/cli/src/ui/contexts/SettingsContext.test.tsx @@ -113,7 +113,7 @@ describe('SettingsContext', () => { it('should trigger re-renders when settings change (external event)', () => { const { result } = renderHook(() => useSettingsStore(), { wrapper }); - expect(result.current.settings.merged.ui?.theme).toBe('default-theme'); + expect(result.current.settings.merged.ui.theme).toBe('default-theme'); const newSnapshot = { ...mockSnapshot, @@ -128,7 +128,7 @@ describe('SettingsContext', () => { listeners.forEach((l) => l()); }); - expect(result.current.settings.merged.ui?.theme).toBe('new-theme'); + expect(result.current.settings.merged.ui.theme).toBe('new-theme'); }); it('should call store.setValue when setSetting is called', () => { diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index c23c9fa2db..29fde0c416 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -119,7 +119,7 @@ function categorizeAtCommands( const resourceParts: AtCommandPart[] = []; const fileParts: AtCommandPart[] = []; - const agentRegistry = config.getAgentRegistry?.(); + const agentRegistry = config.getAgentRegistry(); const resourceRegistry = config.getResourceRegistry(); for (const part of commandParts) { @@ -129,7 +129,7 @@ function categorizeAtCommands( const name = part.content.substring(1); - if (agentRegistry?.getDefinition(name)) { + if (agentRegistry.getDefinition(name)) { agentParts.push(part); } else if (resourceRegistry.findResourceByUri(name)) { resourceParts.push(part); diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index c3f178ad1b..c79de1efbc 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -273,10 +273,6 @@ export const useSlashCommandProcessor = ( ); useEffect(() => { - if (!config) { - return; - } - const listener = () => { reloadCommands(); }; @@ -535,7 +531,7 @@ export const useSlashCommandProcessor = ( } } case 'load_history': { - config?.getGeminiClient()?.setHistory(result.clientHistory); + config?.getGeminiClient().setHistory(result.clientHistory); fullCommandContext.ui.clear(); result.history.forEach((item, index) => { fullCommandContext.ui.addItem(item, index); @@ -688,7 +684,7 @@ export const useSlashCommandProcessor = ( command: resolvedCommandPath[0], subcommand, status: SlashCommandStatus.ERROR, - extension_id: commandToExecute?.extensionId, + extension_id: commandToExecute.extensionId, }); logSlashCommand(config, event); } @@ -706,7 +702,7 @@ export const useSlashCommandProcessor = ( command: resolvedCommandPath[0], subcommand, status: SlashCommandStatus.SUCCESS, - extension_id: commandToExecute?.extensionId, + extension_id: commandToExecute.extensionId, }); logSlashCommand(config, event); } diff --git a/packages/cli/src/ui/hooks/useAtCompletion.ts b/packages/cli/src/ui/hooks/useAtCompletion.ts index 8d860bb6ce..b8612e33fb 100644 --- a/packages/cli/src/ui/hooks/useAtCompletion.ts +++ b/packages/cli/src/ui/hooks/useAtCompletion.ts @@ -115,7 +115,7 @@ interface ResourceSuggestionCandidate { function buildResourceCandidates( config?: Config, ): ResourceSuggestionCandidate[] { - const registry = config?.getResourceRegistry?.(); + const registry = config?.getResourceRegistry(); if (!registry) { return []; } @@ -137,7 +137,7 @@ function buildResourceCandidates( } function buildAgentCandidates(config?: Config): Suggestion[] { - const registry = config?.getAgentRegistry?.(); + const registry = config?.getAgentRegistry(); if (!registry) { return []; } @@ -232,7 +232,7 @@ export function useAtCompletion(props: UseAtCompletionProps): void { }, [cwd, config]); useEffect(() => { - const workspaceContext = config?.getWorkspaceContext?.(); + const workspaceContext = config?.getWorkspaceContext(); if (!workspaceContext) return; const unsubscribe = @@ -274,9 +274,9 @@ export function useAtCompletion(props: UseAtCompletionProps): void { const initialize = async () => { const currentEpoch = initEpoch.current; try { - const directories = config - ?.getWorkspaceContext?.() - ?.getDirectories() ?? [cwd]; + const directories = config?.getWorkspaceContext().getDirectories() ?? [ + cwd, + ]; const initPromises: Array> = []; @@ -296,7 +296,7 @@ export function useAtCompletion(props: UseAtCompletionProps): void { config?.getEnableRecursiveFileSearch() ?? true, enableFuzzySearch: config?.getFileFilteringEnableFuzzySearch() ?? true, - maxFiles: config?.getFileFilteringOptions()?.maxFileCount, + maxFiles: config?.getFileFilteringOptions().maxFileCount, }); initPromises.push( @@ -342,7 +342,7 @@ export function useAtCompletion(props: UseAtCompletionProps): void { }, 200); const timeoutMs = - config?.getFileFilteringOptions()?.searchTimeout ?? + config?.getFileFilteringOptions().searchTimeout ?? DEFAULT_SEARCH_TIMEOUT_MS; // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -358,9 +358,9 @@ export function useAtCompletion(props: UseAtCompletionProps): void { })(); try { - const directories = config - ?.getWorkspaceContext?.() - ?.getDirectories() ?? [cwd]; + const directories = config?.getWorkspaceContext().getDirectories() ?? [ + cwd, + ]; const cwdRealpath = directories[0]; const allSearchPromises = [...fileSearchMap.current.entries()].map( diff --git a/packages/cli/src/ui/hooks/useFocus.ts b/packages/cli/src/ui/hooks/useFocus.ts index 638e9c0cc8..a126087c4d 100644 --- a/packages/cli/src/ui/hooks/useFocus.ts +++ b/packages/cli/src/ui/hooks/useFocus.ts @@ -41,13 +41,13 @@ export const useFocus = (): { }; // Enable focus reporting - stdout?.write(ENABLE_FOCUS_REPORTING); - stdin?.on('data', handleData); + stdout.write(ENABLE_FOCUS_REPORTING); + stdin.on('data', handleData); return () => { // Disable focus reporting on cleanup - stdout?.write(DISABLE_FOCUS_REPORTING); - stdin?.removeListener('data', handleData); + stdout.write(DISABLE_FOCUS_REPORTING); + stdin.removeListener('data', handleData); }; }, [stdin, stdout]); diff --git a/packages/cli/src/ui/hooks/useFolderTrust.ts b/packages/cli/src/ui/hooks/useFolderTrust.ts index e2a5373e34..328e73c7f3 100644 --- a/packages/cli/src/ui/hooks/useFolderTrust.ts +++ b/packages/cli/src/ui/hooks/useFolderTrust.ts @@ -35,7 +35,7 @@ export const useFolderTrust = ( const [isRestarting, setIsRestarting] = useState(false); const startupMessageSent = useRef(false); - const folderTrust = settings.merged.security.folderTrust.enabled ?? true; + const folderTrust = settings.merged.security.folderTrust.enabled; useEffect(() => { let isMounted = true; @@ -68,13 +68,11 @@ export const useFolderTrust = ( }; if (isHeadlessMode()) { - if (isMounted) { - setIsTrusted(trusted); - setIsFolderTrustDialogOpen(false); - onTrustChange(true); - showUntrustedMessage(); - } - } else if (isMounted) { + setIsTrusted(trusted); + setIsFolderTrustDialogOpen(false); + onTrustChange(true); + showUntrustedMessage(); + } else { setIsTrusted(trusted); setIsFolderTrustDialogOpen(trusted === undefined); onTrustChange(trusted); @@ -95,7 +93,6 @@ export const useFolderTrust = ( }; const trustLevel = trustLevelMap[choice]; - if (!trustLevel) return; const cwd = process.cwd(); const trustedFolders = loadTrustedFolders(); diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 2a25359614..1ada824e2c 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -968,7 +968,7 @@ export const useGeminiStream = ( type: MessageType.ERROR, text: parseAndFormatApiError( eventValue.error, - config.getContentGeneratorConfig()?.authType, + config.getContentGeneratorConfig().authType, undefined, config.getModel(), DEFAULT_GEMINI_FLASH_MODEL, @@ -1407,7 +1407,7 @@ export const useGeminiStream = ( new UserPromptEvent( promptText.length, prompt_id!, - config.getContentGeneratorConfig()?.authType, + config.getContentGeneratorConfig().authType, promptText, ), ); @@ -1499,7 +1499,7 @@ export const useGeminiStream = ( type: MessageType.ERROR, text: parseAndFormatApiError( getErrorMessage(error) || 'Unknown error', - config.getContentGeneratorConfig()?.authType, + config.getContentGeneratorConfig().authType, undefined, config.getModel(), DEFAULT_GEMINI_FLASH_MODEL, @@ -1625,7 +1625,7 @@ export const useGeminiStream = ( | TrackedCompletedToolCall | TrackedCancelledToolCall; return ( - completedOrCancelledCall.response?.responseParts !== undefined + completedOrCancelledCall.response.responseParts !== undefined ); } return false; @@ -1653,7 +1653,7 @@ export const useGeminiStream = ( const isShell = t.request.name === 'run_shell_command'; // Access result from the tracked tool call response const response = t.response as ToolResponseWithParts; - const rawData = response?.data; + const rawData = response.data; const data = isShellToolData(rawData) ? rawData : undefined; // Use data.pid for shell commands moved to the background. @@ -1661,9 +1661,9 @@ export const useGeminiStream = ( if (isShell && pid) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const command = (data?.['command'] as string) ?? 'shell'; + const command = (data['command'] as string) ?? 'shell'; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const initialOutput = (data?.['initialOutput'] as string) ?? ''; + const initialOutput = (data['initialOutput'] as string) ?? ''; registerBackgroundShell(pid, command, initialOutput); } diff --git a/packages/cli/src/ui/hooks/useHistoryManager.ts b/packages/cli/src/ui/hooks/useHistoryManager.ts index 93f7f01f28..21bd94e7f5 100644 --- a/packages/cli/src/ui/hooks/useHistoryManager.ts +++ b/packages/cli/src/ui/hooks/useHistoryManager.ts @@ -86,21 +86,21 @@ export function useHistory({ switch (itemData.type) { case 'compression': case 'info': - chatRecordingService?.recordMessage({ + chatRecordingService.recordMessage({ model: undefined, type: 'info', content: itemData.text ?? '', }); break; case 'warning': - chatRecordingService?.recordMessage({ + chatRecordingService.recordMessage({ model: undefined, type: 'warning', content: itemData.text ?? '', }); break; case 'error': - chatRecordingService?.recordMessage({ + chatRecordingService.recordMessage({ model: undefined, type: 'error', content: itemData.text ?? '', diff --git a/packages/cli/src/ui/hooks/useQuotaAndFallback.ts b/packages/cli/src/ui/hooks/useQuotaAndFallback.ts index 40b1f68926..1425d4c84a 100644 --- a/packages/cli/src/ui/hooks/useQuotaAndFallback.ts +++ b/packages/cli/src/ui/hooks/useQuotaAndFallback.ts @@ -138,7 +138,7 @@ export function useQuotaAndFallback({ : null, `/stats model for usage details`, `/model to switch models.`, - contentGeneratorConfig?.authType === AuthType.LOGIN_WITH_GOOGLE + contentGeneratorConfig.authType === AuthType.LOGIN_WITH_GOOGLE ? `/auth to switch to API key.` : null, ].filter(Boolean); @@ -194,7 +194,7 @@ export function useQuotaAndFallback({ message, isTerminalQuotaError, isModelNotFoundError, - authType: contentGeneratorConfig?.authType, + authType: contentGeneratorConfig.authType, }); }, ); diff --git a/packages/cli/src/ui/hooks/useSessionBrowser.ts b/packages/cli/src/ui/hooks/useSessionBrowser.ts index 9c6d05b322..17b6f512be 100644 --- a/packages/cli/src/ui/hooks/useSessionBrowser.ts +++ b/packages/cli/src/ui/hooks/useSessionBrowser.ts @@ -104,7 +104,7 @@ export const useSessionBrowser = ( try { const chatRecordingService = config .getGeminiClient() - ?.getChatRecordingService(); + .getChatRecordingService(); if (chatRecordingService) { chatRecordingService.deleteSession(session.file); } diff --git a/packages/cli/src/ui/hooks/useSessionResume.ts b/packages/cli/src/ui/hooks/useSessionResume.ts index 055686773b..80d99b5bfc 100644 --- a/packages/cli/src/ui/hooks/useSessionResume.ts +++ b/packages/cli/src/ui/hooks/useSessionResume.ts @@ -84,7 +84,7 @@ export function useSessionResume({ } // Give the history to the Gemini client. - await config.getGeminiClient()?.resumeChat(clientHistory, resumedData); + await config.getGeminiClient().resumeChat(clientHistory, resumedData); } catch (error) { coreEvents.emitFeedback( 'error', diff --git a/packages/cli/src/ui/hooks/useTurnActivityMonitor.ts b/packages/cli/src/ui/hooks/useTurnActivityMonitor.ts index 8cd7883007..7b03e59748 100644 --- a/packages/cli/src/ui/hooks/useTurnActivityMonitor.ts +++ b/packages/cli/src/ui/hooks/useTurnActivityMonitor.ts @@ -55,8 +55,7 @@ export const useTurnActivityMonitor = ( pendingToolCalls.some((tc) => { if (tc.request.name !== 'run_shell_command') return false; - const command = - (tc.request.args as { command?: string })?.command || ''; + const command = (tc.request.args as { command?: string }).command || ''; return hasRedirection(command); }), [pendingToolCalls], diff --git a/packages/cli/src/ui/privacy/PrivacyNotice.tsx b/packages/cli/src/ui/privacy/PrivacyNotice.tsx index ab2ffd1084..429d483fe3 100644 --- a/packages/cli/src/ui/privacy/PrivacyNotice.tsx +++ b/packages/cli/src/ui/privacy/PrivacyNotice.tsx @@ -22,7 +22,7 @@ const PrivacyNoticeText = ({ config: Config; onExit: () => void; }) => { - const authType = config.getContentGeneratorConfig()?.authType; + const authType = config.getContentGeneratorConfig().authType; switch (authType) { case AuthType.USE_GEMINI: diff --git a/packages/cli/src/ui/themes/theme.ts b/packages/cli/src/ui/themes/theme.ts index 7785e9bda0..5201d38222 100644 --- a/packages/cli/src/ui/themes/theme.ts +++ b/packages/cli/src/ui/themes/theme.ts @@ -353,7 +353,7 @@ export class Theme { this._colorMap = Object.freeze(this._buildColorMap(rawMappings)); // Build and freeze the map // Determine the default foreground color - const rawDefaultColor = rawMappings['hljs']?.color; + const rawDefaultColor = rawMappings['hljs'].color; this.defaultColor = (rawDefaultColor ? Theme._resolveColor(rawDefaultColor) : undefined) ?? ''; // Default to empty string if not found or resolvable @@ -394,7 +394,7 @@ export class Theme { } const style = hljsTheme[key]; - if (style?.color) { + if (style.color) { const resolvedColor = Theme._resolveColor(style.color); if (resolvedColor !== undefined) { // Use the original key from the hljsTheme (e.g., 'hljs-keyword') diff --git a/packages/cli/src/ui/utils/CodeColorizer.tsx b/packages/cli/src/ui/utils/CodeColorizer.tsx index 56e34eefa4..d13af89b6f 100644 --- a/packages/cli/src/ui/utils/CodeColorizer.tsx +++ b/packages/cli/src/ui/utils/CodeColorizer.tsx @@ -41,7 +41,7 @@ function renderHastNode( if (node.type === 'element') { const nodeClasses: string[] = // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - (node.properties?.['className'] as string[]) || []; + (node.properties['className'] as string[]) || []; let elementColor: string | undefined = undefined; // Find color defined specifically for this element's class @@ -59,7 +59,7 @@ function renderHastNode( // Recursively render children, passing the determined color down // Ensure child type matches expected HAST structure (ElementContent is common) - const children = node.children?.map( + const children = node.children.map( (child: ElementContent, index: number) => ( {renderHastNode(child, theme, colorToPassDown)} @@ -81,7 +81,7 @@ function renderHastNode( // Pass down the initial inheritedColor (likely undefined from the top call) // Ensure child type matches expected HAST structure (RootContent is common) - return node.children?.map((child: RootContent, index: number) => ( + return node.children.map((child: RootContent, index: number) => ( {renderHastNode(child, theme, inheritedColor)} diff --git a/packages/cli/src/ui/utils/commandUtils.ts b/packages/cli/src/ui/utils/commandUtils.ts index d6fdb99f0f..f48e7d227e 100644 --- a/packages/cli/src/ui/utils/commandUtils.ts +++ b/packages/cli/src/ui/utils/commandUtils.ts @@ -124,18 +124,18 @@ const getStdioTty = (): TtyTarget => { // On Windows, prioritize stdout to prevent shell-specific formatting (e.g., PowerShell's // red stderr) from corrupting the raw escape sequence payload. if (process.platform === 'win32') { - if (process.stdout?.isTTY) + if (process.stdout.isTTY) return { stream: process.stdout, closeAfter: false }; - if (process.stderr?.isTTY) + if (process.stderr.isTTY) return { stream: process.stderr, closeAfter: false }; return null; } // On non-Windows platforms, prioritize stderr to avoid polluting stdout, // preserving it for potential redirection or piping. - if (process.stderr?.isTTY) + if (process.stderr.isTTY) return { stream: process.stderr, closeAfter: false }; - if (process.stdout?.isTTY) + if (process.stdout.isTTY) return { stream: process.stdout, closeAfter: false }; return null; }; diff --git a/packages/cli/src/ui/utils/inlineThinkingMode.ts b/packages/cli/src/ui/utils/inlineThinkingMode.ts index 16ca1a44a2..a6d6f75d27 100644 --- a/packages/cli/src/ui/utils/inlineThinkingMode.ts +++ b/packages/cli/src/ui/utils/inlineThinkingMode.ts @@ -11,5 +11,5 @@ export type InlineThinkingMode = 'off' | 'full'; export function getInlineThinkingMode( settings: LoadedSettings, ): InlineThinkingMode { - return settings.merged.ui?.inlineThinkingMode ?? 'off'; + return settings.merged.ui.inlineThinkingMode ?? 'off'; } diff --git a/packages/cli/src/ui/utils/terminalCapabilityManager.ts b/packages/cli/src/ui/utils/terminalCapabilityManager.ts index a161b2aa1b..a17a2957a4 100644 --- a/packages/cli/src/ui/utils/terminalCapabilityManager.ts +++ b/packages/cli/src/ui/utils/terminalCapabilityManager.ts @@ -22,7 +22,7 @@ const TERMINAL_CLEANUP_SEQUENCE = '\x1b[4;0m\x1b[?2004l'; export function cleanupTerminalOnExit() { try { - if (process.stdout?.fd !== undefined) { + if (process.stdout.fd !== undefined) { fs.writeSync(process.stdout.fd, TERMINAL_CLEANUP_SEQUENCE); return; } diff --git a/packages/cli/src/ui/utils/terminalSetup.ts b/packages/cli/src/ui/utils/terminalSetup.ts index aaa8d9fc6f..232a67c5cb 100644 --- a/packages/cli/src/ui/utils/terminalSetup.ts +++ b/packages/cli/src/ui/utils/terminalSetup.ts @@ -78,10 +78,8 @@ const TERMINAL_DATA: Record = { /** * Maps a supported terminal ID to its display name and config folder name. */ -function getSupportedTerminalData( - terminal: SupportedTerminal, -): TerminalData | null { - return TERMINAL_DATA[terminal] || null; +function getSupportedTerminalData(terminal: SupportedTerminal): TerminalData { + return TERMINAL_DATA[terminal]; } type Keybinding = { diff --git a/packages/cli/src/utils/activityLogger.ts b/packages/cli/src/utils/activityLogger.ts index a6f903fe49..f58c95c8d6 100644 --- a/packages/cli/src/utils/activityLogger.ts +++ b/packages/cli/src/utils/activityLogger.ts @@ -340,7 +340,7 @@ export class ActivityLogger extends EventEmitter { const readStream = async () => { try { - while (true) { + for (;;) { const { done, value } = await reader.read(); if (done) break; diff --git a/packages/cli/src/utils/cleanup.ts b/packages/cli/src/utils/cleanup.ts index 6185b34fe5..05501e7d6e 100644 --- a/packages/cli/src/utils/cleanup.ts +++ b/packages/cli/src/utils/cleanup.ts @@ -92,7 +92,7 @@ export async function runExitCleanup() { } async function drainStdin() { - if (!process.stdin?.isTTY) return; + if (!process.stdin.isTTY) return; // Resume stdin and attach a no-op listener to drain the buffer. // We use removeAllListeners to ensure we don't trigger other handlers. process.stdin diff --git a/packages/cli/src/utils/envVarResolver.ts b/packages/cli/src/utils/envVarResolver.ts index 6e01f67ac7..f56e3c5eaa 100644 --- a/packages/cli/src/utils/envVarResolver.ts +++ b/packages/cli/src/utils/envVarResolver.ts @@ -19,7 +19,7 @@ */ export function resolveEnvVarsInString( value: string, - customEnv?: Record, + customEnv?: Record, ): string { const envVarRegex = /\$(?:(\w+)|{([^}]+)})/g; // Find $VAR_NAME or ${VAR_NAME} return value.replace(envVarRegex, (match, varName1, varName2) => { @@ -56,7 +56,7 @@ export function resolveEnvVarsInString( */ export function resolveEnvVarsInObject( obj: T, - customEnv?: Record, + customEnv?: Record, ): T { return resolveEnvVarsInObjectInternal(obj, new WeakSet(), customEnv); } @@ -71,7 +71,7 @@ export function resolveEnvVarsInObject( function resolveEnvVarsInObjectInternal( obj: T, visited: WeakSet, - customEnv?: Record, + customEnv?: Record, ): T { if ( obj === null || diff --git a/packages/cli/src/utils/errors.ts b/packages/cli/src/utils/errors.ts index 89c0fe6b22..4e7f5c4ac6 100644 --- a/packages/cli/src/utils/errors.ts +++ b/packages/cli/src/utils/errors.ts @@ -75,7 +75,7 @@ export function handleError( ): never { const errorMessage = parseAndFormatApiError( error, - config.getContentGeneratorConfig()?.authType, + config.getContentGeneratorConfig().authType, ); if (config.getOutputFormat() === OutputFormat.STREAM_JSON) { diff --git a/packages/cli/src/utils/installationInfo.ts b/packages/cli/src/utils/installationInfo.ts index a682cc75e1..ace362d8db 100644 --- a/packages/cli/src/utils/installationInfo.ts +++ b/packages/cli/src/utils/installationInfo.ts @@ -43,7 +43,7 @@ export function getInstallationInfo( try { // Normalize path separators to forward slashes for consistent matching. const realPath = fs.realpathSync(cliPath).replace(/\\/g, '/'); - const normalizedProjectRoot = projectRoot?.replace(/\\/g, '/'); + const normalizedProjectRoot = projectRoot.replace(/\\/g, '/'); const isGit = isGitRepository(process.cwd()); // Check for local git clone first diff --git a/packages/cli/src/utils/relaunch.ts b/packages/cli/src/utils/relaunch.ts index 7e287e4565..0f36c0f350 100644 --- a/packages/cli/src/utils/relaunch.ts +++ b/packages/cli/src/utils/relaunch.ts @@ -12,7 +12,7 @@ import { } from '@google/gemini-cli-core'; export async function relaunchOnExitCode(runner: () => Promise) { - while (true) { + for (;;) { try { const exitCode = await runner(); diff --git a/packages/cli/src/utils/sandbox.ts b/packages/cli/src/utils/sandbox.ts index ffd77fb119..99ce40a033 100644 --- a/packages/cli/src/utils/sandbox.ts +++ b/packages/cli/src/utils/sandbox.ts @@ -195,8 +195,8 @@ export async function start_sandbox( stdio: 'inherit', }); return await new Promise((resolve, reject) => { - sandboxProcess?.on('error', reject); - sandboxProcess?.on('close', (code) => { + sandboxProcess.on('error', reject); + sandboxProcess.on('close', (code) => { process.stdin.resume(); resolve(code ?? 1); }); @@ -707,7 +707,7 @@ export async function start_sandbox( reject(err); }); - sandboxProcess?.on('close', (code, signal) => { + sandboxProcess.on('close', (code, signal) => { process.stdin.resume(); if (code !== 0 && code !== null) { debugLogger.log( diff --git a/packages/cli/src/utils/settingsUtils.ts b/packages/cli/src/utils/settingsUtils.ts index 11c3a9a13f..8963aa17f4 100644 --- a/packages/cli/src/utils/settingsUtils.ts +++ b/packages/cli/src/utils/settingsUtils.ts @@ -75,11 +75,11 @@ export function getSettingDefinition( } export function requiresRestart(key: string): boolean { - return getFlattenedSchema()[key]?.requiresRestart ?? false; + return getFlattenedSchema()[key].requiresRestart ?? false; } export function getDefaultValue(key: string): SettingsValue { - return getFlattenedSchema()[key]?.default; + return getFlattenedSchema()[key].default; } /** @@ -208,11 +208,11 @@ export function isValidSettingKey(key: string): boolean { } export function getSettingCategory(key: string): string | undefined { - return getFlattenedSchema()[key]?.category; + return getFlattenedSchema()[key].category; } export function shouldShowInDialog(key: string): boolean { - return getFlattenedSchema()[key]?.showInDialog ?? true; // Default to true for backward compatibility + return getFlattenedSchema()[key].showInDialog ?? true; // Default to true for backward compatibility } export function getDialogSettingKeys(): string[] { @@ -285,7 +285,7 @@ export function getDisplayValue( let valueString = String(value); if (definition?.type === 'enum' && definition.options) { - const option = definition.options?.find((option) => option.value === value); + const option = definition.options.find((option) => option.value === value); valueString = option?.label ?? `${value}`; } diff --git a/packages/cli/src/zed-integration/zedIntegration.test.ts b/packages/cli/src/zed-integration/zedIntegration.test.ts index 810cb9a1de..0a9a102781 100644 --- a/packages/cli/src/zed-integration/zedIntegration.test.ts +++ b/packages/cli/src/zed-integration/zedIntegration.test.ts @@ -1231,7 +1231,7 @@ describe('Session', () => { streamStarted(true); const reader = stream.getReader(); try { - while (true) { + for (;;) { process.stdout.write('TEST: waiting for read\n'); const { done, value } = await reader.read(); process.stdout.write(`TEST: read returned done=${done}\n`); diff --git a/packages/cli/src/zed-integration/zedIntegration.ts b/packages/cli/src/zed-integration/zedIntegration.ts index dc07502f7f..88b0acb6bb 100644 --- a/packages/cli/src/zed-integration/zedIntegration.ts +++ b/packages/cli/src/zed-integration/zedIntegration.ts @@ -657,11 +657,11 @@ export class Session { try { const model = resolveModel( this.config.getModel(), - (await this.config.getGemini31Launched?.()) ?? false, + (await this.config.getGemini31Launched()) ?? false, ); const responseStream = await chat.sendMessageStream( { model }, - nextMessage?.parts ?? [], + nextMessage.parts ?? [], promptId, pendingSend.signal, LlmRole.MAIN, diff --git a/packages/core/src/agents/browser/browserAgentFactory.ts b/packages/core/src/agents/browser/browserAgentFactory.ts index a8a3b0f338..80875a6a66 100644 --- a/packages/core/src/agents/browser/browserAgentFactory.ts +++ b/packages/core/src/agents/browser/browserAgentFactory.ts @@ -101,7 +101,7 @@ export async function createBrowserAgentDefinition( `The installed chrome-devtools-mcp version may be too old.` ); } - const authType = config.getContentGeneratorConfig()?.authType; + const authType = config.getContentGeneratorConfig().authType; const blockedAuthTypes = new Set([ AuthType.LOGIN_WITH_GOOGLE, AuthType.LEGACY_CLOUD_SHELL, diff --git a/packages/core/src/agents/local-executor.ts b/packages/core/src/agents/local-executor.ts index 7bbecdac7c..c66cbc74bc 100644 --- a/packages/core/src/agents/local-executor.ts +++ b/packages/core/src/agents/local-executor.ts @@ -492,7 +492,7 @@ export class LocalAgentExecutor { } : { role: 'user', parts: [{ text: query }] }; - while (true) { + for (;;) { // Check for termination conditions like max turns. const reason = this.checkTermination(turnCounter, maxTurns); if (reason) { diff --git a/packages/core/src/availability/policyHelpers.ts b/packages/core/src/availability/policyHelpers.ts index 05c1dd19f9..3c14771f2d 100644 --- a/packages/core/src/availability/policyHelpers.ts +++ b/packages/core/src/availability/policyHelpers.ts @@ -41,8 +41,8 @@ export function resolvePolicyChain( wrapsAround: boolean = false, ): ModelPolicyChain { const modelFromConfig = - preferredModel ?? config.getActiveModel?.() ?? config.getModel(); - const configuredModel = config.getModel(); + preferredModel ?? config.getActiveModel?.() ?? config.getModel?.(); + const configuredModel = config.getModel?.(); let chain; const useGemini31 = config.getGemini31LaunchedSync?.() ?? false; @@ -73,7 +73,7 @@ export function resolvePolicyChain( configuredModel === PREVIEW_GEMINI_MODEL_AUTO; chain = getModelPolicyChain({ previewEnabled, - userTier: config.getUserTier(), + userTier: config.getUserTier?.(), useGemini31, useCustomToolModel, }); @@ -82,7 +82,7 @@ export function resolvePolicyChain( // to the stable Gemini 2.5 chain. return getModelPolicyChain({ previewEnabled: false, - userTier: config.getUserTier(), + userTier: config.getUserTier?.(), useGemini31, useCustomToolModel, }); @@ -139,7 +139,7 @@ export function resolvePolicyAction( failureKind: FailureKind, policy: ModelPolicy, ): FallbackAction { - return policy.actions?.[failureKind] ?? 'prompt'; + return policy.actions[failureKind] ?? 'prompt'; } /** @@ -236,7 +236,7 @@ export function applyAvailabilityTransition( const context = getContext?.(); if (!context) return; - const transition = context.policy.stateTransitions?.[failureKind]; + const transition = context.policy.stateTransitions[failureKind]; if (!transition) return; if (transition === 'terminal') { diff --git a/packages/core/src/code_assist/setup.ts b/packages/core/src/code_assist/setup.ts index 35ef980db2..85a493dbd4 100644 --- a/packages/core/src/code_assist/setup.ts +++ b/packages/core/src/code_assist/setup.ts @@ -101,7 +101,7 @@ export async function setupUser( }; let loadRes: LoadCodeAssistResponse; - while (true) { + for (;;) { loadRes = await caServer.loadCodeAssist({ cloudaicompanionProject: projectId, metadata: { diff --git a/packages/core/src/code_assist/telemetry.ts b/packages/core/src/code_assist/telemetry.ts index 59ff179c50..6f5bfab719 100644 --- a/packages/core/src/code_assist/telemetry.ts +++ b/packages/core/src/code_assist/telemetry.ts @@ -197,7 +197,7 @@ function includesCode(resp: GenerateContentResponse): boolean { continue; } for (const part of candidate.content.parts) { - if ('text' in part && part?.text?.includes('```')) { + if ('text' in part && part.text?.includes('```')) { return true; } } @@ -232,7 +232,7 @@ function hasError(response: GenerateContentResponse): boolean { // Non-OK SDK results should be considered an error. if ( response.sdkHttpResponse && - !response.sdkHttpResponse?.responseInternal?.ok + !response.sdkHttpResponse.responseInternal.ok ) { return true; } diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index ce07271139..f4cb08940e 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1386,7 +1386,7 @@ export class Config implements McpContext { } getContentGeneratorConfig(): ContentGeneratorConfig { - return this.contentGeneratorConfig; + return this.contentGeneratorConfig ?? {}; } getModel(): string { diff --git a/packages/core/src/config/projectRegistry.ts b/packages/core/src/config/projectRegistry.ts index 725ea081f9..ef51e52732 100644 --- a/packages/core/src/config/projectRegistry.ts +++ b/packages/core/src/config/projectRegistry.ts @@ -236,7 +236,7 @@ export class ProjectRegistry { let counter = 0; const existingIds = new Set(Object.values(existingMappings)); - while (true) { + for (;;) { const candidate = counter === 0 ? slug : `${slug}-${counter}`; counter++; diff --git a/packages/core/src/core/apiKeyCredentialStorage.ts b/packages/core/src/core/apiKeyCredentialStorage.ts index 4836ba075b..c027e3ea19 100644 --- a/packages/core/src/core/apiKeyCredentialStorage.ts +++ b/packages/core/src/core/apiKeyCredentialStorage.ts @@ -20,7 +20,7 @@ export async function loadApiKey(): Promise { try { const credentials = await storage.getCredentials(DEFAULT_API_KEY_ENTRY); - if (credentials?.token?.accessToken) { + if (credentials?.token.accessToken) { return credentials.token.accessToken; } diff --git a/packages/core/src/core/baseLlmClient.ts b/packages/core/src/core/baseLlmClient.ts index 0de4dd1e20..647989f4d8 100644 --- a/packages/core/src/core/baseLlmClient.ts +++ b/packages/core/src/core/baseLlmClient.ts @@ -326,10 +326,10 @@ export class BaseLlmClient { handleFallback(this.config, currentModel, authType, error) : undefined, authType: - this.authType ?? this.config.getContentGeneratorConfig()?.authType, + this.authType ?? this.config.getContentGeneratorConfig().authType, }); } catch (error) { - if (abortSignal?.aborted) { + if (abortSignal.aborted) { throw error; } diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index 1f9ecf2976..8364833331 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -1142,7 +1142,7 @@ ${JSON.stringify( // Consume the stream manually to get the final return value. let finalResult: Turn | undefined; - while (true) { + for (;;) { const result = await stream.next(); if (result.done) { finalResult = result.value; @@ -1198,7 +1198,7 @@ ${JSON.stringify( let finalResult: Turn | undefined; // Consume the stream and count iterations - while (true) { + for (;;) { const result = await stream.next(); if (result.done) { finalResult = result.value; @@ -1325,7 +1325,7 @@ ${JSON.stringify( // Consume the stream and count iterations try { - while (true) { + for (;;) { const result = await stream.next(); if (result.done) { break; diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 1bf4c5cd89..518161bb72 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -229,10 +229,7 @@ export class GeminiClient { } private getContentGeneratorOrFail(): ContentGenerator { - if (!this.config.getContentGenerator()) { - throw new Error('Content generator not initialized'); - } - return this.config.getContentGenerator(); + return this.config.getContentGenerator()!; } async addHistory(content: Content) { @@ -551,7 +548,7 @@ export class GeminiClient { // including any permanent fallbacks (config.setModel) or manual overrides. return resolveModel( this.config.getActiveModel(), - this.config.getGemini31LaunchedSync?.() ?? false, + this.config.getGemini31LaunchedSync(), ); } @@ -761,7 +758,7 @@ export class GeminiClient { } } - if (!turn.pendingToolCalls.length && signal && !signal.aborted) { + if (!turn.pendingToolCalls.length && !signal.aborted) { if ( !this.config.getQuotaErrorOccurred() && !this.config.getSkipNextSpeakerCheck() @@ -810,7 +807,6 @@ export class GeminiClient { } const hooksEnabled = this.config.getEnableHooks(); - const messageBus = this.config.getMessageBus(); if (this.lastPromptId !== prompt_id) { this.loopDetector.reset(prompt_id, partListUnionToString(request)); @@ -819,7 +815,7 @@ export class GeminiClient { this.currentSequenceModel = null; } - if (hooksEnabled && messageBus) { + if (hooksEnabled) { const hookResult = await this.fireBeforeAgentHookSafe(request, prompt_id); if (hookResult) { if ( @@ -830,10 +826,7 @@ export class GeminiClient { this.getChat().addHistory(createUserContent(request)); yield hookResult; return new Turn(this.getChat(), prompt_id); - } else if ( - 'type' in hookResult && - hookResult.type === GeminiEventType.AgentExecutionBlocked - ) { + } else if ('type' in hookResult) { yield hookResult; return new Turn(this.getChat(), prompt_id); } else if ('additionalContext' in hookResult) { @@ -863,7 +856,7 @@ export class GeminiClient { ); // Fire AfterAgent hook if we have a turn and no pending tools - if (hooksEnabled && messageBus) { + if (hooksEnabled) { const hookOutput = await this.fireAfterAgentHookSafe( request, prompt_id, @@ -920,9 +913,8 @@ export class GeminiClient { const hookState = this.hookStateMap.get(prompt_id); if (hookState) { hookState.activeCalls--; - const isPendingTools = - turn?.pendingToolCalls && turn.pendingToolCalls.length > 0; - const isAborted = signal?.aborted; + const isPendingTools = turn.pendingToolCalls.length > 0; + const isAborted = signal.aborted; if (hookState.activeCalls <= 0) { if (!isPendingTools || isAborted) { @@ -957,9 +949,7 @@ export class GeminiClient { maxAttempts: availabilityMaxAttempts, } = applyModelSelection(this.config, modelConfigKey); currentAttemptModel = model; - if (newConfig) { - currentAttemptGenerateContentConfig = newConfig; - } + currentAttemptGenerateContentConfig = newConfig; // Define callback to refresh context based on currentAttemptModel which might be updated by fallback handler const getAvailabilityContext: () => RetryAvailabilityContext | undefined = @@ -1031,7 +1021,7 @@ export class GeminiClient { const result = await retryWithBackoff(apiCall, { onPersistent429: onPersistent429Callback, onValidationRequired: onValidationRequiredCallback, - authType: this.config.getContentGeneratorConfig()?.authType, + authType: this.config.getContentGeneratorConfig().authType, maxAttempts: availabilityMaxAttempts, getAvailabilityContext, }); diff --git a/packages/core/src/core/contentGenerator.ts b/packages/core/src/core/contentGenerator.ts index 4270305ca7..03ec55b1c1 100644 --- a/packages/core/src/core/contentGenerator.ts +++ b/packages/core/src/core/contentGenerator.ts @@ -114,7 +114,7 @@ export async function createContentGeneratorConfig( const contentGeneratorConfig: ContentGeneratorConfig = { authType, - proxy: config?.getProxy(), + proxy: config.getProxy(), }; // If we are using Google auth or we are in Cloud Shell, there is nothing else to validate for now @@ -206,7 +206,7 @@ export async function createContentGenerator( config.authType === AuthType.USE_VERTEX_AI ) { let headers: Record = { ...baseHeaders }; - if (gcConfig?.getUsageStatisticsEnabled()) { + if (gcConfig.getUsageStatisticsEnabled()) { const installationManager = new InstallationManager(); const installationId = installationManager.getInstallationId(); headers = { diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts index 23473e199d..96838e828d 100644 --- a/packages/core/src/core/coreToolScheduler.ts +++ b/packages/core/src/core/coreToolScheduler.ts @@ -618,23 +618,10 @@ export class CoreToolScheduler { return; } - // This logic is moved from the old `for` loop in `_schedule`. if (toolCall.status === CoreToolCallStatus.Validating) { const { request: reqInfo, invocation } = toolCall; try { - if (signal.aborted) { - this.setStatusInternal( - reqInfo.callId, - CoreToolCallStatus.Cancelled, - signal, - 'Tool call cancelled by user.', - ); - // The completion check will handle the cascade. - await this.checkAndNotifyCompletion(signal); - return; - } - // Policy Check using PolicyEngine // We must reconstruct the FunctionCall format expected by PolicyEngine const toolCallForPolicy = { @@ -759,27 +746,17 @@ export class CoreToolScheduler { } } } catch (error) { - if (signal.aborted) { - this.setStatusInternal( - reqInfo.callId, - CoreToolCallStatus.Cancelled, - signal, - 'Tool call cancelled by user.', - ); - await this.checkAndNotifyCompletion(signal); - } else { - this.setStatusInternal( - reqInfo.callId, - CoreToolCallStatus.Error, - signal, - createErrorResponse( - reqInfo, - error instanceof Error ? error : new Error(String(error)), - ToolErrorType.UNHANDLED_EXCEPTION, - ), - ); - await this.checkAndNotifyCompletion(signal); - } + this.setStatusInternal( + reqInfo.callId, + CoreToolCallStatus.Error, + signal, + createErrorResponse( + reqInfo, + error instanceof Error ? error : new Error(String(error)), + ToolErrorType.UNHANDLED_EXCEPTION, + ), + ); + await this.checkAndNotifyCompletion(signal); } } await this.attemptExecutionOfScheduledCalls(signal); @@ -912,8 +889,6 @@ export class CoreToolScheduler { ); for (const toolCall of callsToExecute) { - if (toolCall.status !== CoreToolCallStatus.Scheduled) continue; - this.setStatusInternal( toolCall.request.callId, CoreToolCallStatus.Executing, diff --git a/packages/core/src/core/geminiChat.test.ts b/packages/core/src/core/geminiChat.test.ts index 105d70e49f..0ce1a5e189 100644 --- a/packages/core/src/core/geminiChat.test.ts +++ b/packages/core/src/core/geminiChat.test.ts @@ -147,6 +147,9 @@ describe('GeminiChat', () => { }), getQuotaErrorOccurred: vi.fn().mockReturnValue(false), setQuotaErrorOccurred: vi.fn(), + getGemini31LaunchedSync: vi.fn().mockReturnValue(false), + getGemini31Launched: vi.fn().mockResolvedValue(false), + getHasAccessToPreviewModel: vi.fn().mockReturnValue(true), flashFallbackHandler: undefined, getProjectRoot: vi.fn().mockReturnValue('/test/project/root'), storage: { diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts index 87d0c235f4..3ce1f86321 100644 --- a/packages/core/src/core/geminiChat.ts +++ b/packages/core/src/core/geminiChat.ts @@ -121,7 +121,7 @@ function isValidContent(content: Content): boolean { return false; } for (const part of content.parts) { - if (part === undefined || Object.keys(part).length === 0) { + if (Object.keys(part).length === 0) { return false; } if (!part.thought && part.text !== undefined && part.text === '') { @@ -154,7 +154,7 @@ function validateHistory(history: Content[]) { * ensures that subsequent requests could be accepted by the model. */ function extractCuratedHistory(comprehensiveHistory: Content[]): Content[] { - if (comprehensiveHistory === undefined || comprehensiveHistory.length === 0) { + if (comprehensiveHistory.length === 0) { return []; } const curatedHistory: Content[] = []; @@ -496,7 +496,7 @@ export class GeminiChat { () => lastModelToUse, ); // Track initial active model to detect fallback changes - const initialActiveModel = this.config.getActiveModel(); + const initialActiveModel = this.config.getActiveModel?.(); const apiCall = async () => { const useGemini3_1 = (await this.config.getGemini31Launched?.()) ?? false; @@ -634,7 +634,7 @@ export class GeminiChat { const streamResponse = await retryWithBackoff(apiCall, { onPersistent429: onPersistent429Callback, onValidationRequired: onValidationRequiredCallback, - authType: this.config.getContentGeneratorConfig()?.authType, + authType: this.config.getContentGeneratorConfig().authType, retryFetchErrors: this.config.getRetryFetchErrors(), signal: abortSignal, maxAttempts: availabilityMaxAttempts ?? this.config.getMaxAttempts(), @@ -828,7 +828,7 @@ export class GeminiChat { let finishReason: FinishReason | undefined; for await (const chunk of streamResponse) { - const candidateWithReason = chunk?.candidates?.find( + const candidateWithReason = chunk.candidates?.find( (candidate) => candidate.finishReason, ); if (candidateWithReason) { @@ -863,7 +863,7 @@ export class GeminiChat { } const hookSystem = this.config.getHookSystem(); - if (originalRequest && chunk && hookSystem) { + if (hookSystem) { const hookResult = await hookSystem.fireAfterModelEvent( originalRequest, chunk, @@ -893,7 +893,8 @@ export class GeminiChat { for (const part of modelResponseParts) { const lastPart = consolidatedParts[consolidatedParts.length - 1]; if ( - lastPart?.text && + lastPart && + lastPart.text && isValidNonThoughtTextPart(lastPart) && isValidNonThoughtTextPart(part) ) { @@ -978,10 +979,10 @@ export class GeminiChat { toolCalls: CompletedToolCall[], ): void { const toolCallRecords = toolCalls.map((call) => { - const resultDisplayRaw = call.response?.resultDisplay; + const resultDisplayRaw = call.response.resultDisplay; const resultDisplay = typeof resultDisplayRaw === 'string' || - (typeof resultDisplayRaw === 'object' && resultDisplayRaw !== null) + typeof resultDisplayRaw === 'object' ? resultDisplayRaw : undefined; @@ -989,7 +990,7 @@ export class GeminiChat { id: call.request.callId, name: call.request.name, args: call.request.args, - result: call.response?.responseParts || null, + result: call.response.responseParts || null, status: call.status, timestamp: new Date().toISOString(), resultDisplay, @@ -1007,7 +1008,7 @@ export class GeminiChat { return; } - const thoughtPart = content.parts[0]; + const thoughtPart = content.parts![0]; if (thoughtPart.text) { // Extract subject and description using the same logic as turn.ts const rawText = thoughtPart.text; diff --git a/packages/core/src/core/localLiteRtLmClient.ts b/packages/core/src/core/localLiteRtLmClient.ts index 798dcb5765..791d1dd43f 100644 --- a/packages/core/src/core/localLiteRtLmClient.ts +++ b/packages/core/src/core/localLiteRtLmClient.ts @@ -55,7 +55,7 @@ export class LocalLiteRtLmClient { if (reminder) { const lastContent = geminiContents.at(-1); - if (lastContent?.role === 'user' && lastContent.parts?.[0]?.text) { + if (lastContent?.role === 'user' && lastContent.parts[0]?.text) { lastContent.parts[0].text += `\n\n${reminder}`; } } diff --git a/packages/core/src/core/loggingContentGenerator.ts b/packages/core/src/core/loggingContentGenerator.ts index 60144740c2..e7d69be7f9 100644 --- a/packages/core/src/core/loggingContentGenerator.ts +++ b/packages/core/src/core/loggingContentGenerator.ts @@ -212,7 +212,7 @@ export class LoggingContentGenerator implements ContentGenerator { const genConfig = this.config.getContentGeneratorConfig(); // Case 2: Using an API key for Vertex AI. - if (genConfig?.vertexai) { + if (genConfig.vertexai) { const location = process.env['GOOGLE_CLOUD_LOCATION']; if (location) { return { address: `${location}-aiplatform.googleapis.com`, port: 443 }; @@ -252,7 +252,7 @@ export class LoggingContentGenerator implements ContentGenerator { candidates: responseCandidates, response_id: responseId, }, - this.config.getContentGeneratorConfig()?.authType, + this.config.getContentGeneratorConfig().authType, usageMetadata, responseText, role, @@ -330,7 +330,7 @@ export class LoggingContentGenerator implements ContentGenerator { generate_content_config: generationConfig, server: serverDetails, }, - this.config.getContentGeneratorConfig()?.authType, + this.config.getContentGeneratorConfig().authType, errorType, isStructuredError(error) ? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index 6d65596ce4..591acea6a9 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -79,9 +79,9 @@ describe('Core System Prompt (prompts.ts)', () => { // Stub process.platform to 'linux' by default for deterministic snapshots across OSes mockPlatform('linux'); - vi.stubEnv('SANDBOX', undefined); - vi.stubEnv('GEMINI_SYSTEM_MD', undefined); - vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', undefined); + vi.stubEnv('SANDBOX', ''); + vi.stubEnv('GEMINI_SYSTEM_MD', ''); + vi.stubEnv('GEMINI_WRITE_SYSTEM_MD', ''); mockConfig = { getToolRegistry: vi.fn().mockReturnValue({ getAllToolNames: vi.fn().mockReturnValue(['grep_search', 'glob']), diff --git a/packages/core/src/core/turn.ts b/packages/core/src/core/turn.ts index 4fd6af2185..8c9c2505ea 100644 --- a/packages/core/src/core/turn.ts +++ b/packages/core/src/core/turn.ts @@ -269,7 +269,7 @@ export class Turn { ); for await (const streamEvent of responseStream) { - if (signal?.aborted) { + if (signal.aborted) { yield { type: GeminiEventType.UserCancelled }; return; } diff --git a/packages/core/src/hooks/hookEventHandler.ts b/packages/core/src/hooks/hookEventHandler.ts index 7fa45e3271..f30bde30ae 100644 --- a/packages/core/src/hooks/hookEventHandler.ts +++ b/packages/core/src/hooks/hookEventHandler.ts @@ -372,7 +372,7 @@ export class HookEventHandler { const transcriptPath = this.config .getGeminiClient() - ?.getChatRecordingService() + .getChatRecordingService() ?.getConversationFilePath() ?? ''; return { diff --git a/packages/core/src/hooks/hookRunner.ts b/packages/core/src/hooks/hookRunner.ts index 4f44958787..ce18df659b 100644 --- a/packages/core/src/hooks/hookRunner.ts +++ b/packages/core/src/hooks/hookRunner.ts @@ -418,12 +418,12 @@ export class HookRunner { } // Collect stdout - child.stdout?.on('data', (data: Buffer) => { + child.stdout.on('data', (data: Buffer) => { stdout += data.toString(); }); // Collect stderr - child.stderr?.on('data', (data: Buffer) => { + child.stderr.on('data', (data: Buffer) => { stderr += data.toString(); }); diff --git a/packages/core/src/hooks/hookSystem.ts b/packages/core/src/hooks/hookSystem.ts index f748665985..eebe4e88c8 100644 --- a/packages/core/src/hooks/hookSystem.ts +++ b/packages/core/src/hooks/hookSystem.ts @@ -292,8 +292,8 @@ export class HookSystem { beforeModelOutput.applyLLMRequestModifications(llmRequest); return { blocked: false, - modifiedConfig: modifiedRequest?.config, - modifiedContents: modifiedRequest?.contents, + modifiedConfig: modifiedRequest.config, + modifiedContents: modifiedRequest.contents, }; } diff --git a/packages/core/src/hooks/types.ts b/packages/core/src/hooks/types.ts index 9c6217ffa4..6b271a4039 100644 --- a/packages/core/src/hooks/types.ts +++ b/packages/core/src/hooks/types.ts @@ -424,7 +424,7 @@ export class AfterModelHookOutput extends DefaultHookOutput { const hookResponse = this.hookSpecificOutput[ 'llm_response' ] as Partial; - if (hookResponse?.candidates?.[0]?.content?.parts?.length) { + if (hookResponse.candidates?.[0]?.content?.parts?.length) { // Convert hook format to SDK format return defaultHookTranslator.fromHookLLMResponse( // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion diff --git a/packages/core/src/ide/ide-client.ts b/packages/core/src/ide/ide-client.ts index 373df31f5f..14fa0c291f 100644 --- a/packages/core/src/ide/ide-client.ts +++ b/packages/core/src/ide/ide-client.ts @@ -452,7 +452,7 @@ export class IdeClient { // Don't log an error if the method is not found, which is a common case. if ( error instanceof Error && - !error.message?.includes('Method not found') + !error.message.includes('Method not found') ) { logger.error(`Error discovering tools from IDE: ${error.message}`); } else { diff --git a/packages/core/src/ide/ide-installer.ts b/packages/core/src/ide/ide-installer.ts index 886670d4f8..262a7e4e17 100644 --- a/packages/core/src/ide/ide-installer.ts +++ b/packages/core/src/ide/ide-installer.ts @@ -99,7 +99,7 @@ async function findCommand( } } else if (platform === 'linux') { // Linux - const linuxConfig = appConfigs[appname]?.linux; + const linuxConfig = appConfigs[appname].linux; if (linuxConfig) { locations.push( `/usr/share/${linuxConfig.appBinary}/bin/${linuxConfig.appBinary}`, @@ -178,7 +178,7 @@ class VsCodeInstaller implements IdeInstaller { if (result.status !== 0) { throw new Error( - `Failed to install extension: ${result.stderr?.toString()}`, + `Failed to install extension: ${result.stderr.toString()}`, ); } @@ -228,7 +228,7 @@ class PositronInstaller implements IdeInstaller { if (result.status !== 0) { throw new Error( - `Failed to install extension: ${result.stderr?.toString()}`, + `Failed to install extension: ${result.stderr.toString()}`, ); } @@ -281,7 +281,7 @@ class AntigravityInstaller implements IdeInstaller { if (result.status !== 0) { throw new Error( - `Failed to install extension: ${result.stderr?.toString()}`, + `Failed to install extension: ${result.stderr.toString()}`, ); } diff --git a/packages/core/src/mcp/google-auth-provider.test.ts b/packages/core/src/mcp/google-auth-provider.test.ts index f535f17d83..a35ad179f2 100644 --- a/packages/core/src/mcp/google-auth-provider.test.ts +++ b/packages/core/src/mcp/google-auth-provider.test.ts @@ -93,6 +93,7 @@ describe('GoogleCredentialProvider', () => { mockGetAccessToken = vi.fn(); mockClient = { getAccessToken: mockGetAccessToken, + credentials: { expiry_date: null }, }; (GoogleAuth.prototype.getClient as Mock).mockResolvedValue(mockClient); provider = new GoogleCredentialProvider(validConfig); diff --git a/packages/core/src/mcp/google-auth-provider.ts b/packages/core/src/mcp/google-auth-provider.ts index a4f61c3139..6f404352d0 100644 --- a/packages/core/src/mcp/google-auth-provider.ts +++ b/packages/core/src/mcp/google-auth-provider.ts @@ -49,7 +49,7 @@ export class GoogleCredentialProvider implements McpAuthProvider { ); } - const scopes = this.config?.oauth?.scopes; + const scopes = this.config.oauth?.scopes; if (!scopes || scopes.length === 0) { throw new Error( 'Scopes must be provided in the oauth config for Google Credentials provider', @@ -98,7 +98,7 @@ export class GoogleCredentialProvider implements McpAuthProvider { token_type: 'Bearer', }; - const expiryTime = client.credentials?.expiry_date; + const expiryTime = client.credentials.expiry_date; if (expiryTime) { this.tokenExpiryTime = expiryTime; this.cachedToken = newToken; diff --git a/packages/core/src/mcp/token-storage/file-token-storage.ts b/packages/core/src/mcp/token-storage/file-token-storage.ts index 97eae56194..04e8845df2 100644 --- a/packages/core/src/mcp/token-storage/file-token-storage.ts +++ b/packages/core/src/mcp/token-storage/file-token-storage.ts @@ -82,10 +82,8 @@ export class FileTokenStorage extends BaseTokenStorage { return new Map(); } if ( - err.message?.includes('Invalid encrypted data format') || - err.message?.includes( - 'Unsupported state or unable to authenticate data', - ) + err.message.includes('Invalid encrypted data format') || + err.message.includes('Unsupported state or unable to authenticate data') ) { // Decryption failed - this can happen when switching between auth types // or if the file is genuinely corrupted. diff --git a/packages/core/src/policy/policy-engine.ts b/packages/core/src/policy/policy-engine.ts index 03087716ff..f53efeae3b 100644 --- a/packages/core/src/policy/policy-engine.ts +++ b/packages/core/src/policy/policy-engine.ts @@ -396,8 +396,8 @@ export class PolicyEngine { isShellCommand = true; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const args = toolCall.args as { command?: string; dir_path?: string }; - command = args?.command; - shellDirPath = args?.dir_path; + command = args.command; + shellDirPath = args.dir_path; } // Find the first matching rule (already sorted by priority) diff --git a/packages/core/src/prompts/promptProvider.ts b/packages/core/src/prompts/promptProvider.ts index 9b8759c2af..46fcb06073 100644 --- a/packages/core/src/prompts/promptProvider.ts +++ b/packages/core/src/prompts/promptProvider.ts @@ -52,10 +52,10 @@ export class PromptProvider { const approvalMode = config.getApprovalMode?.() ?? ApprovalMode.DEFAULT; const isPlanMode = approvalMode === ApprovalMode.PLAN; const isYoloMode = approvalMode === ApprovalMode.YOLO; - const skills = config.getSkillManager().getSkills(); - const toolNames = config.getToolRegistry().getAllToolNames(); + const skills = config.getSkillManager?.()?.getSkills() ?? []; + const toolNames = config.getToolRegistry?.()?.getAllToolNames() ?? []; const enabledToolNames = new Set(toolNames); - const approvedPlanPath = config.getApprovedPlanPath(); + const approvedPlanPath = config.getApprovedPlanPath?.(); const desiredModel = resolveModel( config.getActiveModel(), diff --git a/packages/core/src/routing/modelRouterService.ts b/packages/core/src/routing/modelRouterService.ts index 1bd19f3622..d59bb1529f 100644 --- a/packages/core/src/routing/modelRouterService.ts +++ b/packages/core/src/routing/modelRouterService.ts @@ -6,6 +6,8 @@ import { GemmaClassifierStrategy } from './strategies/gemmaClassifierStrategy.js'; import type { Config } from '../config/config.js'; +import { resolveModel } from '../config/models.js'; +import { ModelRoutingEvent } from '../telemetry/types.js'; import type { RoutingContext, RoutingDecision, @@ -21,7 +23,6 @@ import { OverrideStrategy } from './strategies/overrideStrategy.js'; import { ApprovalModeStrategy } from './strategies/approvalModeStrategy.js'; import { logModelRouting } from '../telemetry/loggers.js'; -import { ModelRoutingEvent } from '../telemetry/types.js'; import { debugLogger } from '../utils/debugLogger.js'; /** @@ -47,7 +48,7 @@ export class ModelRouterService { strategies.push(new ApprovalModeStrategy()); // Then, if enabled, the Gemma classifier is used. - if (this.config.getGemmaModelRouterSettings()?.enabled) { + if (this.config.getGemmaModelRouterSettings().enabled) { strategies.push(new GemmaClassifierStrategy()); } @@ -74,7 +75,18 @@ export class ModelRouterService { */ async route(context: RoutingContext): Promise { const startTime = Date.now(); - let decision: RoutingDecision; + const fallbackModel = resolveModel( + this.config.getModel(), + this.config.getGemini31LaunchedSync(), + ); + let decision: RoutingDecision = { + model: fallbackModel, + metadata: { + source: 'fallback', + latencyMs: 0, + reasoning: 'Initialized with fallback model', + }, + }; const [enableNumericalRouting, thresholdValue] = await Promise.all([ this.config.getNumericalRoutingEnabled(), @@ -118,10 +130,10 @@ export class ModelRouterService { ); } finally { const event = new ModelRoutingEvent( - decision!.model, - decision!.metadata.source, - decision!.metadata.latencyMs, - decision!.metadata.reasoning, + decision.model, + decision.metadata.source, + decision.metadata.latencyMs, + decision.metadata.reasoning, failed, error_message, this.config.getApprovalMode(), diff --git a/packages/core/src/routing/strategies/classifierStrategy.ts b/packages/core/src/routing/strategies/classifierStrategy.ts index 5fd6208b15..17f79cc581 100644 --- a/packages/core/src/routing/strategies/classifierStrategy.ts +++ b/packages/core/src/routing/strategies/classifierStrategy.ts @@ -172,7 +172,7 @@ export class ClassifierStrategy implements RoutingStrategy { const reasoning = routerResponse.reasoning; const latencyMs = Date.now() - startTime; - const useGemini3_1 = (await config.getGemini31Launched?.()) ?? false; + const useGemini3_1 = (await config.getGemini31Launched()) ?? false; const useCustomToolModel = useGemini3_1 && config.getContentGeneratorConfig().authType === AuthType.USE_GEMINI; diff --git a/packages/core/src/routing/strategies/fallbackStrategy.ts b/packages/core/src/routing/strategies/fallbackStrategy.ts index 21a080e9da..99d434f34d 100644 --- a/packages/core/src/routing/strategies/fallbackStrategy.ts +++ b/packages/core/src/routing/strategies/fallbackStrategy.ts @@ -38,10 +38,7 @@ export class FallbackStrategy implements RoutingStrategy { const selection = selectModelForAvailability(config, requestedModel); - if ( - selection?.selectedModel && - selection.selectedModel !== requestedModel - ) { + if (selection.selectedModel && selection.selectedModel !== requestedModel) { return { model: selection.selectedModel, metadata: { diff --git a/packages/core/src/routing/strategies/gemmaClassifierStrategy.ts b/packages/core/src/routing/strategies/gemmaClassifierStrategy.ts index f1175cc101..8858611bda 100644 --- a/packages/core/src/routing/strategies/gemmaClassifierStrategy.ts +++ b/packages/core/src/routing/strategies/gemmaClassifierStrategy.ts @@ -174,7 +174,7 @@ ${formattedHistory} ): Promise { const startTime = Date.now(); const gemmaRouterSettings = config.getGemmaModelRouterSettings(); - if (!gemmaRouterSettings?.enabled) { + if (!gemmaRouterSettings.enabled) { return null; } diff --git a/packages/core/src/routing/strategies/numericalClassifierStrategy.ts b/packages/core/src/routing/strategies/numericalClassifierStrategy.ts index 39805fb43c..1ec8d6c12b 100644 --- a/packages/core/src/routing/strategies/numericalClassifierStrategy.ts +++ b/packages/core/src/routing/strategies/numericalClassifierStrategy.ts @@ -185,7 +185,7 @@ export class NumericalClassifierStrategy implements RoutingStrategy { config, config.getSessionId() || 'unknown-session', ); - const useGemini3_1 = (await config.getGemini31Launched?.()) ?? false; + const useGemini3_1 = (await config.getGemini31Launched()) ?? false; const useCustomToolModel = useGemini3_1 && config.getContentGeneratorConfig().authType === AuthType.USE_GEMINI; diff --git a/packages/core/src/safety/context-builder.ts b/packages/core/src/safety/context-builder.ts index c7b33f5e2f..e1cc183159 100644 --- a/packages/core/src/safety/context-builder.ts +++ b/packages/core/src/safety/context-builder.ts @@ -19,7 +19,7 @@ export class ContextBuilder { * Builds the full context object with all available data. */ buildFullContext(): SafetyCheckInput['context'] { - const clientHistory = this.config.getGeminiClient()?.getHistory() || []; + const clientHistory = this.config.getGeminiClient().getHistory() || []; const history = this.convertHistoryToTurns(clientHistory); debugLogger.debug( diff --git a/packages/core/src/services/chatCompressionService.ts b/packages/core/src/services/chatCompressionService.ts index 5303a1a82a..5f6b21c959 100644 --- a/packages/core/src/services/chatCompressionService.ts +++ b/packages/core/src/services/chatCompressionService.ts @@ -86,12 +86,14 @@ export function findCompressSplitPoint( // We found no split points after targetCharCount. // Check if it's safe to compress everything. - const lastContent = contents[contents.length - 1]; - if ( - lastContent?.role === 'model' && - !lastContent?.parts?.some((part) => part.functionCall) - ) { - return contents.length; + if (contents.length > 0) { + const lastContent = contents[contents.length - 1]; + if ( + lastContent.role === 'model' && + !lastContent.parts?.some((part) => part.functionCall) + ) { + return contents.length; + } } // Can't compress everything so just compress at last splitpoint. diff --git a/packages/core/src/services/loopDetectionService.ts b/packages/core/src/services/loopDetectionService.ts index 54ac5d8d50..084de7627b 100644 --- a/packages/core/src/services/loopDetectionService.ts +++ b/packages/core/src/services/loopDetectionService.ts @@ -594,10 +594,7 @@ export class LoopDetectionService { role: LlmRole.UTILITY_LOOP_DETECTOR, }); - if ( - result && - typeof result['unproductive_state_confidence'] === 'number' - ) { + if (typeof result['unproductive_state_confidence'] === 'number') { return result; } return null; diff --git a/packages/core/src/services/shellExecutionService.ts b/packages/core/src/services/shellExecutionService.ts index fdb2ca79b5..a4add86666 100644 --- a/packages/core/src/services/shellExecutionService.ts +++ b/packages/core/src/services/shellExecutionService.ts @@ -834,7 +834,7 @@ export class ShellExecutionService { aborted: abortSignal.aborted, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment pid: ptyProcess.pid, - executionMethod: ptyInfo?.name ?? 'node-pty', + executionMethod: ptyInfo.name ?? 'node-pty', }); }; diff --git a/packages/core/src/skills/skillLoader.ts b/packages/core/src/skills/skillLoader.ts index e746caa179..146a7457ab 100644 --- a/packages/core/src/skills/skillLoader.ts +++ b/packages/core/src/skills/skillLoader.ts @@ -181,7 +181,7 @@ export async function loadSkillFromFile( name: sanitizedName, description: frontmatter.description, location: filePath, - body: match[2]?.trim() ?? '', + body: match[2].trim() ?? '', }; } catch (error) { debugLogger.log(`Error parsing skill file ${filePath}:`, error); diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 8646a3f6d4..04e1d8dbcb 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -275,7 +275,7 @@ export class ClearcutLogger { private constructor(config: Config) { this.config = config; this.events = new FixedDeque(Array, MAX_EVENTS); - this.promptId = config?.getSessionId() ?? ''; + this.promptId = config.getSessionId() ?? ''; this.installationManager = new InstallationManager(); this.userAccountManager = new UserAccountManager(); @@ -288,7 +288,7 @@ export class ClearcutLogger { } static getInstance(config?: Config): ClearcutLogger | undefined { - if (config === undefined || !config?.getUsageStatisticsEnabled()) + if (config === undefined || !config.getUsageStatisticsEnabled()) return undefined; if (!ClearcutLogger.instance) { ClearcutLogger.instance = new ClearcutLogger(config); @@ -1731,7 +1731,7 @@ export class ClearcutLogger { gemini_cli_key: EventMetadataKey.GEMINI_CLI_ACTIVE_APPROVAL_MODE, value: typeof this.config?.getPolicyEngine === 'function' && - typeof this.config.getPolicyEngine()?.getApprovalMode === 'function' + typeof this.config.getPolicyEngine().getApprovalMode === 'function' ? this.config.getPolicyEngine().getApprovalMode() : '', }, @@ -1739,7 +1739,7 @@ export class ClearcutLogger { if (this.config?.getExperiments()) { defaultLogMetadata.push({ gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXPERIMENT_IDS, - value: this.config?.getExperiments()?.experimentIds.toString() ?? 'NA', + value: this.config.getExperiments()?.experimentIds.toString() ?? 'NA', }); } return [...data, ...defaultLogMetadata]; diff --git a/packages/core/src/telemetry/gcp-exporters.test.ts b/packages/core/src/telemetry/gcp-exporters.test.ts index 6415104d53..fd31723980 100644 --- a/packages/core/src/telemetry/gcp-exporters.test.ts +++ b/packages/core/src/telemetry/gcp-exporters.test.ts @@ -149,6 +149,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -173,6 +175,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -211,6 +215,8 @@ describe('GCP Exporters', () => { hrTimeObserved: [1234567890, 123456789], severityNumber, body: 'Test message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -240,6 +246,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -268,6 +276,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message 1', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -276,6 +286,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message 2', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -314,6 +326,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -355,6 +369,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; @@ -383,6 +399,8 @@ describe('GCP Exporters', () => { hrTime: [1234567890, 123456789], hrTimeObserved: [1234567890, 123456789], body: 'Test log message', + attributes: {}, + resource: { attributes: {} }, } as unknown as ReadableLogRecord, ]; diff --git a/packages/core/src/telemetry/gcp-exporters.ts b/packages/core/src/telemetry/gcp-exporters.ts index 3bf1781b87..d40a80771c 100644 --- a/packages/core/src/telemetry/gcp-exporters.ts +++ b/packages/core/src/telemetry/gcp-exporters.ts @@ -76,7 +76,7 @@ export class GcpLogExporter implements LogRecordExporter { }, { ...log.attributes, - ...log.resource?.attributes, + ...log.resource.attributes, message: log.body, }, ); diff --git a/packages/core/src/telemetry/types.ts b/packages/core/src/telemetry/types.ts index a84f051cac..c7e2f4caf6 100644 --- a/packages/core/src/telemetry/types.ts +++ b/packages/core/src/telemetry/types.ts @@ -425,7 +425,7 @@ export class ApiRequestEvent implements BaseTelemetryEvent { const { 'gen_ai.response.model': _, ...requestConventionAttributes } = getConventionAttributes({ model: this.model, - auth_type: config.getContentGeneratorConfig()?.authType, + auth_type: config.getContentGeneratorConfig().authType, }); const attributes: LogAttributes = { ...getCommonAttributes(config), diff --git a/packages/core/src/tools/activate-skill.ts b/packages/core/src/tools/activate-skill.ts index 21ee2e98c6..d70ae9e217 100644 --- a/packages/core/src/tools/activate-skill.ts +++ b/packages/core/src/tools/activate-skill.ts @@ -71,10 +71,6 @@ class ActivateSkillToolInvocation extends BaseToolInvocation< protected override async getConfirmationDetails( _abortSignal: AbortSignal, ): Promise { - if (!this.messageBus) { - return false; - } - const skillName = this.params.name; const skill = this.config.getSkillManager().getSkill(skillName); diff --git a/packages/core/src/tools/ask-user.ts b/packages/core/src/tools/ask-user.ts index 621d4c10d1..d60a1eb47f 100644 --- a/packages/core/src/tools/ask-user.ts +++ b/packages/core/src/tools/ask-user.ts @@ -44,7 +44,7 @@ export class AskUserTool extends BaseDeclarativeTool< protected override validateToolParamValues( params: AskUserParams, ): string | null { - if (!params.questions || params.questions.length === 0) { + if (params.questions.length === 0) { return 'At least one question is required.'; } @@ -73,10 +73,7 @@ export class AskUserTool extends BaseDeclarativeTool< ) { return `Question ${i + 1}, option ${j + 1}: 'label' is required and must be a non-empty string.`; } - if ( - opt.description === undefined || - typeof opt.description !== 'string' - ) { + if (typeof opt.description !== 'string') { return `Question ${i + 1}, option ${j + 1}: 'description' is required and must be a string.`; } } @@ -184,7 +181,7 @@ export class AskUserInvocation extends BaseToolInvocation< ? `**User answered:**\n${answerEntries .map(([index, answer]) => { const question = this.params.questions[parseInt(index, 10)]; - const category = question?.header ?? `Q${index}`; + const category = question.header; const prefix = ` ${category} → `; const indent = ' '.repeat(prefix.length); diff --git a/packages/core/src/tools/diff-utils.ts b/packages/core/src/tools/diff-utils.ts index 9c44a1756e..f968d2427f 100644 --- a/packages/core/src/tools/diff-utils.ts +++ b/packages/core/src/tools/diff-utils.ts @@ -25,12 +25,12 @@ export function getDiffContextSnippet( for (const change of changes) { if (change.added) { - ranges.push({ start: newLineIdx, end: newLineIdx + (change.count ?? 0) }); - newLineIdx += change.count ?? 0; + ranges.push({ start: newLineIdx, end: newLineIdx + change.count }); + newLineIdx += change.count; } else if (change.removed) { ranges.push({ start: newLineIdx, end: newLineIdx }); } else { - newLineIdx += change.count ?? 0; + newLineIdx += change.count; } } diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts index a7169e99f2..fbfc3eefa3 100644 --- a/packages/core/src/tools/edit.ts +++ b/packages/core/src/tools/edit.ts @@ -56,7 +56,6 @@ import { EDIT_DEFINITION } from './definitions/coreTools.js'; import { resolveToolDeclaration } from './definitions/resolver.js'; import { detectOmissionPlaceholders } from './omissionPlaceholderDetector.js'; -const ENABLE_FUZZY_MATCH_RECOVERY = true; const FUZZY_MATCH_THRESHOLD = 0.1; // Allow up to 10% weighted difference const WHITESPACE_PENALTY_FACTOR = 0.1; // Whitespace differences cost 10% of a character difference interface ReplacementContext { @@ -88,7 +87,7 @@ export function applyReplacement( return oldString === '' ? newString : ''; } // If oldString is empty and it's not a new file, do not modify the content. - if (oldString === '' && !isNewFile) { + if (oldString === '') { return currentContent; } @@ -326,11 +325,8 @@ export async function calculateReplacement( return regexResult; } - let fuzzyResult; - if ( - ENABLE_FUZZY_MATCH_RECOVERY && - (fuzzyResult = await calculateFuzzyReplacement(config, context)) - ) { + const fuzzyResult = await calculateFuzzyReplacement(config, context); + if (fuzzyResult) { return fuzzyResult; } diff --git a/packages/core/src/tools/get-internal-docs.ts b/packages/core/src/tools/get-internal-docs.ts index 23bda8f4dd..40d2ba3557 100644 --- a/packages/core/src/tools/get-internal-docs.ts +++ b/packages/core/src/tools/get-internal-docs.ts @@ -54,7 +54,7 @@ async function getDocsRoot(): Promise { return false; }; - while (true) { + for (;;) { const candidate = path.join(searchDir, 'docs'); if (await isDocsDir(candidate)) { return candidate; diff --git a/packages/core/src/tools/glob.ts b/packages/core/src/tools/glob.ts index c2f3c4ab54..46f193c106 100644 --- a/packages/core/src/tools/glob.ts +++ b/packages/core/src/tools/glob.ts @@ -17,7 +17,6 @@ import { } from './tools.js'; import { shortenPath, makeRelative } from '../utils/paths.js'; import { type Config } from '../config/config.js'; -import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js'; import { ToolErrorType } from './tool-error.js'; import { GLOB_TOOL_NAME, GLOB_DISPLAY_NAME } from './tool-names.js'; import { getErrorMessage } from '../utils/errors.js'; @@ -184,13 +183,11 @@ class GlobToolInvocation extends BaseToolInvocation< const { filteredPaths, ignoredCount } = fileDiscovery.filterFilesWithReport(relativePaths, { respectGitIgnore: - this.params?.respect_git_ignore ?? - this.config.getFileFilteringOptions().respectGitIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGitIgnore, + this.params.respect_git_ignore ?? + this.config.getFileFilteringOptions().respectGitIgnore, respectGeminiIgnore: - this.params?.respect_gemini_ignore ?? - this.config.getFileFilteringOptions().respectGeminiIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGeminiIgnore, + this.params.respect_gemini_ignore ?? + this.config.getFileFilteringOptions().respectGeminiIgnore, }); const filteredAbsolutePaths = new Set( @@ -201,7 +198,7 @@ class GlobToolInvocation extends BaseToolInvocation< filteredAbsolutePaths.has(entry.fullpath()), ); - if (!filteredEntries || filteredEntries.length === 0) { + if (filteredEntries.length === 0) { let message = `No files found matching pattern "${this.params.pattern}"`; if (searchDirectories.length === 1) { message += ` within ${searchDirectories[0]}`; diff --git a/packages/core/src/tools/ls.ts b/packages/core/src/tools/ls.ts index 9456f8ffc9..04589ee41d 100644 --- a/packages/core/src/tools/ls.ts +++ b/packages/core/src/tools/ls.ts @@ -15,8 +15,7 @@ import { type ToolResult, } from './tools.js'; import { makeRelative, shortenPath } from '../utils/paths.js'; -import type { Config } from '../config/config.js'; -import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js'; +import { type Config } from '../config/config.js'; import { ToolErrorType } from './tool-error.js'; import { LS_TOOL_NAME } from './tool-names.js'; import { debugLogger } from '../utils/debugLogger.js'; @@ -167,15 +166,6 @@ class LSToolInvocation extends BaseToolInvocation { try { const stats = await fs.stat(resolvedDirPath); - if (!stats) { - // fs.statSync throws on non-existence, so this check might be redundant - // but keeping for clarity. Error message adjusted. - return this.errorResult( - `Error: Directory not found or inaccessible: ${resolvedDirPath}`, - `Directory not found or inaccessible.`, - ToolErrorType.FILE_NOT_FOUND, - ); - } if (!stats.isDirectory()) { return this.errorResult( `Error: Path is not a directory: ${resolvedDirPath}`, @@ -205,12 +195,10 @@ class LSToolInvocation extends BaseToolInvocation { fileDiscovery.filterFilesWithReport(relativePaths, { respectGitIgnore: this.params.file_filtering_options?.respect_git_ignore ?? - this.config.getFileFilteringOptions().respectGitIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGitIgnore, + this.config.getFileFilteringOptions().respectGitIgnore, respectGeminiIgnore: this.params.file_filtering_options?.respect_gemini_ignore ?? - this.config.getFileFilteringOptions().respectGeminiIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGeminiIgnore, + this.config.getFileFilteringOptions().respectGeminiIgnore, }); const entries = []; @@ -325,7 +313,7 @@ export class LSTool extends BaseDeclarativeTool { return new LSToolInvocation( this.config, params, - messageBus ?? this.messageBus, + messageBus, _toolName, _toolDisplayName, ); diff --git a/packages/core/src/tools/mcp-client-manager.ts b/packages/core/src/tools/mcp-client-manager.ts index 96d7abf55c..75c71c7eb3 100644 --- a/packages/core/src/tools/mcp-client-manager.ts +++ b/packages/core/src/tools/mcp-client-manager.ts @@ -281,7 +281,7 @@ export class McpClientManager { // Check if blocked by admin settings (allowlist/excludelist) if (this.isBlockedBySettings(name)) { if (!this.blockedMcpServers.find((s) => s.name === name)) { - this.blockedMcpServers?.push({ + this.blockedMcpServers.push({ name, extensionName: config.extension?.name ?? '', }); diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index 6e0d1066de..1182501af8 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -974,7 +974,7 @@ class LenientJsonSchemaValidator implements jsonSchemaValidator { } catch (error) { debugLogger.warn( `Failed to compile MCP tool output schema (${ - (schema as Record)?.['$id'] ?? '' + (schema as Record)['$id'] ?? '' }): ${error instanceof Error ? error.message : String(error)}. ` + 'Skipping output validation for this tool.', ); @@ -1149,7 +1149,7 @@ export async function discoverTools( mcpServerName, toolDef.name, toolDef.description ?? '', - toolDef.inputSchema ?? { type: 'object', properties: {} }, + toolDef.inputSchema, messageBus, mcpServerConfig.trust, isReadOnly, @@ -1175,10 +1175,7 @@ export async function discoverTools( } return discoveredTools; } catch (error) { - if ( - error instanceof Error && - !error.message?.includes('Method not found') - ) { + if (error instanceof Error && !error.message.includes('Method not found')) { cliConfig.emitMcpDiagnostic( 'error', `Error discovering tools from ${mcpServerName}: ${getErrorMessage( @@ -1303,7 +1300,7 @@ export async function discoverPrompts( })); } catch (error) { // It's okay if the method is not found, which is a common case. - if (error instanceof Error && error.message?.includes('Method not found')) { + if (error instanceof Error && error.message.includes('Method not found')) { return []; } cliConfig.emitMcpDiagnostic( @@ -1347,11 +1344,11 @@ async function listResources( }, ListResourcesResultSchema, ); - resources.push(...(response.resources ?? [])); + resources.push(...response.resources); cursor = response.nextCursor ?? undefined; } while (cursor); } catch (error) { - if (error instanceof Error && error.message?.includes('Method not found')) { + if (error instanceof Error && error.message.includes('Method not found')) { return []; } cliConfig.emitMcpDiagnostic( @@ -1399,10 +1396,7 @@ export async function invokeMcpPrompt( return response; } catch (error) { - if ( - error instanceof Error && - !error.message?.includes('Method not found') - ) { + if (error instanceof Error && !error.message.includes('Method not found')) { cliConfig.emitMcpDiagnostic( 'error', `Error invoking prompt '${promptName}' from ${mcpServerName} ${promptParams}: ${getErrorMessage( @@ -1707,7 +1701,6 @@ export async function connectToMcpServer( // Continue to OAuth handling below (after SSE fallback section) } else if ( // If not 401, and HTTP failed with url without explicit type, try SSE fallback - firstAttemptError && mcpServerConfig.url && !mcpServerConfig.type && !mcpServerConfig.httpUrl @@ -2036,7 +2029,7 @@ export async function createTransport( if (authProvider === undefined) { // Check if we have OAuth configuration or stored tokens let accessToken: string | null = null; - if (mcpServerConfig.oauth?.enabled && mcpServerConfig.oauth) { + if (mcpServerConfig.oauth?.enabled) { const tokenStorage = new MCPOAuthTokenStorage(); const mcpAuthProvider = new MCPOAuthProvider(tokenStorage); accessToken = await mcpAuthProvider.getValidToken( diff --git a/packages/core/src/tools/mcp-tool.ts b/packages/core/src/tools/mcp-tool.ts index 2c52c72573..0c8fd06bfa 100644 --- a/packages/core/src/tools/mcp-tool.ts +++ b/packages/core/src/tools/mcp-tool.ts @@ -159,7 +159,7 @@ export class DiscoveredMCPToolInvocation extends BaseToolInvocation< // This is needed because CallToolResults should return errors inside the response. // ref: https://modelcontextprotocol.io/specification/2025-06-18/schema#calltoolresult isMCPToolError(rawResponseParts: Part[]): boolean { - const functionResponse = rawResponseParts?.[0]?.functionResponse; + const functionResponse = rawResponseParts[0]?.functionResponse; const response = functionResponse?.response; interface McpError { @@ -174,7 +174,7 @@ export class DiscoveredMCPToolInvocation extends BaseToolInvocation< } // Legacy check for nested error object (keep for backward compatibility if any tools rely on it) - const error = (response as { error?: McpError })?.error; + const error = (response as { error?: McpError }).error; const isError = error?.isError; if (error && (isError === true || isError === 'true')) { @@ -379,10 +379,10 @@ function transformResourceBlock( toolName: string, ): Part | Part[] | null { const resource = block.resource; - if (resource?.text) { + if (resource.text) { return { text: resource.text }; } - if (resource?.blob) { + if (resource.blob) { const mimeType = resource.mimeType || 'application/octet-stream'; return [ { @@ -412,7 +412,7 @@ function transformResourceLinkBlock(block: McpResourceLinkBlock): Part { * @returns A clean Part[] array ready for the scheduler. */ function transformMcpContentToParts(sdkResponse: Part[]): Part[] { - const funcResponse = sdkResponse?.[0]?.functionResponse; + const funcResponse = sdkResponse[0]?.functionResponse; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const mcpContent = funcResponse?.response?.['content'] as McpContentBlock[]; const toolName = funcResponse?.name || 'unknown tool'; @@ -452,7 +452,7 @@ function transformMcpContentToParts(sdkResponse: Part[]): Part[] { */ function getStringifiedResultForDisplay(rawResponse: Part[]): string { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const mcpContent = rawResponse?.[0]?.functionResponse?.response?.[ + const mcpContent = rawResponse[0]?.functionResponse?.response?.[ 'content' ] as McpContentBlock[]; @@ -471,11 +471,11 @@ function getStringifiedResultForDisplay(rawResponse: Part[]): string { case 'resource_link': return `[Link to ${block.title || block.name}: ${block.uri}]`; case 'resource': - if (block.resource?.text) { + if (block.resource.text) { return block.resource.text; } return `[Embedded Resource: ${ - block.resource?.mimeType || 'unknown type' + block.resource.mimeType || 'unknown type' }]`; default: return `[Unknown content type: ${(block as { type: string }).type}]`; diff --git a/packages/core/src/tools/modifiable-tool.ts b/packages/core/src/tools/modifiable-tool.ts index 69abeacb82..1edf63ed8b 100644 --- a/packages/core/src/tools/modifiable-tool.ts +++ b/packages/core/src/tools/modifiable-tool.ts @@ -191,8 +191,8 @@ export async function modifyWithEditor( : await modifyContext.getProposedContent(originalParams); const { oldPath, newPath, dirPath } = createTempFilesForModify( - currentContent ?? '', - proposedContent ?? '', + currentContent, + proposedContent, modifyContext.getFilePath(originalParams), ); diff --git a/packages/core/src/tools/read-many-files.ts b/packages/core/src/tools/read-many-files.ts index c9c4e230e6..8de1cf6a9a 100644 --- a/packages/core/src/tools/read-many-files.ts +++ b/packages/core/src/tools/read-many-files.ts @@ -24,10 +24,7 @@ import { type ProcessedFileReadResult, } from '../utils/fileUtils.js'; import type { PartListUnion } from '@google/genai'; -import { - type Config, - DEFAULT_FILE_FILTERING_OPTIONS, -} from '../config/config.js'; +import { type Config } from '../config/config.js'; import { FileOperation } from '../telemetry/metrics.js'; import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js'; import { logFileOperation } from '../telemetry/loggers.js'; @@ -215,12 +212,10 @@ ${finalExclusionPatternsForDescription fileDiscovery.filterFilesWithReport(relativeEntries, { respectGitIgnore: this.params.file_filtering_options?.respect_git_ignore ?? - this.config.getFileFilteringOptions().respectGitIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGitIgnore, + this.config.getFileFilteringOptions().respectGitIgnore, respectGeminiIgnore: this.params.file_filtering_options?.respect_gemini_ignore ?? - this.config.getFileFilteringOptions().respectGeminiIgnore ?? - DEFAULT_FILE_FILTERING_OPTIONS.respectGeminiIgnore, + this.config.getFileFilteringOptions().respectGeminiIgnore, }); for (const relativePath of filteredPaths) { diff --git a/packages/core/src/tools/ripGrep.ts b/packages/core/src/tools/ripGrep.ts index 000b4f0071..7b50f1781f 100644 --- a/packages/core/src/tools/ripGrep.ts +++ b/packages/core/src/tools/ripGrep.ts @@ -675,7 +675,7 @@ export class RipGrepTool extends BaseDeclarativeTool< this.config, this.fileDiscoveryService, params, - messageBus ?? this.messageBus, + messageBus, _toolName, _toolDisplayName, ); diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 4ea83b0af4..ae9324ae99 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -280,7 +280,7 @@ export class ShellToolInvocation extends BaseToolInvocation< }, ); - if (pid) { + if (pid !== undefined) { if (setPidCallback) { setPidCallback(pid); } diff --git a/packages/core/src/tools/tool-registry.ts b/packages/core/src/tools/tool-registry.ts index bdd8c7d403..2723715119 100644 --- a/packages/core/src/tools/tool-registry.ts +++ b/packages/core/src/tools/tool-registry.ts @@ -63,31 +63,28 @@ class DiscoveredToolInvocation extends BaseToolInvocation< let stdout = ''; let stderr = ''; - let error: Error | null = null; - let code: number | null = null; - let signal: NodeJS.Signals | null = null; - await new Promise((resolve) => { + const result = await new Promise<{ + code: number | null; + signal: NodeJS.Signals | null; + error: Error | null; + }>((resolve) => { + let error: Error | null = null; const onStdout = (data: Buffer) => { - stdout += data?.toString(); + stdout += data.toString(); }; const onStderr = (data: Buffer) => { - stderr += data?.toString(); + stderr += data.toString(); }; const onError = (err: Error) => { error = err; }; - const onClose = ( - _code: number | null, - _signal: NodeJS.Signals | null, - ) => { - code = _code; - signal = _signal; + const onClose = (code: number | null, signal: NodeJS.Signals | null) => { cleanup(); - resolve(); + resolve({ code, signal, error }); }; const cleanup = () => { @@ -106,14 +103,16 @@ class DiscoveredToolInvocation extends BaseToolInvocation< child.on('close', onClose); }); + const { code, signal, error } = result; + // if there is any error, non-zero exit code, signal, or stderr, return error details instead of stdout if (error || code !== 0 || signal || stderr) { const llmContent = [ `Stdout: ${stdout || '(empty)'}`, `Stderr: ${stderr || '(empty)'}`, - `Error: ${error ?? '(none)'}`, - `Exit Code: ${code ?? '(none)'}`, - `Signal: ${signal ?? '(none)'}`, + `Error: ${error ? error.message : '(none)'}`, + `Exit Code: ${code !== null ? code : '(none)'}`, + `Signal: ${signal !== null ? signal : '(none)'}`, ].join('\n'); return { llmContent, @@ -644,7 +643,7 @@ export class ToolRegistry { const serverTools: AnyDeclarativeTool[] = []; for (const tool of this.getActiveTools()) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - if ((tool as DiscoveredMCPTool)?.serverName === serverName) { + if ((tool as DiscoveredMCPTool).serverName === serverName) { serverTools.push(tool); } } diff --git a/packages/core/src/tools/tools.ts b/packages/core/src/tools/tools.ts index 0a82cc1510..14a47f8d60 100644 --- a/packages/core/src/tools/tools.ts +++ b/packages/core/src/tools/tools.ts @@ -117,11 +117,6 @@ export abstract class BaseToolInvocation< ); } - if (decision === 'ASK_USER') { - return this.getConfirmationDetails(abortSignal); - } - - // Default to confirmation details if decision is unknown (should not happen with exhaustive policy) return this.getConfirmationDetails(abortSignal); } @@ -167,10 +162,6 @@ export abstract class BaseToolInvocation< protected async getConfirmationDetails( _abortSignal: AbortSignal, ): Promise { - if (!this.messageBus) { - return false; - } - const confirmationDetails: ToolCallConfirmationDetails = { type: 'info', title: `Confirm: ${this._toolDisplayName || this._toolName}`, @@ -185,8 +176,8 @@ export abstract class BaseToolInvocation< protected getMessageBusDecision( abortSignal: AbortSignal, ): Promise<'ALLOW' | 'DENY' | 'ASK_USER'> { - if (!this.messageBus || !this._toolName) { - // If there's no message bus, we can't make a decision, so we allow. + if (!this._toolName) { + // If there's no tool name, we can't make a decision, so we allow. // The legacy confirmation flow will still apply if the tool needs it. return Promise.resolve('ALLOW'); } @@ -205,11 +196,6 @@ export abstract class BaseToolInvocation< }; return new Promise<'ALLOW' | 'DENY' | 'ASK_USER'>((resolve) => { - if (!this.messageBus) { - resolve('ALLOW'); - return; - } - let timeoutId: NodeJS.Timeout | null = null; let unsubscribe: (() => void) | null = null; @@ -260,7 +246,7 @@ export abstract class BaseToolInvocation< responseHandler, ); unsubscribe = () => { - this.messageBus?.unsubscribe( + this.messageBus.unsubscribe( MessageBusType.TOOL_CONFIRMATION_RESPONSE, responseHandler, ); diff --git a/packages/core/src/tools/web-fetch.ts b/packages/core/src/tools/web-fetch.ts index 3170227188..8acdccf6b0 100644 --- a/packages/core/src/tools/web-fetch.ts +++ b/packages/core/src/tools/web-fetch.ts @@ -344,7 +344,7 @@ ${textContent} const chunks: Uint8Array[] = []; let totalLength = 0; try { - while (true) { + for (;;) { const { done, value } = await reader.read(); if (done) break; totalLength += value.length; diff --git a/packages/core/src/tools/web-search.ts b/packages/core/src/tools/web-search.ts index 2756599b28..d140f85934 100644 --- a/packages/core/src/tools/web-search.ts +++ b/packages/core/src/tools/web-search.ts @@ -239,7 +239,7 @@ export class WebSearchTool extends BaseDeclarativeTool< return new WebSearchToolInvocation( this.config, params, - messageBus ?? this.messageBus, + messageBus, _toolName, _toolDisplayName, ); diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts index f78821f0e1..bd24ae3cd3 100644 --- a/packages/core/src/tools/write-file.ts +++ b/packages/core/src/tools/write-file.ts @@ -266,10 +266,7 @@ class WriteFileToolInvocation extends BaseToolInvocation< } = correctedContentResult; // fileExists is true if the file existed (and was readable or unreadable but caught by readError). // fileExists is false if the file did not exist (ENOENT). - const isNewFile = - !fileExists || - (correctedContentResult.error !== undefined && - !correctedContentResult.fileExists); + const isNewFile = !fileExists; try { const dirName = path.dirname(this.resolvedPath); @@ -298,9 +295,7 @@ class WriteFileToolInvocation extends BaseToolInvocation< // If there was a readError, originalContent in correctedContentResult is '', // but for the diff, we want to show the original content as it was before the write if possible. // However, if it was unreadable, currentContentForDiff will be empty. - const currentContentForDiff = correctedContentResult.error - ? '' // Or some indicator of unreadable content - : originalContent; + const currentContentForDiff = originalContent; const fileDiff = Diff.createPatch( fileName, @@ -483,7 +478,7 @@ export class WriteFileTool return new WriteFileToolInvocation( this.config, params, - messageBus ?? this.messageBus, + messageBus, this.name, this.displayName, ); diff --git a/packages/core/src/tools/write-todos.ts b/packages/core/src/tools/write-todos.ts index dd7ab780e6..5efc61325c 100644 --- a/packages/core/src/tools/write-todos.ts +++ b/packages/core/src/tools/write-todos.ts @@ -45,7 +45,7 @@ class WriteTodosToolInvocation extends BaseToolInvocation< } getDescription(): string { - const count = this.params.todos?.length ?? 0; + const count = this.params.todos.length; if (count === 0) { return 'Cleared todo list'; } @@ -56,7 +56,7 @@ class WriteTodosToolInvocation extends BaseToolInvocation< _signal: AbortSignal, _updateOutput?: (output: string) => void, ): Promise { - const todos = this.params.todos ?? []; + const todos = this.params.todos; const todoListString = todos .map( (todo, index) => `${index + 1}. [${todo.status}] ${todo.description}`, @@ -101,19 +101,21 @@ export class WriteTodosTool extends BaseDeclarativeTool< protected override validateToolParamValues( params: WriteTodosToolParams, ): string | null { - const todos = params?.todos; - if (!params || !Array.isArray(todos)) { + const todos = params.todos; + if (!Array.isArray(todos)) { return '`todos` parameter must be an array'; } for (const todo of todos) { - if (typeof todo !== 'object' || todo === null) { + if (typeof todo !== 'object') { return 'Each todo item must be an object'; } if (typeof todo.description !== 'string' || !todo.description.trim()) { return 'Each todo must have a non-empty description string'; } - if (!TODO_STATUSES.includes(todo.status)) { + if ( + !TODO_STATUSES.includes(todo.status as (typeof TODO_STATUSES)[number]) + ) { return `Each todo must have a valid status (${TODO_STATUSES.join(', ')})`; } } diff --git a/packages/core/src/utils/bfsFileSearch.ts b/packages/core/src/utils/bfsFileSearch.ts index 460abfec27..93dab352be 100644 --- a/packages/core/src/utils/bfsFileSearch.ts +++ b/packages/core/src/utils/bfsFileSearch.ts @@ -81,7 +81,7 @@ export async function bfsFileSearch( } catch (error) { // Warn user that a directory could not be read, as this affects search results. // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const message = (error as Error)?.message ?? 'Unknown error'; + const message = (error as Error).message ?? 'Unknown error'; debugLogger.warn( `[WARN] Skipping unreadable directory: ${currentDir} (${message})`, ); @@ -155,7 +155,7 @@ export function bfsFileSearchSync( ); } catch (error) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const message = (error as Error)?.message ?? 'Unknown error'; + const message = (error as Error).message ?? 'Unknown error'; debugLogger.warn( `[WARN] Skipping unreadable directory: ${currentDir} (${message})`, ); diff --git a/packages/core/src/utils/compatibility.ts b/packages/core/src/utils/compatibility.ts index 15b2ae24b4..0fb3bf10ad 100644 --- a/packages/core/src/utils/compatibility.ts +++ b/packages/core/src/utils/compatibility.ts @@ -42,7 +42,7 @@ export function isAppleTerminal(): boolean { */ export function supports256Colors(): boolean { // Check if stdout supports at least 8-bit color depth - if (process.stdout.getColorDepth && process.stdout.getColorDepth() >= 8) { + if (process.stdout.getColorDepth() >= 8) { return true; } @@ -68,7 +68,7 @@ export function supportsTrueColor(): boolean { } // Check if stdout supports 24-bit color depth - if (process.stdout.getColorDepth && process.stdout.getColorDepth() >= 24) { + if (process.stdout.getColorDepth() >= 24) { return true; } diff --git a/packages/core/src/utils/errors.ts b/packages/core/src/utils/errors.ts index a390abcdc4..40a9c077b8 100644 --- a/packages/core/src/utils/errors.ts +++ b/packages/core/src/utils/errors.ts @@ -59,7 +59,7 @@ export function getErrorType(error: unknown): string { // Return constructor name if the generic 'Error' name is used (for custom errors) return error.name === 'Error' - ? (error.constructor?.name ?? 'Error') + ? (error.constructor.name ?? 'Error') : error.name; } @@ -153,23 +153,15 @@ function isResponseData(data: unknown): data is ResponseData { return false; } const error = candidate.error; - if (typeof error !== 'object' || error === null) { + if (typeof error !== 'object') { return false; // error property exists but is not an object (could be undefined, but we checked 'in') } // Optional properties check - if ( - 'code' in error && - typeof error.code !== 'number' && - error.code !== undefined - ) { + if ('code' in error && typeof error.code !== 'number') { return false; } - if ( - 'message' in error && - typeof error.message !== 'string' && - error.message !== undefined - ) { + if ('message' in error && typeof error.message !== 'string') { return false; } diff --git a/packages/core/src/utils/fileUtils.ts b/packages/core/src/utils/fileUtils.ts index 2497439a63..53e26f40ad 100644 --- a/packages/core/src/utils/fileUtils.ts +++ b/packages/core/src/utils/fileUtils.ts @@ -37,7 +37,7 @@ export async function loadWasmBinary( ): Promise { try { const module = await dynamicImport(); - if (module?.default instanceof Uint8Array) { + if (module.default instanceof Uint8Array) { return module.default; } } catch (error) { diff --git a/packages/core/src/utils/filesearch/result-cache.ts b/packages/core/src/utils/filesearch/result-cache.ts index cf0c2b4b12..ccfdd6d389 100644 --- a/packages/core/src/utils/filesearch/result-cache.ts +++ b/packages/core/src/utils/filesearch/result-cache.ts @@ -43,7 +43,7 @@ export class ResultCache { // This finds the most specific, already-cached query that is a prefix // of the current query. let bestBaseQuery = ''; - for (const key of this.cache?.keys?.() ?? []) { + for (const key of this.cache.keys() ?? []) { if (query.startsWith(key) && key.length > bestBaseQuery.length) { bestBaseQuery = key; } diff --git a/packages/core/src/utils/generateContentResponseUtilities.ts b/packages/core/src/utils/generateContentResponseUtilities.ts index fdd5dff81a..125fbfff84 100644 --- a/packages/core/src/utils/generateContentResponseUtilities.ts +++ b/packages/core/src/utils/generateContentResponseUtilities.ts @@ -36,7 +36,7 @@ function toParts(input: PartListUnion): Part[] { for (const part of Array.isArray(input) ? input : [input]) { if (typeof part === 'string') { parts.push({ text: part }); - } else if (part) { + } else { parts.push(part); } } @@ -130,9 +130,6 @@ export function convertToFunctionResponse( } export function getResponseTextFromParts(parts: Part[]): string | undefined { - if (!parts) { - return undefined; - } const textSegments = parts .map((part) => part.text) .filter((text): text is string => typeof text === 'string'); @@ -160,9 +157,6 @@ export function getFunctionCalls( export function getFunctionCallsFromParts( parts: Part[], ): FunctionCall[] | undefined { - if (!parts) { - return undefined; - } const functionCallParts = parts .filter((part) => !!part.functionCall) // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion diff --git a/packages/core/src/utils/getFolderStructure.ts b/packages/core/src/utils/getFolderStructure.ts index 6e1814cd90..7e26a5c10c 100644 --- a/packages/core/src/utils/getFolderStructure.ts +++ b/packages/core/src/utils/getFolderStructure.ts @@ -130,8 +130,8 @@ async function readFullStructure( const filesInCurrentDir: string[] = []; const subFoldersInCurrentDir: FullFolderInfo[] = []; const filterFileOptions: FilterFilesOptions = { - respectGitIgnore: options.fileFilteringOptions?.respectGitIgnore, - respectGeminiIgnore: options.fileFilteringOptions?.respectGeminiIgnore, + respectGitIgnore: options.fileFilteringOptions.respectGitIgnore, + respectGeminiIgnore: options.fileFilteringOptions.respectGeminiIgnore, }; // Process files first in the current directory diff --git a/packages/core/src/utils/gitUtils.ts b/packages/core/src/utils/gitUtils.ts index 9ac8f1b04a..bce5d648ad 100644 --- a/packages/core/src/utils/gitUtils.ts +++ b/packages/core/src/utils/gitUtils.ts @@ -16,7 +16,7 @@ export function isGitRepository(directory: string): boolean { try { let currentDir = path.resolve(directory); - while (true) { + for (;;) { const gitDir = path.join(currentDir, '.git'); // Check if .git exists (either as directory or file for worktrees) @@ -50,7 +50,7 @@ export function findGitRoot(directory: string): string | null { try { let currentDir = path.resolve(directory); - while (true) { + for (;;) { const gitDir = path.join(currentDir, '.git'); if (fs.existsSync(gitDir)) { diff --git a/packages/core/src/utils/googleErrors.ts b/packages/core/src/utils/googleErrors.ts index f7f972f568..f1cbf5eac7 100644 --- a/packages/core/src/utils/googleErrors.ts +++ b/packages/core/src/utils/googleErrors.ts @@ -271,7 +271,7 @@ function fromGaxiosError(errorObj: object): ErrorShape | undefined { data = data[0]; } - if (typeof data === 'object' && data !== null) { + if (typeof data === 'object') { if ('error' in data) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const potentialError = (data as { error: unknown }).error; @@ -334,7 +334,7 @@ function fromApiError(errorObj: object): ErrorShape | undefined { data = data[0]; } - if (typeof data === 'object' && data !== null) { + if (typeof data === 'object') { if ('error' in data) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const potentialError = (data as { error: unknown }).error; diff --git a/packages/core/src/utils/googleQuotaErrors.ts b/packages/core/src/utils/googleQuotaErrors.ts index d0c251e839..5d0ecfb7bf 100644 --- a/packages/core/src/utils/googleQuotaErrors.ts +++ b/packages/core/src/utils/googleQuotaErrors.ts @@ -375,16 +375,9 @@ export function classifyGoogleError(error: unknown): unknown { // If we reached this point and the status is still 429 or 499, we return retryable. if (status === 429 || status === 499) { const errorMessage = - googleApiError?.message || + googleApiError.message || (error instanceof Error ? error.message : String(error)); - return new RetryableQuotaError( - errorMessage, - googleApiError ?? { - code: status, - message: errorMessage, - details: [], - }, - ); + return new RetryableQuotaError(errorMessage, googleApiError); } return error; // Fallback to original error if no specific classification fits. } diff --git a/packages/core/src/utils/ignorePatterns.ts b/packages/core/src/utils/ignorePatterns.ts index 9f9776db53..da22668ccf 100644 --- a/packages/core/src/utils/ignorePatterns.ts +++ b/packages/core/src/utils/ignorePatterns.ts @@ -165,8 +165,8 @@ export class FileExclusions { // Add custom patterns from configuration // TODO: getCustomExcludes method needs to be implemented in Config interface - if (this.config) { - const configCustomExcludes = this.config.getCustomExcludes?.() ?? []; + if (this.config && typeof this.config.getCustomExcludes === 'function') { + const configCustomExcludes = this.config.getCustomExcludes() ?? []; patterns.push(...configCustomExcludes); } @@ -200,7 +200,10 @@ export class FileExclusions { // Add any custom patterns from config if available // TODO: getCustomExcludes method needs to be implemented in Config interface - const configPatterns = this.config?.getCustomExcludes?.() ?? []; + const configPatterns = + typeof this.config?.getCustomExcludes === 'function' + ? (this.config.getCustomExcludes() ?? []) + : []; return [...corePatterns, ...configPatterns, ...additionalExcludes]; } diff --git a/packages/core/src/utils/memoryDiscovery.ts b/packages/core/src/utils/memoryDiscovery.ts index 677c571bec..773a979d5e 100644 --- a/packages/core/src/utils/memoryDiscovery.ts +++ b/packages/core/src/utils/memoryDiscovery.ts @@ -43,7 +43,7 @@ export interface GeminiFileContent { async function findProjectRoot(startDir: string): Promise { let currentDir = normalizePath(startDir); - while (true) { + for (;;) { const gitPath = path.join(currentDir, '.git'); try { const stats = await fs.lstat(gitPath); @@ -441,7 +441,7 @@ async function findUpwardGeminiFiles( ); } - while (true) { + for (;;) { if (currentDir === globalGeminiDir) { break; } diff --git a/packages/core/src/utils/memoryImportProcessor.ts b/packages/core/src/utils/memoryImportProcessor.ts index bf20bd6c13..238e614061 100644 --- a/packages/core/src/utils/memoryImportProcessor.ts +++ b/packages/core/src/utils/memoryImportProcessor.ts @@ -51,7 +51,7 @@ export interface ProcessImportsResult { // Helper to find the project root (looks for .git directory) async function findProjectRoot(startDir: string): Promise { let currentDir = path.resolve(startDir); - while (true) { + for (;;) { const gitPath = path.join(currentDir, '.git'); try { const stats = await fs.lstat(gitPath); diff --git a/packages/core/src/utils/nextSpeakerChecker.ts b/packages/core/src/utils/nextSpeakerChecker.ts index a5ce286feb..bf1393fb4c 100644 --- a/packages/core/src/utils/nextSpeakerChecker.ts +++ b/packages/core/src/utils/nextSpeakerChecker.ts @@ -70,10 +70,7 @@ export async function checkNextSpeaker( // If the last message is a user message containing only function_responses, // then the model should speak next. - if ( - lastComprehensiveMessage && - isFunctionResponse(lastComprehensiveMessage) - ) { + if (isFunctionResponse(lastComprehensiveMessage)) { return { reasoning: 'The last message was a function response, so the model should speak next.', @@ -82,7 +79,6 @@ export async function checkNextSpeaker( } if ( - lastComprehensiveMessage && lastComprehensiveMessage.role === 'model' && lastComprehensiveMessage.parts && lastComprehensiveMessage.parts.length === 0 @@ -98,7 +94,7 @@ export async function checkNextSpeaker( // Things checked out. Let's proceed to potentially making an LLM request. const lastMessage = curatedHistory[curatedHistory.length - 1]; - if (!lastMessage || lastMessage.role !== 'model') { + if (lastMessage.role !== 'model') { // Cannot determine next speaker if the last turn wasn't from the model // or if history is empty. return null; @@ -121,7 +117,6 @@ export async function checkNextSpeaker( })) as unknown as NextSpeakerResponse; if ( - parsedResponse && parsedResponse.next_speaker && ['user', 'model'].includes(parsedResponse.next_speaker) ) { diff --git a/packages/core/src/utils/schemaValidator.ts b/packages/core/src/utils/schemaValidator.ts index db5dee11ba..5a07936bb1 100644 --- a/packages/core/src/utils/schemaValidator.ts +++ b/packages/core/src/utils/schemaValidator.ts @@ -50,7 +50,6 @@ const DRAFT_2020_12_SCHEMA = 'https://json-schema.org/draft/2020-12/schema'; function getValidator(schema: AnySchema): Ajv { if ( typeof schema === 'object' && - schema !== null && '$schema' in schema && schema.$schema === DRAFT_2020_12_SCHEMA ) { @@ -93,7 +92,7 @@ export class SchemaValidator { debugLogger.warn( `Failed to compile schema (${ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - (schema as Record)?.['$schema'] ?? '' + (schema as Record)['$schema'] ?? '' }): ${error instanceof Error ? error.message : String(error)}. ` + 'Skipping parameter validation.', ); @@ -125,7 +124,7 @@ export class SchemaValidator { debugLogger.warn( `Failed to validate schema (${ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - (schema as Record)?.['$schema'] ?? '' + (schema as Record)['$schema'] ?? '' }): ${error instanceof Error ? error.message : String(error)}. ` + 'Skipping schema validation.', ); diff --git a/packages/core/src/utils/shell-utils.ts b/packages/core/src/utils/shell-utils.ts index 00b3533400..ba1a51bb71 100644 --- a/packages/core/src/utils/shell-utils.ts +++ b/packages/core/src/utils/shell-utils.ts @@ -272,10 +272,7 @@ function normalizeCommandName(raw: string): string { function extractNameFromNode(node: Node): string | null { switch (node.type) { case 'command': { - const nameNode = node.childForFieldName('name'); - if (!nameNode) { - return null; - } + const nameNode = node.childForFieldName('name')!; return normalizeCommandName(nameNode.text); } case 'declaration_command': @@ -878,12 +875,8 @@ export async function* execStreaming( } } - if (child.exitCode !== null) { - checkExit(child.exitCode); - } else { - child.on('close', (code) => checkExit(code)); - child.on('error', (err) => reject(err)); - } + child.on('close', (code) => checkExit(code)); + child.on('error', (err) => reject(err)); }); } } diff --git a/packages/sdk/src/session.ts b/packages/sdk/src/session.ts index 8332ef29d0..9ce078e702 100644 --- a/packages/sdk/src/session.ts +++ b/packages/sdk/src/session.ts @@ -183,7 +183,7 @@ export class GeminiCliSession { { text: prompt }, ]; - while (true) { + for (;;) { if (typeof this.instructions === 'function') { const context: SessionContext = { sessionId, diff --git a/packages/vscode-ide-companion/src/diff-manager.ts b/packages/vscode-ide-companion/src/diff-manager.ts index 83cc97984a..5415fb14ea 100644 --- a/packages/vscode-ide-companion/src/diff-manager.ts +++ b/packages/vscode-ide-companion/src/diff-manager.ts @@ -145,7 +145,7 @@ export class DiffManager { if (uriToClose) { const rightDoc = await vscode.workspace.openTextDocument(uriToClose); - const modifiedContent = rightDoc.getText() ?? ''; + const modifiedContent = rightDoc.getText(); await this.closeDiffEditor(uriToClose); return modifiedContent; } @@ -162,7 +162,7 @@ export class DiffManager { } const rightDoc = await vscode.workspace.openTextDocument(rightDocUri); - const modifiedContent = rightDoc.getText() ?? ''; + const modifiedContent = rightDoc.getText(); await this.closeDiffEditor(rightDocUri); this.onDidChangeEmitter.fire( @@ -188,7 +188,7 @@ export class DiffManager { } const rightDoc = await vscode.workspace.openTextDocument(rightDocUri); - const modifiedContent = rightDoc.getText() ?? ''; + const modifiedContent = rightDoc.getText(); await this.closeDiffEditor(rightDocUri); this.onDidChangeEmitter.fire( diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 456ec6e872..f20d7acd3a 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -223,15 +223,11 @@ export async function activate(context: vscode.ExtensionContext) { export async function deactivate(): Promise { log('Extension deactivated'); try { - if (ideServer) { - await ideServer.stop(); - } + await ideServer.stop(); } catch (err) { const message = err instanceof Error ? err.message : String(err); log(`Failed to stop IDE server during deactivation: ${message}`); } finally { - if (logger) { - logger.dispose(); - } + logger.dispose(); } } diff --git a/packages/vscode-ide-companion/src/ide-server.ts b/packages/vscode-ide-companion/src/ide-server.ts index a4adad6db9..54e6b7ac64 100644 --- a/packages/vscode-ide-companion/src/ide-server.ts +++ b/packages/vscode-ide-companion/src/ide-server.ts @@ -214,7 +214,7 @@ export class IDEServer { const sessionId = getSessionId(req); let transport: StreamableHTTPServerTransport; - if (sessionId && this.transports[sessionId]) { + if (sessionId && sessionId in this.transports) { transport = this.transports[sessionId]; } else if (!sessionId && isInitializeRequest(req.body)) { transport = new StreamableHTTPServerTransport({ @@ -294,7 +294,7 @@ export class IDEServer { const handleSessionRequest = async (req: Request, res: Response) => { const sessionId = getSessionId(req); - if (!sessionId || !this.transports[sessionId]) { + if (!sessionId || !(sessionId in this.transports)) { this.log('Invalid or missing session ID'); res.status(400).send('Invalid or missing session ID'); return; diff --git a/packages/vscode-ide-companion/src/open-files-manager.ts b/packages/vscode-ide-companion/src/open-files-manager.ts index 3fae487ad3..3de93a22c4 100644 --- a/packages/vscode-ide-companion/src/open-files-manager.ts +++ b/packages/vscode-ide-companion/src/open-files-manager.ts @@ -146,12 +146,10 @@ export class OpenFilesManager { return; } - file.cursor = editor.selection.active - ? { - line: editor.selection.active.line + 1, - character: editor.selection.active.character + 1, - } - : undefined; + file.cursor = { + line: editor.selection.active.line + 1, + character: editor.selection.active.character + 1, + }; let selectedText: string | undefined = editor.document.getText(editor.selection) || undefined;