Base branch: origin/main (stacked on Issue 2 = issues/547)
Parent: #547 (Issue 2 must merge first)
Depends on: Issue 2 for pasteTimingConstants.ts (was chatPasteConstants.ts, renamed in 547) and single-clipboard-write architecture
Context
Claude Code's chat panel has a webview initialization delay on first use — the panel needs time to become ready before it can accept a paste command. Without cold-start detection, the first R-L after binding sends the paste command into a panel that isn't listening yet, and the link is silently lost. On subsequent uses (warm path), a short 200ms delay suffices.
Gemini Code Assist has the same cold-start behaviour. The issues/529 branch already designed and implemented the cold-start infrastructure for both assistants. Issue 3 surgically extracts the shared infrastructure and the Claude Code-specific settings from issues/529, leaving Gemini-specific pieces (destination kind, availability detection, Gemini settings) for Issue 4.
The extraction source commits on issues/529:
86fb2efc — "Add Gemini cold-start settings with runtime validation and architecture cleanup" (shared infrastructure: ColdRefocusConfig, AIAssistantFocusCapability cold-start logic, FocusCapabilityFactory changes, ConfigReader on DestinationBuilderContext, Gemini settings)
946eed1f — "Add Claude Code cold-start settings matching Gemini pattern" (Claude Code-specific settings keys, defaults, getColdRefocus hook, package.json entries)
Issue 3 carries forward the shared infrastructure from 86fb2efc (minus Gemini-specific settings entries) and the Claude Code-specific work from 946eed1f.
Assumptions Made
FOCUS_TO_PASTE_DELAY_MS lives in pasteTimingConstants.ts (renamed from chatPasteConstants.ts in Issue 2, already on the current branch)
- The
AIAssistantInsertFactory clipboard-write removal from 86fb2efc is already done by Issue 2 (single clipboard write) — no need to redo
LazyResolvedFocusCapability.ts already exists on the current branch (it's on main now), so only the barrel export needs updating
EXTENSION_ID_CLAUDE_CODE and friends are already exported from src/utils/aiAssistants/ on main (added in the ce339481 Gemini base commit, but the constants may not exist yet — verify during implementation)
Plan
S1: Add ColdRefocusConfig interface (new file)
Create src/destinations/capabilities/ColdRefocusConfig.ts — 4-line interface:
export interface ColdRefocusConfig {
readonly totalMs: number;
readonly intervalMs: number;
}
Source: commit 86fb2efc, squashed.
S2: Add cold-start logic to AIAssistantFocusCapability
Modify src/destinations/capabilities/AIAssistantFocusCapability.ts:
- Add
panelIsWarm = false private field
- Add
getColdRefocus: (() => ColdRefocusConfig) | undefined constructor parameter (4th param, before insertFactory)
- Import
FOCUS_TO_PASTE_DELAY_MS from ../../constants/pasteTimingConstants (note: named pasteTimingConstants post-Issue-2 rename)
- Import
ColdRefocusConfig from ./ColdRefocusConfig
- After focus command succeeds: if
!panelIsWarm && coldRefocus, call refocusDuring(); otherwise wait FOCUS_TO_PASTE_DELAY_MS; then set panelIsWarm = true
- Add private
refocusDuring(context, refocus) method — loop at intervalMs intervals up to totalMs, re-sending focus commands each tick, stopping early if elapsed >= totalMs
- Update class JSDoc to mention cold-start behaviour and Claude Code
- Update tests: new file
src/__tests__/destinations/capabilities/AIAssistantFocusCapability.test.ts (~258 lines from 86fb2efc)
Source: commit 86fb2efc.
S3: Update FocusCapabilityFactory
Modify src/destinations/capabilities/FocusCapabilityFactory.ts:
- Import
ColdRefocusConfig
- Add
getColdRefocus: (() => ColdRefocusConfig) | undefined as 3rd param to createAIAssistantCapability(), pass it through to AIAssistantFocusCapability constructor
- Extract
createStandardAIAssistantInsertFactory() private method (DRY up the 2 call sites in buildCustomAIAssistantTiers and buildBuiltinFallbackTier)
- Update tests:
FocusCapabilityFactory.test.ts
Source: commit 86fb2efc.
S4: Add ConfigReader to DestinationBuilderContext
Modify src/destinations/DestinationRegistry.ts:
- Import
ConfigReader from ../config/ConfigReader
- Add
readonly configReader: ConfigReader to DestinationBuilderContext interface
- Add
configReader: ConfigReader parameter to constructor, assign to this.context
Modify src/createWiringServices.ts:
- Pass
configReader (already in scope) to new DestinationRegistry(...) constructor as 7th argument
Update tests that construct DestinationRegistry or mock DestinationBuilderContext.
Source: commit 86fb2efc.
S5: Add getColdRefocus to BuiltinAiAssistantDef and wire Claude Code
Modify src/destinations/destinationBuilders.ts:
- Import Claude Code setting keys and defaults
- Add optional
getColdRefocus?: (context: DestinationBuilderContext) => ColdRefocusConfig to BuiltinAiAssistantDef interface
- Add
getColdRefocus hook on the Claude Code entry: reads coldStartDelayMs and coldRefocusIntervalMs from config via context.configReader.getWithDefault(), validates totalMs > intervalMs (warns and falls back to defaults if misconfigured), returns { totalMs, intervalMs }
- Update
buildBuiltinAiAssistantDestination to pass def.getColdRefocus through to createAIAssistantCapability() (only if defined)
- Do NOT add
getColdRefocus to the Gemini entry (Gemini is Issue 4) — but the Gemini def already exists in the file from the ce339481 commit on main; leave it unchanged
Source: commits 86fb2efc (interface + wiring pattern) and 946eed1f (Claude Code getColdRefocus implementation).
S6: Add Claude Code cold-start setting keys and defaults
Modify src/constants/settingKeys.ts:
- Add
// Destination Settings section header
- Add
SETTINGS_DESTINATIONS_PREFIX = 'destinations.'
- Add
SETTING_DESTINATIONS_CLAUDE_CODE_COLD_REFOCUS_INTERVAL_MS and SETTING_DESTINATIONS_CLAUDE_CODE_COLD_START_DELAY_MS
- Do NOT add Gemini setting keys (Issue 4)
Modify src/constants/settingDefaults.ts:
- Add
// Destination Defaults section header
- Add
DEFAULT_DESTINATIONS_CLAUDE_CODE_COLD_REFOCUS_INTERVAL_MS = 300 and DEFAULT_DESTINATIONS_CLAUDE_CODE_COLD_START_DELAY_MS = 2500
- Do NOT add Gemini defaults (Issue 4)
Update contract tests: settingDefaults.test.ts, packageJsonContracts.test.ts.
Source: commit 946eed1f (Claude Code keys/defaults), commit 86fb2efc (section structure pattern).
S7: Add Claude Code cold-start settings to package.json
Modify package.json contributes.configuration:
- Add
rangelink.destinations.claudeCode.coldStartDelayMs (type: number, default: 2500, min: 500, max: 15000)
- Add
rangelink.destinations.claudeCode.coldRefocusIntervalMs (type: number, default: 300, min: 100, max: 5000)
- Do NOT add Gemini settings (Issue 4)
Source: commit 946eed1f.
S8: Update capabilities barrel export
Modify src/destinations/capabilities/index.ts:
- Export
ColdRefocusConfig type
- Export
LazyResolvedFocusCapability class (the class already exists on main; if not, it's on the issues/529 branch and needs to be extracted)
Source: commit 86fb2efc.
S9: Update unit tests
Update all affected test files:
- New:
src/__tests__/destinations/capabilities/AIAssistantFocusCapability.test.ts (~258 lines, 100% branch coverage — cold start, warm path, refocus loop timer, validation fallback)
- Modify:
FocusCapabilityFactory.test.ts — add undefined 3rd argument to createAIAssistantCapability calls, verify createStandardAIAssistantInsertFactory extraction
- Modify:
destinationBuilders.test.ts — add configReader to mock context, assert Claude Code def has getColdRefocus, assert it reads from correct setting keys, assert validation fallback when totalMs <= intervalMs
- Modify:
DestinationRegistry.test.ts — add mockConfigReader to constructor calls
- Modify:
settingDefaults.test.ts — add Claude Code default value assertions
- Modify:
packageJsonContracts.test.ts — add Claude Code setting schema assertions
S10: QA YAML — add Claude Code cold-start TCs
Append to qa/qa-test-cases-v1.1.0.yaml under the "Built-in AI Assistants" section:
claude-code-006: cold-start default settings produce correct ColdRefocusConfig (automated: true — unit test covers this)
claude-code-007: cold-start validation rejects invalid config (totalMs <= intervalMs) and falls back to defaults (automated: true — unit test)
(IDs 006-007 continue from existing claude-code-005.)
S11: Documentation
- CHANGELOG: add entry under
[Unreleased] for Claude Code cold-start settings
- README: add Claude Code entries to settings table with
<sup>Unreleased</sup> markers
Base branch: origin/main (stacked on Issue 2 = issues/547)
Parent: #547 (Issue 2 must merge first)
Depends on: Issue 2 for
pasteTimingConstants.ts(waschatPasteConstants.ts, renamed in 547) and single-clipboard-write architectureContext
Claude Code's chat panel has a webview initialization delay on first use — the panel needs time to become ready before it can accept a paste command. Without cold-start detection, the first R-L after binding sends the paste command into a panel that isn't listening yet, and the link is silently lost. On subsequent uses (warm path), a short 200ms delay suffices.
Gemini Code Assist has the same cold-start behaviour. The
issues/529branch already designed and implemented the cold-start infrastructure for both assistants. Issue 3 surgically extracts the shared infrastructure and the Claude Code-specific settings fromissues/529, leaving Gemini-specific pieces (destination kind, availability detection, Gemini settings) for Issue 4.The extraction source commits on
issues/529:86fb2efc— "Add Gemini cold-start settings with runtime validation and architecture cleanup" (shared infrastructure:ColdRefocusConfig,AIAssistantFocusCapabilitycold-start logic,FocusCapabilityFactorychanges,ConfigReaderonDestinationBuilderContext, Gemini settings)946eed1f— "Add Claude Code cold-start settings matching Gemini pattern" (Claude Code-specific settings keys, defaults,getColdRefocushook,package.jsonentries)Issue 3 carries forward the shared infrastructure from
86fb2efc(minus Gemini-specific settings entries) and the Claude Code-specific work from946eed1f.Assumptions Made
FOCUS_TO_PASTE_DELAY_MSlives inpasteTimingConstants.ts(renamed fromchatPasteConstants.tsin Issue 2, already on the current branch)AIAssistantInsertFactoryclipboard-write removal from86fb2efcis already done by Issue 2 (single clipboard write) — no need to redoLazyResolvedFocusCapability.tsalready exists on the current branch (it's onmainnow), so only the barrel export needs updatingEXTENSION_ID_CLAUDE_CODEand friends are already exported fromsrc/utils/aiAssistants/onmain(added in thece339481Gemini base commit, but the constants may not exist yet — verify during implementation)Plan
S1: Add ColdRefocusConfig interface (new file)
Create
src/destinations/capabilities/ColdRefocusConfig.ts— 4-line interface:Source: commit
86fb2efc, squashed.S2: Add cold-start logic to AIAssistantFocusCapability
Modify
src/destinations/capabilities/AIAssistantFocusCapability.ts:panelIsWarm = falseprivate fieldgetColdRefocus: (() => ColdRefocusConfig) | undefinedconstructor parameter (4th param, beforeinsertFactory)FOCUS_TO_PASTE_DELAY_MSfrom../../constants/pasteTimingConstants(note: namedpasteTimingConstantspost-Issue-2 rename)ColdRefocusConfigfrom./ColdRefocusConfig!panelIsWarm && coldRefocus, callrefocusDuring(); otherwise waitFOCUS_TO_PASTE_DELAY_MS; then setpanelIsWarm = truerefocusDuring(context, refocus)method — loop atintervalMsintervals up tototalMs, re-sending focus commands each tick, stopping early if elapsed >= totalMssrc/__tests__/destinations/capabilities/AIAssistantFocusCapability.test.ts(~258 lines from86fb2efc)Source: commit
86fb2efc.S3: Update FocusCapabilityFactory
Modify
src/destinations/capabilities/FocusCapabilityFactory.ts:ColdRefocusConfiggetColdRefocus: (() => ColdRefocusConfig) | undefinedas 3rd param tocreateAIAssistantCapability(), pass it through toAIAssistantFocusCapabilityconstructorcreateStandardAIAssistantInsertFactory()private method (DRY up the 2 call sites inbuildCustomAIAssistantTiersandbuildBuiltinFallbackTier)FocusCapabilityFactory.test.tsSource: commit
86fb2efc.S4: Add ConfigReader to DestinationBuilderContext
Modify
src/destinations/DestinationRegistry.ts:ConfigReaderfrom../config/ConfigReaderreadonly configReader: ConfigReadertoDestinationBuilderContextinterfaceconfigReader: ConfigReaderparameter to constructor, assign tothis.contextModify
src/createWiringServices.ts:configReader(already in scope) tonew DestinationRegistry(...)constructor as 7th argumentUpdate tests that construct
DestinationRegistryor mockDestinationBuilderContext.Source: commit
86fb2efc.S5: Add getColdRefocus to BuiltinAiAssistantDef and wire Claude Code
Modify
src/destinations/destinationBuilders.ts:getColdRefocus?: (context: DestinationBuilderContext) => ColdRefocusConfigtoBuiltinAiAssistantDefinterfacegetColdRefocushook on the Claude Code entry: readscoldStartDelayMsandcoldRefocusIntervalMsfrom config viacontext.configReader.getWithDefault(), validatestotalMs > intervalMs(warns and falls back to defaults if misconfigured), returns{ totalMs, intervalMs }buildBuiltinAiAssistantDestinationto passdef.getColdRefocusthrough tocreateAIAssistantCapability()(only if defined)getColdRefocusto the Gemini entry (Gemini is Issue 4) — but the Gemini def already exists in the file from thece339481commit onmain; leave it unchangedSource: commits
86fb2efc(interface + wiring pattern) and946eed1f(Claude Code getColdRefocus implementation).S6: Add Claude Code cold-start setting keys and defaults
Modify
src/constants/settingKeys.ts:// Destination Settingssection headerSETTINGS_DESTINATIONS_PREFIX = 'destinations.'SETTING_DESTINATIONS_CLAUDE_CODE_COLD_REFOCUS_INTERVAL_MSandSETTING_DESTINATIONS_CLAUDE_CODE_COLD_START_DELAY_MSModify
src/constants/settingDefaults.ts:// Destination Defaultssection headerDEFAULT_DESTINATIONS_CLAUDE_CODE_COLD_REFOCUS_INTERVAL_MS = 300andDEFAULT_DESTINATIONS_CLAUDE_CODE_COLD_START_DELAY_MS = 2500Update contract tests:
settingDefaults.test.ts,packageJsonContracts.test.ts.Source: commit
946eed1f(Claude Code keys/defaults), commit86fb2efc(section structure pattern).S7: Add Claude Code cold-start settings to package.json
Modify
package.jsoncontributes.configuration:rangelink.destinations.claudeCode.coldStartDelayMs(type: number, default: 2500, min: 500, max: 15000)rangelink.destinations.claudeCode.coldRefocusIntervalMs(type: number, default: 300, min: 100, max: 5000)Source: commit
946eed1f.S8: Update capabilities barrel export
Modify
src/destinations/capabilities/index.ts:ColdRefocusConfigtypeLazyResolvedFocusCapabilityclass (the class already exists onmain; if not, it's on theissues/529branch and needs to be extracted)Source: commit
86fb2efc.S9: Update unit tests
Update all affected test files:
src/__tests__/destinations/capabilities/AIAssistantFocusCapability.test.ts(~258 lines, 100% branch coverage — cold start, warm path, refocus loop timer, validation fallback)FocusCapabilityFactory.test.ts— addundefined3rd argument tocreateAIAssistantCapabilitycalls, verifycreateStandardAIAssistantInsertFactoryextractiondestinationBuilders.test.ts— addconfigReaderto mock context, assert Claude Code def hasgetColdRefocus, assert it reads from correct setting keys, assert validation fallback whentotalMs <= intervalMsDestinationRegistry.test.ts— addmockConfigReaderto constructor callssettingDefaults.test.ts— add Claude Code default value assertionspackageJsonContracts.test.ts— add Claude Code setting schema assertionsS10: QA YAML — add Claude Code cold-start TCs
Append to
qa/qa-test-cases-v1.1.0.yamlunder the "Built-in AI Assistants" section:claude-code-006: cold-start default settings produce correct ColdRefocusConfig (automated: true — unit test covers this)claude-code-007: cold-start validation rejects invalid config (totalMs <= intervalMs) and falls back to defaults (automated: true — unit test)(IDs 006-007 continue from existing
claude-code-005.)S11: Documentation
[Unreleased]for Claude Code cold-start settings<sup>Unreleased</sup>markers