From 5a185eb73606a8f32113f52521c32c171da59238 Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Wed, 1 Apr 2026 14:14:33 -0500 Subject: [PATCH] fix: Add telemetry opt-out via installer prompt and env var Sentry error reporting can now be disabled by: 1. Declining during the interactive installer (sets CODEGRAPH_TELEMETRY=off in the MCP server config env) 2. Setting CODEGRAPH_TELEMETRY=off in your shell environment README updated with a Telemetry section documenting what is collected and how to opt out. Closes #68 Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 31 ++++++++++++++++++++++++++----- src/installer/config-writer.ts | 12 ++++++++---- src/installer/index.ts | 28 ++++++++++++++++++++-------- src/sentry.ts | 7 +++++++ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 27930a5..2e6b9a4 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ The interactive installer will: - Prompt to install `codegraph` globally (needed for hooks & MCP server to work) - Configure the MCP server in `~/.claude.json` - Set up auto-allow permissions for CodeGraph tools +- Ask about [anonymous error reporting](#-telemetry) (opt-out available) - Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) - Install Claude Code hooks for automatic index syncing - Optionally initialize your current project @@ -309,11 +310,12 @@ npx @colbymchenry/codegraph # Run via npx (no global install needed) The installer will: 1. Prompt to install `codegraph` globally (needed for hooks & MCP server) 2. Ask for installation location (global `~/.claude` or local `./.claude`) -3. Configure the MCP server in `claude.json` -4. Optionally set up auto-allow permissions -5. Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) -6. Install Claude Code hooks for automatic index syncing -7. For local installs: initialize and index the current project +3. Optionally set up auto-allow permissions +4. Ask about [anonymous error reporting](#-telemetry) (opt-out available) +5. Configure the MCP server in `claude.json` +6. Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) +7. Install Claude Code hooks for automatic index syncing +8. For local installs: initialize and index the current project ### `codegraph init [path]` @@ -709,6 +711,25 @@ Run `codegraph init` in your project directory first. - Check if the file's language is supported - Verify the file isn't excluded by config patterns +## 📡 Telemetry + +CodeGraph collects anonymous error reports via [Sentry](https://sentry.io) to help diagnose and fix bugs. This is **enabled by default** in production environments (disabled in development/test). + +**What is collected:** +- Error type, message, and stack trace (includes local file paths in the trace) +- CodeGraph version and process name (CLI or MCP server) + +**What is NOT collected:** +- Source code contents +- File contents or repository data +- Personal information or environment variables + +**To opt out**, set the environment variable before running CodeGraph: + +```bash +export CODEGRAPH_TELEMETRY=off +``` + --- ## 📄 License diff --git a/src/installer/config-writer.ts b/src/installer/config-writer.ts index 2d9e8ab..dc70f92 100644 --- a/src/installer/config-writer.ts +++ b/src/installer/config-writer.ts @@ -100,18 +100,22 @@ function writeJsonFile(filePath: string, data: Record): void { /** * Get the MCP server configuration */ -function getMcpServerConfig(): Record { - return { +function getMcpServerConfig(options?: { telemetry?: boolean }): Record { + const config: Record = { type: 'stdio', command: 'codegraph', args: ['serve', '--mcp'], }; + if (options?.telemetry === false) { + config.env = { CODEGRAPH_TELEMETRY: 'off' }; + } + return config; } /** * Write the MCP server configuration to claude.json */ -export function writeMcpConfig(location: InstallLocation): void { +export function writeMcpConfig(location: InstallLocation, options?: { telemetry?: boolean }): void { const claudeJsonPath = getClaudeJsonPath(location); const config = readJsonFile(claudeJsonPath); @@ -121,7 +125,7 @@ export function writeMcpConfig(location: InstallLocation): void { } // Add or update codegraph server - config.mcpServers.codegraph = getMcpServerConfig(); + config.mcpServers.codegraph = getMcpServerConfig(options); writeJsonFile(claudeJsonPath, config); } diff --git a/src/installer/index.ts b/src/installer/index.ts index bea2e62..81a33f4 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -51,9 +51,25 @@ export async function runInstaller(): Promise { const location = await promptInstallLocation(); console.log(); - // Step 3: Write MCP configuration (always uses npx for reliability) + // Step 3: Ask about auto-allow permissions + const autoAllow = await promptAutoAllow(); + console.log(); + + // Step 4: Ask about anonymous error reporting + console.log(chalk.bold(' Send anonymous error reports?') + chalk.dim(' (Helps fix bugs — no source code collected)')); + console.log(); + const enableTelemetry = await promptConfirm('Enable anonymous error reporting', true); + + if (!enableTelemetry) { + info('Telemetry disabled'); + } else { + success('Anonymous error reporting enabled'); + } + console.log(); + + // Step 5: Write MCP configuration (includes telemetry env if opted out) const alreadyHasMcp = hasMcpConfig(location); - writeMcpConfig(location); + writeMcpConfig(location, { telemetry: enableTelemetry }); if (alreadyHasMcp) { success(`Updated MCP server in ${location === 'global' ? '~/.claude.json' : './.claude.json'}`); @@ -61,10 +77,6 @@ export async function runInstaller(): Promise { success(`Added MCP server to ${location === 'global' ? '~/.claude.json' : './.claude.json'}`); } - // Step 4: Ask about auto-allow permissions - const autoAllow = await promptAutoAllow(); - console.log(); - if (autoAllow) { const alreadyHasPerms = hasPermissions(location); writePermissions(location); @@ -76,7 +88,7 @@ export async function runInstaller(): Promise { } } - // Step 5: Write auto-sync hooks + // Step 6: Write auto-sync hooks const alreadyHasHooks = hasHooks(location); writeHooks(location); @@ -86,7 +98,7 @@ export async function runInstaller(): Promise { success(`Added auto-sync hooks to ${location === 'global' ? '~/.claude/settings.json' : './.claude/settings.json'}`); } - // Step 6: Write CLAUDE.md instructions + // Step 7: Write CLAUDE.md instructions const claudeMdResult = writeClaudeMd(location); const claudeMdPath = location === 'global' ? '~/.claude/CLAUDE.md' : './.claude/CLAUDE.md'; diff --git a/src/sentry.ts b/src/sentry.ts index 76fe496..bc2230d 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -19,12 +19,19 @@ let _tags: Record = {}; /** * Initialize Sentry error reporting. * Safe to call multiple times — subsequent calls update tags/release. + * + * Opt out by setting the environment variable CODEGRAPH_TELEMETRY=off */ export function initSentry({ processName, version }: { processName: string; version?: string }) { // Skip in development/test environments if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' || process.env.VITEST) { return; } + // Respect user opt-out + const telemetry = process.env.CODEGRAPH_TELEMETRY?.toLowerCase(); + if (telemetry === 'off' || telemetry === '0' || telemetry === 'false') { + return; + } _enabled = true; _release = `codegraph@${version ?? process.env.npm_package_version ?? 'unknown'}`; _tags = { processName };