From 10fa40c406378124013cdf79ca51cd6cfcd74369 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Sat, 16 May 2026 22:09:29 +0100 Subject: [PATCH] Add message milestone analytics and install diagnostics metadata Track lifetime successful messages in globalState and emit milestone events at 1, 50, 100, 200, ... Surface platform/arch on all install outcomes and split out a dedicated WSL_NOT_SUPPORTED error code. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/extension.ts | 37 +++++++++++++++++++++++++++++++------ src/script.ts | 30 ++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1b5c66b..ba42e54 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -998,7 +998,8 @@ class ClaudeChatProvider { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1', - ...customEnvVars // Apply custom environment variables (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, etc.) + ...customEnvVars, // Apply custom environment variables (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, etc.) + CLAUDE_CODE_ENTRYPOINT: 'claude-vscode' }; // OpenCredits: clear Anthropic-specific vars so Claude CLI uses env vars directly @@ -1031,6 +1032,7 @@ class ClaudeChatProvider { wslEnvOverrides['DISABLE_TELEMETRY'] = 'true'; wslEnvOverrides['DISABLE_COST_WARNINGS'] = 'true'; } + wslEnvOverrides['CLAUDE_CODE_ENTRYPOINT'] = 'claude-vscode'; const envExports = Object.entries(wslEnvOverrides) .map(([k, v]) => `export ${k}="${v.replace(/"/g, '\\"')}"`) .join(' && '); @@ -1576,6 +1578,19 @@ class ClaudeChatProvider { this._totalCost += jsonData.total_cost_usd; } + // Lifetime success counter — survives reloads, scoped to the + // extension globalState. Used for milestone analytics (1, 50, 100, 200, …). + try { + const prev = this._context.globalState.get('lifetimeMessageSuccessCount', 0) || 0; + const next = prev + 1; + this._context.globalState.update('lifetimeMessageSuccessCount', next); + if (next === 1 || next === 50 || (next > 50 && next % 100 === 0)) { + this._postMessage({ type: 'messageMilestone', count: next }); + } + } catch { + // best-effort — analytics shouldn't break the response path + } + // Send updated totals to webview this._postMessage({ @@ -3632,6 +3647,8 @@ class ClaudeChatProvider { const config = vscode.workspace.getConfiguration('claudeCodeChat'); const wslEnabled = config.get('wsl.enabled', false); + const platform = process.platform; + const arch = os.arch(); // WSL install needs to run inside the distro, not on the Windows host. // The old shell-based flow didn't handle this either — not regressing, @@ -3643,7 +3660,9 @@ class ClaudeChatProvider { success: false, method, error: 'WSL mode: please install Claude inside your WSL distro, then set claudeCodeChat.wsl.claudePath.', - errorCode: 'UNSUPPORTED_PLATFORM' + errorCode: 'WSL_NOT_SUPPORTED', + platform, + arch }); return; } @@ -3653,8 +3672,10 @@ class ClaudeChatProvider { type: 'installComplete', success: false, method, - error: `Unsupported platform: ${process.platform}/${os.arch()}. Install Claude manually from https://code.claude.com.`, - errorCode: 'UNSUPPORTED_PLATFORM' + error: `Unsupported platform: ${platform}/${arch}. Install Claude manually from https://code.claude.com.`, + errorCode: 'UNSUPPORTED_PLATFORM', + platform, + arch }); return; } @@ -3683,7 +3704,9 @@ class ClaudeChatProvider { configuredPath: existing ? undefined : result.binaryPath, existingPathRespected: !!existing, source: result.source, - version: result.version + version: result.version, + platform, + arch }); } catch (err) { const d = err instanceof DownloaderError ? err : null; @@ -3698,7 +3721,9 @@ class ClaudeChatProvider { // analytics can bucket "both npm+cdn failed with NETWORK" vs // "npm INTEGRITY, cdn NETWORK" etc. npmCode: typeof details?.npmCode === 'string' ? details.npmCode : undefined, - cdnCode: typeof details?.cdnCode === 'string' ? details.cdnCode : undefined + cdnCode: typeof details?.cdnCode === 'string' ? details.cdnCode : undefined, + platform, + arch }); } } diff --git a/src/script.ts b/src/script.ts index 900b8d7..4c949e2 100644 --- a/src/script.ts +++ b/src/script.ts @@ -2933,19 +2933,23 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt if (ocOption) ocOption.style.display = opencreditsEnabled ? '' : 'none'; if (success) { - const baseProps = { source: extra && extra.source, version: extra && extra.version }; + const existingPathRespected = !!(extra && extra.existingPathRespected); + const autoConfigured = !!(extra && extra.configuredPath); + sendStats('Install success', { + source: extra && extra.source, + version: extra && extra.version, + platform: extra && extra.platform, + arch: extra && extra.arch, + existingPathRespected: existingPathRespected, + autoConfigured: autoConfigured + }); + successEl.querySelector('.install-success-text').textContent = 'Installed'; if (extra && extra.configuredPath) { - sendStats('Install auto configured path', Object.assign({ existingPathRespected: !!extra.existingPathRespected }, baseProps)); - successEl.querySelector('.install-success-text').textContent = 'Installed'; successEl.querySelector('.install-success-hint').textContent = 'Configured automatically. Send a message to get started.'; - } else if (extra && extra.existingPathRespected) { - sendStats('Install success', baseProps); - successEl.querySelector('.install-success-text').textContent = 'Installed'; + } else if (existingPathRespected) { successEl.querySelector('.install-success-hint').textContent = 'Your existing executable.path setting was left unchanged. Send a message to get started.'; } else { - sendStats('Install success', baseProps); - successEl.querySelector('.install-success-text').textContent = 'Installed'; successEl.querySelector('.install-success-hint').textContent = 'Send a message to get started'; } } else { @@ -2954,6 +2958,8 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt errorCode: errorCode, npmCode: extra && extra.npmCode, cdnCode: extra && extra.cdnCode, + platform: extra && extra.platform, + arch: extra && extra.arch, error: (error || 'Unknown error').substring(0, 200) }); successEl.querySelector('.install-success-icon').style.display = 'none'; @@ -2962,6 +2968,10 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt successEl.querySelector('.install-success-text').textContent = 'Unsupported platform'; successEl.querySelector('.install-success-hint').textContent = error || 'Your platform is not supported. Install Claude manually from https://code.claude.com.'; + } else if (errorCode === 'WSL_NOT_SUPPORTED') { + successEl.querySelector('.install-success-text').textContent = 'WSL mode'; + successEl.querySelector('.install-success-hint').textContent = + error || 'Install Claude inside your WSL distro and set claudeCodeChat.wsl.claudePath.'; } else { successEl.querySelector('.install-success-text').textContent = 'Installation failed'; successEl.querySelector('.install-success-hint').textContent = @@ -3807,6 +3817,10 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt }); break; + case 'messageMilestone': + sendStats('Message milestone', { count: message.count }); + break; + case 'showRestoreOption': showRestoreContainer(message.data); break;