33 *
44 * Agents mark the shells they spawn with an environment variable, and the CLI
55 * reports that mark so usage driven by an agent can be told apart from a person
6- * at a terminal. The checks, their order, and the names follow the GitHub CLI
7- * (`internal/agents/detect.go`), which is the most complete verified table:
8- * generic conventions first, then vendor markers, with the more specific
9- * marker ahead of a broader one it implies (Amp sets `CLAUDECODE` too; Cowork
10- * is Claude Code plus its own flag).
6+ * at a terminal. The checks, their order, and the names follow the GitHub CLI's
7+ * `internal/agents/detect.go` exactly: the generic `AI_AGENT` declaration
8+ * first, then vendor markers, with a more specific agent ahead of a broader
9+ * marker it also sets (Amp and Cowork both set `CLAUDECODE`).
1110 *
12- * Only markers an agent sets on the shells it drives are consulted. Variables
13- * that merely mean an agent is installed or configured — `REPL_ID`,
14- * `GOOSE_PROVIDER`, `AIDER_*`, `COPILOT_*` — are deliberately absent, because
15- * they would attribute a person's own command to an agent.
11+ * Four of that table's signals are deliberately left out, each one the GitHub
12+ * CLI itself marks as low confidence: `REPL_ID` (present in every Replit
13+ * environment), `GOOSE_PROVIDER` (Goose is merely configured),
14+ * `TERM_PROGRAM=kiro` (Kiro's terminal, which a person uses too), and a
15+ * `.pi/agent` entry on `PATH`. Each describes where a person is working rather
16+ * than an agent driving the command, so reporting it would attribute that
17+ * person's own commands to an agent.
1618 */
1719
18- /** The value an agent may declare itself with under the generic conventions . */
20+ /** The value an agent may declare itself with under `AI_AGENT` . */
1921const AGENT_NAME_PATTERN = / ^ [ a - z 0 - 9 _ - ] + $ / i
2022const MAX_AGENT_NAME_LENGTH = 64
2123
@@ -29,34 +31,25 @@ const anyOf =
2931 ( env : NodeJS . ProcessEnv ) =>
3032 variables . some ( ( variable ) => Boolean ( env [ variable ] ) )
3133
32- /** Vendor markers, most specific first . */
34+ /** Vendor markers, in the GitHub CLI's order . */
3335const AGENT_MARKERS : readonly AgentMarker [ ] = [
34- { name : 'amp' , matches : ( env ) => env . AGENT === 'amp' || Boolean ( env . AMP_CURRENT_THREAD_ID ) } ,
35- {
36- name : 'codex' ,
37- matches : anyOf (
38- 'CODEX_THREAD_ID' ,
39- 'CODEX_SANDBOX' ,
40- 'CODEX_CI' ,
41- 'CODEX_SANDBOX_NETWORK_DISABLED'
42- ) ,
43- } ,
36+ { name : 'amp' , matches : ( env ) => env . AGENT === 'amp' } ,
37+ /** Set by Codex on the commands it runs (`codex-rs/core`: `spawn.rs`, `exec_env.rs`, `unified_exec`). */
38+ { name : 'codex' , matches : anyOf ( 'CODEX_SANDBOX' , 'CODEX_CI' , 'CODEX_THREAD_ID' ) } ,
4439 { name : 'gemini-cli' , matches : anyOf ( 'GEMINI_CLI' ) } ,
40+ { name : 'copilot-cli' , matches : anyOf ( 'COPILOT_CLI' ) } ,
41+ /** Not `OPENCODE_CALLER` or `OPENCODE_CLIENT`, which name what launched OpenCode. */
4542 { name : 'opencode' , matches : anyOf ( 'OPENCODE' ) } ,
4643 { name : 'antigravity' , matches : anyOf ( 'ANTIGRAVITY_AGENT' ) } ,
47- { name : 'augment' , matches : anyOf ( 'AUGMENT_AGENT' ) } ,
48- { name : 'cline' , matches : anyOf ( 'CLINE_ACTIVE' ) } ,
44+ { name : 'augment-cli' , matches : anyOf ( 'AUGMENT_AGENT' ) } ,
4945 { name : 'cowork' , matches : anyOf ( 'CLAUDE_CODE_IS_COWORK' ) } ,
46+ /** `CLAUDECODE` is documented in Claude Code's environment variable reference. */
5047 { name : 'claude-code' , matches : anyOf ( 'CLAUDECODE' , 'CLAUDE_CODE' ) } ,
48+ { name : 'cursor' , matches : anyOf ( 'CURSOR_TRACE_ID' ) } ,
5149 {
52- name : 'cursor' ,
53- matches : ( env ) =>
54- anyOf ( 'CURSOR_AGENT' , 'CURSOR_TRACE_ID' ) ( env ) ||
55- env . CURSOR_EXTENSION_HOST_ROLE === 'agent-exec' ,
50+ name : 'cursor-cli' ,
51+ matches : ( env ) => Boolean ( env . CURSOR_AGENT ) || env . CURSOR_EXTENSION_HOST_ROLE === 'agent-exec' ,
5652 } ,
57- { name : 'warp' , matches : anyOf ( 'OZ_RUN_ID' ) } ,
58- { name : 'pi' , matches : anyOf ( 'PI_CODING_AGENT' ) } ,
59- { name : 'crush' , matches : anyOf ( 'CRUSH' ) } ,
6053]
6154
6255/**
@@ -91,17 +84,11 @@ export const NO_CODING_AGENT = 'none'
9184/**
9285 * The agent driving this shell, or `undefined` for a person at a terminal.
9386 *
94- * `AI_AGENT` and `AGENT` are the two generic conventions agents have converged
95- * on for naming themselves and win over vendor markers when set. `AGENT` is
96- * consulted only when it carries a name: OpenCode sets it to `1`, which names
97- * nothing, and its own marker handles it.
87+ * `AI_AGENT` is the generic convention agents use to name themselves, and wins
88+ * over every vendor marker when it holds a well-formed name.
9889 */
9990export function detectCodingAgent ( env : NodeJS . ProcessEnv = process . env ) : string | undefined {
100- const declared = declaredAgentName ( env . AI_AGENT )
101- if ( declared ) return declared
102-
103- const generic = declaredAgentName ( env . AGENT )
104- if ( generic && generic !== '1' ) return generic
105-
106- return AGENT_MARKERS . find ( ( marker ) => marker . matches ( env ) ) ?. name
91+ return (
92+ declaredAgentName ( env . AI_AGENT ) ?? AGENT_MARKERS . find ( ( marker ) => marker . matches ( env ) ) ?. name
93+ )
10794}
0 commit comments