surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| 1 | import type { EngineInterface, On } from 'claude-code' |
| 2 | |
| 3 | import { answerOf } from './answer-of' |
| 4 | import Context from './context' |
| 5 | import Gate from './gate' |
| 6 | import IsAnalyticsOff from './is-analytics-off' |
| 7 | import type { Sender } from './sender' |
| 8 | import { telemetryOf } from './telemetry-of' |
| 9 | |
| 10 | /** |
| 11 | * Registers the plugin's hooks: the gate on `telemetry.*`, the two hooks |
| 12 | * that are what `telemetry.log` and `telemetry.mark` do, and its |
| 13 | * engine.create step, which builds the sender over the nouns beneath. |
| 14 | * |
| 15 | * A row is queued for a built-in caller and refused to any other; a |
| 16 | * malformed entry is denied with its reason; an entry for `collector` is |
| 17 | * not this plugin's and goes on beneath. On an engine with no `telemetry` |
| 18 | * of its own the step adds the noun, its methods the same queueing. A batch |
| 19 | * goes out on a timer, when full, and when the session ends. |
| 20 | * |
| 21 | * @param on the engine's registrar |
| 22 | */ |
| 23 | export function register(on: On) { |
| 24 | let sender: Sender | undefined |
| 25 | let isInteractive: boolean | undefined |
| 26 | |
| 27 | on('telemetry.*', (_$, e, next) => Gate.served(e, next)).catch( |
| 28 | (_$, e, next) => Gate.caught(e, next), |
| 29 | ) |
| 30 | |
| 31 | on('telemetry.log', (_$, e, next) => |
| 32 | sender === undefined || e.to === 'collector' |
| 33 | ? next(e) |
| 34 | : answerOf(sender.telemetry.log(e)), |
| 35 | ) |
| 36 | |
| 37 | on('telemetry.mark', (_$, e, next) => |
| 38 | sender === undefined ? next(e) : answerOf(sender.telemetry.mark(e)), |
| 39 | ) |
| 40 | |
| 41 | on('session.start', (_$, e, next) => { |
| 42 | isInteractive = e.isInteractive |
| 43 | |
| 44 | return next(e) |
| 45 | }) |
| 46 | |
| 47 | on('session.end', async (_$, e, next) => { |
| 48 | await sender?.flush() |
| 49 | |
| 50 | return next(e) |
| 51 | }) |
| 52 | |
| 53 | on('engine.create', async (_$, e, next) => { |
| 54 | const beneath = await next(e) |
| 55 | |
| 56 | sender = telemetryOf({ |
| 57 | authorize: () => beneath.session.authorize(), |
| 58 | id: () => beneath.session.id(), |
| 59 | model: () => beneath.session.model(), |
| 60 | isInteractive: async () => |
| 61 | isInteractive ?? |
| 62 | (await beneath.session.surfaces()).includes('terminal'), |
| 63 | environment: async () => ({ |
| 64 | userType: await beneath.env.get('USER_TYPE'), |
| 65 | nodeEnv: await beneath.env.get('NODE_ENV'), |
| 66 | disableTelemetry: await beneath.env.get('DISABLE_TELEMETRY'), |
| 67 | disableNonessentialTraffic: await beneath.env.get( |
| 68 | 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC', |
| 69 | ), |
| 70 | doNotTrack: await beneath.env.get('DO_NOT_TRACK'), |
| 71 | customOauthUrl: await beneath.env.get('CLAUDE_CODE_CUSTOM_OAUTH_URL'), |
| 72 | useGateway: await beneath.env.get('CLAUDE_CODE_USE_GATEWAY'), |
| 73 | providerManagedByHost: await beneath.env.get( |
| 74 | 'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST', |
| 75 | ), |
| 76 | useBedrock: await beneath.env.get('CLAUDE_CODE_USE_BEDROCK'), |
| 77 | useVertex: await beneath.env.get('CLAUDE_CODE_USE_VERTEX'), |
| 78 | useFoundry: await beneath.env.get('CLAUDE_CODE_USE_FOUNDRY'), |
| 79 | useAnthropicAws: await beneath.env.get('CLAUDE_CODE_USE_ANTHROPIC_AWS'), |
| 80 | useAnthropicGoogleCloud: await beneath.env.get( |
| 81 | 'CLAUDE_CODE_USE_ANTHROPIC_GOOGLE_CLOUD', |
| 82 | ), |
| 83 | useMantle: await beneath.env.get('CLAUDE_CODE_USE_MANTLE'), |
| 84 | }), |
| 85 | policy: () => beneath.settings.read({ source: 'policy' }), |
| 86 | facts: async () => ({ |
| 87 | entrypoint: await beneath.env.get('CLAUDE_CODE_ENTRYPOINT'), |
| 88 | agentSdkVersion: await beneath.env.get('CLAUDE_AGENT_SDK_VERSION'), |
| 89 | hostPlatform: await beneath.env.get('CLAUDE_CODE_HOST_PLATFORM'), |
| 90 | os: await beneath.env.get('OS'), |
| 91 | processorArchitecture: await beneath.env.get('PROCESSOR_ARCHITECTURE'), |
| 92 | shellPath: |
| 93 | (await beneath.env.get('SHELL')) ?? |
| 94 | (await beneath.env.get('COMSPEC')), |
| 95 | ci: await beneath.env.get('CI'), |
| 96 | claubbit: await beneath.env.get('CLAUBBIT'), |
| 97 | githubActions: await beneath.env.get('GITHUB_ACTIONS'), |
| 98 | claudeCodeAction: await beneath.env.get('CLAUDE_CODE_ACTION'), |
| 99 | claudeCodeRemote: await beneath.env.get('CLAUDE_CODE_REMOTE'), |
| 100 | remoteEnvironmentType: await beneath.env.get( |
| 101 | 'CLAUDE_CODE_REMOTE_ENVIRONMENT_TYPE', |
| 102 | ), |
| 103 | containerId: await beneath.env.get('CLAUDE_CODE_CONTAINER_ID'), |
| 104 | remoteSessionId: await beneath.env.get('CLAUDE_CODE_REMOTE_SESSION_ID'), |
| 105 | tags: await beneath.env.get('CLAUDE_CODE_TAGS'), |
| 106 | hasSessionAccessToken: IsAnalyticsOff.isEnvSet( |
| 107 | await beneath.env.get('CLAUDE_CODE_SESSION_ACCESS_TOKEN'), |
| 108 | ), |
| 109 | hasSessionIngressTokenFile: IsAnalyticsOff.isEnvSet( |
| 110 | await beneath.env.get('CLAUDE_SESSION_INGRESS_TOKEN_FILE'), |
| 111 | ), |
| 112 | hasWebsocketAuthFileDescriptor: IsAnalyticsOff.isEnvSet( |
| 113 | await beneath.env.get('CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR'), |
| 114 | ), |
| 115 | githubEventName: await beneath.env.get('GITHUB_EVENT_NAME'), |
| 116 | runnerEnvironment: await beneath.env.get('RUNNER_ENVIRONMENT'), |
| 117 | runnerOs: await beneath.env.get('RUNNER_OS'), |
| 118 | githubActionPath: await beneath.env.get('GITHUB_ACTION_PATH'), |
| 119 | githubActorId: await beneath.env.get('GITHUB_ACTOR_ID'), |
| 120 | githubRepositoryId: await beneath.env.get('GITHUB_REPOSITORY_ID'), |
| 121 | githubRepositoryOwnerId: await beneath.env.get( |
| 122 | 'GITHUB_REPOSITORY_OWNER_ID', |
| 123 | ), |
| 124 | sweBenchRunId: await beneath.env.get('SWE_BENCH_RUN_ID'), |
| 125 | sweBenchInstanceId: await beneath.env.get('SWE_BENCH_INSTANCE_ID'), |
| 126 | sweBenchTaskId: await beneath.env.get('SWE_BENCH_TASK_ID'), |
| 127 | hasP4Port: IsAnalyticsOff.isEnvSet(await beneath.env.get('P4PORT')), |
| 128 | wslDistroName: await beneath.env.get('WSL_DISTRO_NAME'), |
| 129 | hasCursorTraceId: IsAnalyticsOff.isEnvSet( |
| 130 | await beneath.env.get('CURSOR_TRACE_ID'), |
| 131 | ), |
| 132 | vscodeGitAskpassMain: await beneath.env.get('VSCODE_GIT_ASKPASS_MAIN'), |
| 133 | bundleIdentifier: await beneath.env.get('__CFBundleIdentifier'), |
| 134 | hasVisualStudioVersion: IsAnalyticsOff.isEnvSet( |
| 135 | await beneath.env.get('VisualStudioVersion'), |
| 136 | ), |
| 137 | terminalEmulator: await beneath.env.get('TERMINAL_EMULATOR'), |
| 138 | term: await beneath.env.get('TERM'), |
| 139 | termProgram: await beneath.env.get('TERM_PROGRAM'), |
| 140 | hasTmux: IsAnalyticsOff.isEnvSet(await beneath.env.get('TMUX')), |
| 141 | hasSty: IsAnalyticsOff.isEnvSet(await beneath.env.get('STY')), |
| 142 | hasKonsoleVersion: IsAnalyticsOff.isEnvSet( |
| 143 | await beneath.env.get('KONSOLE_VERSION'), |
| 144 | ), |
| 145 | hasGnomeTerminalService: IsAnalyticsOff.isEnvSet( |
| 146 | await beneath.env.get('GNOME_TERMINAL_SERVICE'), |
| 147 | ), |
| 148 | hasXtermVersion: IsAnalyticsOff.isEnvSet( |
| 149 | await beneath.env.get('XTERM_VERSION'), |
| 150 | ), |
| 151 | hasVteVersion: IsAnalyticsOff.isEnvSet( |
| 152 | await beneath.env.get('VTE_VERSION'), |
| 153 | ), |
| 154 | hasTerminatorUuid: IsAnalyticsOff.isEnvSet( |
| 155 | await beneath.env.get('TERMINATOR_UUID'), |
| 156 | ), |
| 157 | hasKittyWindowId: IsAnalyticsOff.isEnvSet( |
| 158 | await beneath.env.get('KITTY_WINDOW_ID'), |
| 159 | ), |
| 160 | hasAlacrittyLog: IsAnalyticsOff.isEnvSet( |
| 161 | await beneath.env.get('ALACRITTY_LOG'), |
| 162 | ), |
| 163 | hasTilixId: IsAnalyticsOff.isEnvSet(await beneath.env.get('TILIX_ID')), |
| 164 | hasWtSession: IsAnalyticsOff.isEnvSet( |
| 165 | await beneath.env.get('WT_SESSION'), |
| 166 | ), |
| 167 | hasSessionName: IsAnalyticsOff.isEnvSet( |
| 168 | await beneath.env.get('SESSIONNAME'), |
| 169 | ), |
| 170 | msystem: await beneath.env.get('MSYSTEM'), |
| 171 | hasConEmuAnsi: IsAnalyticsOff.isEnvSet( |
| 172 | await beneath.env.get('ConEmuANSI'), |
| 173 | ), |
| 174 | hasConEmuPid: IsAnalyticsOff.isEnvSet( |
| 175 | await beneath.env.get('ConEmuPID'), |
| 176 | ), |
| 177 | hasConEmuTask: IsAnalyticsOff.isEnvSet( |
| 178 | await beneath.env.get('ConEmuTask'), |
| 179 | ), |
| 180 | hasSshConnection: IsAnalyticsOff.isEnvSet( |
| 181 | await beneath.env.get('SSH_CONNECTION'), |
| 182 | ), |
| 183 | hasSshClient: IsAnalyticsOff.isEnvSet( |
| 184 | await beneath.env.get('SSH_CLIENT'), |
| 185 | ), |
| 186 | hasSshTty: IsAnalyticsOff.isEnvSet(await beneath.env.get('SSH_TTY')), |
| 187 | }), |
| 188 | deployment: async () => [ |
| 189 | [ |
| 190 | 'codespaces', |
| 191 | IsAnalyticsOff.isEnvTruthy(await beneath.env.get('CODESPACES')), |
| 192 | ], |
| 193 | [ |
| 194 | 'gitpod', |
| 195 | IsAnalyticsOff.isEnvSet(await beneath.env.get('GITPOD_WORKSPACE_ID')), |
| 196 | ], |
| 197 | ['coder', IsAnalyticsOff.isEnvTruthy(await beneath.env.get('CODER'))], |
| 198 | [ |
| 199 | 'coder', |
| 200 | IsAnalyticsOff.isEnvSet( |
| 201 | await beneath.env.get('CODER_WORKSPACE_NAME'), |
| 202 | ), |
| 203 | ], |
| 204 | ['devpod', IsAnalyticsOff.isEnvTruthy(await beneath.env.get('DEVPOD'))], |
| 205 | [ |
| 206 | 'devpod', |
| 207 | IsAnalyticsOff.isEnvSet( |
| 208 | await beneath.env.get('DEVPOD_WORKSPACE_UID'), |
| 209 | ), |
| 210 | ], |
| 211 | [ |
| 212 | 'daytona', |
| 213 | IsAnalyticsOff.isEnvSet(await beneath.env.get('DAYTONA_WS_ID')), |
| 214 | ], |
| 215 | [ |
| 216 | 'gcp-cloud-workstations', |
| 217 | IsAnalyticsOff.isEnvTruthy( |
| 218 | await beneath.env.get('GOOGLE_CLOUD_WORKSTATIONS'), |
| 219 | ), |
| 220 | ], |
| 221 | [ |
| 222 | 'aws-cloud9', |
| 223 | IsAnalyticsOff.isEnvSet(await beneath.env.get('C9_PID')), |
| 224 | ], |
| 225 | [ |
| 226 | 'aws-cloud9', |
| 227 | IsAnalyticsOff.isEnvSet(await beneath.env.get('C9_USER')), |
| 228 | ], |
| 229 | ['replit', IsAnalyticsOff.isEnvSet(await beneath.env.get('REPL_ID'))], |
| 230 | ['replit', IsAnalyticsOff.isEnvSet(await beneath.env.get('REPL_SLUG'))], |
| 231 | [ |
| 232 | 'glitch', |
| 233 | IsAnalyticsOff.isEnvSet(await beneath.env.get('PROJECT_DOMAIN')), |
| 234 | ], |
| 235 | ['vercel', IsAnalyticsOff.isEnvTruthy(await beneath.env.get('VERCEL'))], |
| 236 | [ |
| 237 | 'railway', |
| 238 | IsAnalyticsOff.isEnvSet( |
| 239 | await beneath.env.get('RAILWAY_ENVIRONMENT_NAME'), |
| 240 | ), |
| 241 | ], |
| 242 | [ |
| 243 | 'railway', |
| 244 | IsAnalyticsOff.isEnvSet( |
| 245 | await beneath.env.get('RAILWAY_SERVICE_NAME'), |
| 246 | ), |
| 247 | ], |
| 248 | ['render', IsAnalyticsOff.isEnvTruthy(await beneath.env.get('RENDER'))], |
| 249 | [ |
| 250 | 'netlify', |
| 251 | IsAnalyticsOff.isEnvTruthy(await beneath.env.get('NETLIFY')), |
| 252 | ], |
| 253 | ['heroku', IsAnalyticsOff.isEnvSet(await beneath.env.get('DYNO'))], |
| 254 | [ |
| 255 | 'fly.io', |
| 256 | IsAnalyticsOff.isEnvSet(await beneath.env.get('FLY_APP_NAME')), |
| 257 | ], |
| 258 | [ |
| 259 | 'fly.io', |
| 260 | IsAnalyticsOff.isEnvSet(await beneath.env.get('FLY_MACHINE_ID')), |
| 261 | ], |
| 262 | [ |
| 263 | 'cloudflare-pages', |
| 264 | IsAnalyticsOff.isEnvTruthy(await beneath.env.get('CF_PAGES')), |
| 265 | ], |
| 266 | [ |
| 267 | 'deno-deploy', |
| 268 | IsAnalyticsOff.isEnvSet(await beneath.env.get('DENO_DEPLOYMENT_ID')), |
| 269 | ], |
| 270 | [ |
| 271 | 'aws-lambda', |
| 272 | IsAnalyticsOff.isEnvSet( |
| 273 | await beneath.env.get('AWS_LAMBDA_FUNCTION_NAME'), |
| 274 | ), |
| 275 | ], |
| 276 | [ |
| 277 | 'aws-fargate', |
| 278 | (await beneath.env.get('AWS_EXECUTION_ENV')) === 'AWS_ECS_FARGATE', |
| 279 | ], |
| 280 | [ |
| 281 | 'aws-ecs', |
| 282 | (await beneath.env.get('AWS_EXECUTION_ENV')) === 'AWS_ECS_EC2', |
| 283 | ], |
| 284 | [ |
| 285 | 'gcp-cloud-run', |
| 286 | IsAnalyticsOff.isEnvSet(await beneath.env.get('K_SERVICE')), |
| 287 | ], |
| 288 | [ |
| 289 | 'gcp', |
| 290 | IsAnalyticsOff.isEnvSet( |
| 291 | await beneath.env.get('GOOGLE_CLOUD_PROJECT'), |
| 292 | ), |
| 293 | ], |
| 294 | [ |
| 295 | 'azure-app-service', |
| 296 | IsAnalyticsOff.isEnvSet(await beneath.env.get('WEBSITE_SITE_NAME')), |
| 297 | ], |
| 298 | [ |
| 299 | 'azure-app-service', |
| 300 | IsAnalyticsOff.isEnvSet(await beneath.env.get('WEBSITE_SKU')), |
| 301 | ], |
| 302 | [ |
| 303 | 'azure-functions', |
| 304 | IsAnalyticsOff.isEnvSet( |
| 305 | await beneath.env.get('AZURE_FUNCTIONS_ENVIRONMENT'), |
| 306 | ), |
| 307 | ], |
| 308 | [ |
| 309 | 'digitalocean-app-platform', |
| 310 | IsAnalyticsOff.isEnvSet(await beneath.env.get('APP_URL')) && |
| 311 | String(await beneath.env.get('APP_URL')).includes( |
| 312 | 'ondigitalocean.app', |
| 313 | ), |
| 314 | ], |
| 315 | [ |
| 316 | 'huggingface-spaces', |
| 317 | IsAnalyticsOff.isEnvSet( |
| 318 | await beneath.env.get('SPACE_CREATOR_USER_ID'), |
| 319 | ), |
| 320 | ], |
| 321 | [ |
| 322 | 'github-actions', |
| 323 | IsAnalyticsOff.isEnvTruthy(await beneath.env.get('GITHUB_ACTIONS')), |
| 324 | ], |
| 325 | [ |
| 326 | 'gitlab-ci', |
| 327 | IsAnalyticsOff.isEnvTruthy(await beneath.env.get('GITLAB_CI')), |
| 328 | ], |
| 329 | [ |
| 330 | 'circleci', |
| 331 | IsAnalyticsOff.isEnvSet(await beneath.env.get('CIRCLECI')), |
| 332 | ], |
| 333 | [ |
| 334 | 'buildkite', |
| 335 | IsAnalyticsOff.isEnvSet(await beneath.env.get('BUILDKITE')), |
| 336 | ], |
| 337 | ['ci', IsAnalyticsOff.isEnvTruthy(await beneath.env.get('CI'))], |
| 338 | [ |
| 339 | 'kubernetes', |
| 340 | IsAnalyticsOff.isEnvSet( |
| 341 | await beneath.env.get('KUBERNETES_SERVICE_HOST'), |
| 342 | ), |
| 343 | ], |
| 344 | ], |
| 345 | configLocation: async () => ({ |
| 346 | configDir: await beneath.env.get('CLAUDE_CONFIG_DIR'), |
| 347 | home: await beneath.env.get('HOME'), |
| 348 | userProfile: await beneath.env.get('USERPROFILE'), |
| 349 | }), |
| 350 | cwd: () => beneath.session.cwd(), |
| 351 | repo: () => beneath.session.repo(), |
| 352 | version: async () => beneath.session.version(), |
| 353 | read: path => beneath.fs.read(path), |
| 354 | list: path => beneath.fs.list(path), |
| 355 | exists: path => beneath.fs.exists(path), |
| 356 | run: argv => |
| 357 | beneath.process.run(argv, { timeoutMs: Context.PROBE_TIMEOUT_MS }), |
| 358 | fetch: (url, init) => beneath.http.fetch(url, init), |
| 359 | after: (ms, fn) => beneath.clock.after(ms, fn), |
| 360 | sleep: ms => beneath.clock.sleep(ms), |
| 361 | debug: text => beneath.ui.log(text, { to: 'debug' }), |
| 362 | }) |
| 363 | |
| 364 | const telemetry: EngineInterface['telemetry'] = sender.telemetry |
| 365 | const added = { telemetry } |
| 366 | |
| 367 | return { ...added, ...beneath } |
| 368 | }) |
| 369 | } |