From befd365b46ef5ce48b908168beeb4419300d97a2 Mon Sep 17 00:00:00 2001 From: Charles Ouimet Date: Thu, 14 May 2026 13:06:09 -0400 Subject: [PATCH] [issues/529] Add Gemini Code Assist as built-in AI assistant destination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Google Gemini Code Assist (`google.geminicodeassist`) as a fourth built-in AI assistant destination alongside Claude Code, Cursor AI, and GitHub Copilot Chat. The integration mirrors the Claude Code pattern exactly: a typed destination kind, focus command wiring, availability detection via extension + command probing, cold-start re-focus loop with configurable timing, and a dedicated bind command. Seven integration tests cover bind, availability, picker ordering, and cold-start validation. README messaging was also updated across the board to consistently include Gemini in named-tool lists, surface terminal AI tools alongside extensions, and replace "AI model" with "AI tool" throughout. - **Gemini Code Assist destination** — new `'gemini-code-assist'` kind in `DESTINATION_KINDS` and `AI_ASSISTANT_KINDS`; `GeminiCodeAssistBindOptions` in the `BindOptions` union; five new message codes (display name, error, info-not-available, user-instructions, status-bar jump success); EN strings wired in `messages.en.ts` - **Detection utility** — `isGeminiCodeAssistAvailable.ts` checks command `cloudcode.gemini.chatView.focus` first, falls back to extension presence `google.geminicodeassist`; 100% coverage unit test suite - **Cold-start re-focus loop** — `aiAssistantFocusCommands.ts` includes `GEMINI_CODE_ASSIST_FOCUS_COMMANDS` with the same `getColdRefocus()` factory the Claude Code destination uses; `destinationBuilders.ts` entry keyed by `'google.geminicodeassist'` with lazy validation lambda; shared defaults `DEFAULT_DESTINATIONS_GEMINI_COLD_START_DELAY_MS` (2500) and `DEFAULT_DESTINATIONS_GEMINI_COLD_REFOCUS_INTERVAL_MS` (300) in `settingDefaults.ts`; configurable via `rangelink.destinations.gemini.coldStartDelayMs` and `coldRefocusIntervalMs` - **Availability & routing** — `DestinationAvailabilityService.ts` info-map entry; `PasteDestinationManager.ts` error-code and send-path switch cases; `Destination.ts` display-name entry - **Picker ordering** — `'gemini-code-assist'` inserted between `'claude-code'` and `'cursor-ai'` in `DESTINATION_PICKER_SEQUENCE` and group map - **Command wiring** — `CMD_BIND_TO_GEMINI_CODE_ASSIST` in `commandIds.ts`; registered in `wireSubscriptions.ts`; `package.json` contributes `"Bind to Gemini Code Assist"` command - **Integration tests** — 7 Gemini tests (gemini-code-assist-001 through 007) covering bind, availability, picker ordering, and cold-start config validation; `waitForExtensionActive()` helper for Gemini's ~4-6s activation delay; `resetRangelinkSettings()` now clears all 3 VS Code config scopes (Global, Workspace, WorkspaceFolder) to prevent config leakage between tests - **README improvements** — Added Gemini Code Assist to all named-tool lists (lines 24, 31); added "or runs in a terminal" to AI-agnostic messaging (line 27); changed "Choice of AI model" to "Choice of AI tool" with product names instead of model names (line 41); added separate "Terminal AI tllet in "Perfect For" section (line 48-49); normalized "Claude Code" naming throughout - **Documentation** — CHANGELOG: Added entry for Gemini Code Assist Integration under `[Unreleased]`; README updated (see above) - [x] All 1912 unit tests pass (109 suites) - [x] New unit tests for `isGeminiCodeAssistAvailable.ts` (100% coverage) - [x] 7 new integration tests for Gemini Code Assist in `builtInAiAssistants.test.ts` - [x] QA YAML updated with new test cases (bind-to-destination-029 through bind-to-destination-038) - [ ] Manual testing: bind to Gemini Code Assist and verify auto-send behavior with the extension installed Closes https://github.com/couimet/rangelink/issues/529 --- .../.vscode-test.with-extensions.mjs | 6 +- .../rangelink-vscode-extension/CHANGELOG.md | 10 +- packages/rangelink-vscode-extension/README.md | 44 ++- .../rangelink-vscode-extension/package.json | 20 ++ .../qa/qa-test-cases-v1.1.0.yaml | 106 ++++++ .../setup-integration-test-settings.js | 5 + .../scripts/test-release-run.sh | 2 +- .../__integration-tests__/helpers/index.ts | 1 + .../helpers/settingsHelpers.ts | 2 + .../__integration-tests__/helpers/testEnv.ts | 28 ++ .../suite/bindToDestination.test.ts | 32 +- .../suite/builtInAiAssistants.test.ts | 328 +++++++++++++++++- .../suite/clipboardPreservation.test.ts | 56 +++ .../suite/commandRegistration.test.ts | 1 + .../suite/customAiAssistants.test.ts | 9 +- .../constants/packageJsonContracts.test.ts | 30 +- .../destinations/DestinationRegistry.test.ts | 3 +- .../PasteDestinationManager.test.ts | 107 ++++++ .../destinations/destinationBuilders.test.ts | 46 ++- .../buildDestinationQuickPickItems.test.ts | 91 +++++ .../src/__tests__/extension.test.ts | 115 +++--- .../createMockBindableQuickPickItem.ts | 3 +- .../helpers/createMockDestinationRegistry.ts | 13 +- .../createMockFocusCapabilityFactory.ts | 2 +- ...ckGeminiCodeAssistComposableDestination.ts | 26 ++ .../createMockGeminiCodeAssistDestination.ts | 22 ++ .../src/__tests__/helpers/index.ts | 3 + .../spyOnIsGeminiCodeAssistAvailable.ts | 9 + .../services/ClipboardRouter.test.ts | 2 +- .../src/__tests__/wireSubscriptions.test.ts | 4 +- .../src/constants/commandIds.ts | 1 + .../src/constants/settingDefaults.ts | 22 +- .../src/constants/settingKeys.ts | 4 + .../DestinationAvailabilityService.ts | 2 + .../src/destinations/DestinationRegistry.ts | 1 + .../destinations/PasteDestinationManager.ts | 3 + .../destinations/aiAssistantFocusCommands.ts | 4 + .../AIAssistantFocusCapability.ts | 2 +- .../src/destinations/capabilities/index.ts | 4 +- .../src/destinations/destinationBuilders.ts | 48 ++- .../src/destinations/index.ts | 1 + .../utils/buildDestinationQuickPickItems.ts | 9 +- .../src/i18n/messages.en.ts | 8 + .../src/services/ClipboardRouter.ts | 6 +- .../src/types/BindOptions.ts | 9 + .../src/types/DestinationKind.ts | 2 + .../src/types/MessageCode.ts | 5 + .../isClaudeCodeAvailable.test.ts | 38 +- .../aiAssistants/isCursorIDEDetected.test.ts | 10 + .../isGeminiCodeAssistAvailable.test.ts | 146 ++++++++ .../isGitHubCopilotChatAvailable.test.ts | 74 ++-- .../utils/aiAssistants/builtInAiAssistants.ts | 3 + .../src/utils/aiAssistants/index.ts | 2 + .../aiAssistants/isClaudeCodeAvailable.ts | 6 +- .../isGeminiCodeAssistAvailable.ts | 27 ++ .../isGitHubCopilotChatAvailable.ts | 19 +- .../src/wireSubscriptions.ts | 11 +- .../dummy-ai-extension/extension.js | 8 + .../dummy-ai-extension/package.json | 4 + 59 files changed, 1410 insertions(+), 195 deletions(-) create mode 100644 packages/rangelink-vscode-extension/src/__tests__/helpers/createMockGeminiCodeAssistComposableDestination.ts create mode 100644 packages/rangelink-vscode-extension/src/__tests__/helpers/createMockGeminiCodeAssistDestination.ts create mode 100644 packages/rangelink-vscode-extension/src/__tests__/helpers/spyOnIsGeminiCodeAssistAvailable.ts create mode 100644 packages/rangelink-vscode-extension/src/utils/__tests__/aiAssistants/isGeminiCodeAssistAvailable.test.ts create mode 100644 packages/rangelink-vscode-extension/src/utils/aiAssistants/builtInAiAssistants.ts create mode 100644 packages/rangelink-vscode-extension/src/utils/aiAssistants/isGeminiCodeAssistAvailable.ts diff --git a/packages/rangelink-vscode-extension/.vscode-test.with-extensions.mjs b/packages/rangelink-vscode-extension/.vscode-test.with-extensions.mjs index 177c2f2a8..915ae956a 100644 --- a/packages/rangelink-vscode-extension/.vscode-test.with-extensions.mjs +++ b/packages/rangelink-vscode-extension/.vscode-test.with-extensions.mjs @@ -3,9 +3,9 @@ import { defineConfig } from '@vscode/test-cli'; import { ASSISTED_TIMEOUT_MS, BASE_CONFIG, userDataDir } from './.vscode-test.base.mjs'; // Marketplace extensions installed before tests run. With these present, -// isClaudeCodeAvailable() returns true, enabling tests that verify real -// focus + paste behavior. -const MARKETPLACE_EXTENSIONS = ['anthropic.claude-code']; +// isGeminiCodeAssistAvailable() and isClaudeCodeAvailable() return true, +// enabling tests that verify real focus + paste behavior. +const MARKETPLACE_EXTENSIONS = ['google.geminicodeassist', 'anthropic.claude-code']; export default defineConfig([ { diff --git a/packages/rangelink-vscode-extension/CHANGELOG.md b/packages/rangelink-vscode-extension/CHANGELOG.md index 91d3f7537..9cd01e5c8 100644 --- a/packages/rangelink-vscode-extension/CHANGELOG.md +++ b/packages/rangelink-vscode-extension/CHANGELOG.md @@ -35,12 +35,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - When more open files exist beyond the inline list, extras collapse into "More files..." which opens a secondary picker - Secondary picker organizes all open files into "Active Files" section + per-"Tab Group N" sections - Escaping the secondary picker returns to the destination picker +- **Gemini Code Assist Integration** - Paste destination for Google's Gemini Code Assist (#529) Unreleased + - Automatically inserts links and selected text directly into Gemini Code Assist + - Requires [Gemini Code Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist) extension (`Google.geminicodeassist`) + - Configurable cold-start timing: `rangelink.destinations.gemini.coldStartDelayMs` (default: `2500`) and `rangelink.destinations.gemini.coldRefocusIntervalMs` (default: `300`) — tune for your machine's speed; increase the delay on slower hardware if the initial paste after binding silently fails to reach the Gemini panel - **Custom AI Assistants** - Connect RangeLink to any AI tool with a VS Code extension (#500) Unreleased - New setting `rangelink.customAiAssistants`: three-tier command schema — `insertCommands` (direct text delivery), `focusAndPasteCommands` (focus + auto-paste), `focusCommands` (focus + manual paste toast) - Custom assistants appear in the destination picker (R-D) alongside built-in destinations - Lazy tier resolution: RangeLink checks which commands are registered on first use and caches the winning tier — no per-operation overhead - Override built-in assistants (Cursor, Claude Code, Copilot) via config with automatic fallback to hardcoded commands - `${content}` template interpolation for insert commands with non-standard argument formats +- **Claude Code Cold-Start Timing** - Configurable cold-start settings for the Claude Code Extension destination Unreleased + - `rangelink.destinations.claudeCode.coldStartDelayMs` (default: `2500`) and `rangelink.destinations.claudeCode.coldRefocusIntervalMs` (default: `300`) — tune for your machine's speed; increase the delay on slower hardware if the initial paste after binding silently fails to reach the Claude Code panel - **Release Notifier** - Once-per-upgrade notification so new versions don't go unnoticed (#525) - On upgrade, shows "RangeLink updated to vX.Y.Z. See what changed!" with "What's New" and "Skip for this version" buttons - "What's New" opens the GitHub releases page; "Skip for this version" silences without opening the browser @@ -116,9 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Forgiving filename navigation** - Links with bare filenames (no directory path) now navigate when exactly one matching file exists in the workspace (#342) - Covers links generated by AI tools that omit directory prefixes (e.g., `RangeLinkNavigationHandler.ts#L10`) - When the filename is ambiguous (multiple matches) or not found, the existing "Cannot find file" warning is shown -- **Claude Code cold-start re-focus loop** - Adds configurable `coldStartDelayMs` and `coldRefocusIntervalMs` settings under `rangelink.destinations.claudeCode.*` to ensure the chat panel is ready before the first paste dispatch after binding. - -