Skip to content

Claude Code cold-start re-focus loop #552

@couimet

Description

@couimet

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

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions