undercover.ts
utils/undercover.ts
No strong subsystem tag
90
Lines
3681
Bytes
3
Exports
3
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with general runtime concerns. It contains 90 lines, 3 detected imports, and 3 detected exports.
Important relationships
Detected exports
isUndercovergetUndercoverInstructionsshouldShowUndercoverAutoNotice
Keywords
internalmodelclaudeprocessundercovercodeuser_typeactivecommitrepo
Detected imports
./commitAttribution.js./config.js./envUtils.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* Undercover mode — safety utilities for contributing to public/open-source repos.
*
* When active, Claude Code adds safety instructions to commit/PR prompts and
* strips all attribution to avoid leaking internal model codenames, project
* names, or other Anthropic-internal information. The model is not told what
* model it is.
*
* Activation:
* - CLAUDE_CODE_UNDERCOVER=1 — force ON (even in internal repos)
* - Otherwise AUTO: active UNLESS the repo remote matches the internal
* allowlist (INTERNAL_MODEL_REPOS in commitAttribution.ts). Safe default
* is ON — Claude may push to public remotes from a CWD that isn't itself
* a git checkout (e.g. /tmp crash repro).
* - There is NO force-OFF. This guards against model codename leaks — if
* we're not confident we're in an internal repo, we stay undercover.
*
* All code paths are gated on process.env.USER_TYPE === 'ant'. Since USER_TYPE is
* a build-time --define, the bundler constant-folds these checks and dead-code-
* eliminates the ant-only branches from external builds. In external builds every
* function in this file reduces to a trivial return.
*/
import { getRepoClassCached } from './commitAttribution.js'
import { getGlobalConfig } from './config.js'
import { isEnvTruthy } from './envUtils.js'
export function isUndercover(): boolean {
if (process.env.USER_TYPE === 'ant') {
if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return true
// Auto: active unless we've positively confirmed we're in an allowlisted
// internal repo. 'external', 'none', and null (check not yet run) all
// resolve to ON. The check is primed in setup.ts; only 'internal' → OFF.
return getRepoClassCached() !== 'internal'
}
return false
}
export function getUndercoverInstructions(): string {
if (process.env.USER_TYPE === 'ant') {
return `## UNDERCOVER MODE — CRITICAL
You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE repository. Your commit
messages, PR titles, and PR bodies MUST NOT contain ANY Anthropic-internal
information. Do not blow your cover.
NEVER include in commit messages or PR descriptions:
- Internal model codenames (animal names like Capybara, Tengu, etc.)
- Unreleased model version numbers (e.g., opus-4-7, sonnet-4-8)
- Internal repo or project names (e.g., claude-cli-internal, anthropics/…)
- Internal tooling, Slack channels, or short links (e.g., go/cc, #claude-code-…)
- The phrase "Claude Code" or any mention that you are an AI
- Any hint of what model or version you are
- Co-Authored-By lines or any other attribution
Write commit messages as a human developer would — describe only what the code
change does.
GOOD:
- "Fix race condition in file watcher initialization"
- "Add support for custom key bindings"
- "Refactor parser for better error messages"
BAD (never write these):
- "Fix bug found while testing with Claude Capybara"
- "1-shotted by claude-opus-4-6"
- "Generated with Claude Code"
- "Co-Authored-By: Claude Opus 4.6 <…>"
`
}
return ''
}
/**
* Check whether to show the one-time explainer dialog for auto-undercover.
* True when: undercover is active via auto-detection (not forced via env),
* and the user hasn't seen the notice before. Pure — the component marks the
* flag on mount.
*/
export function shouldShowUndercoverAutoNotice(): boolean {
if (process.env.USER_TYPE === 'ant') {
// If forced via env, user already knows; don't nag.
if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return false
if (!isUndercover()) return false
if (getGlobalConfig().hasSeenUndercoverAutoNotice) return false
return true
}
return false
}