mirror of
https://github.com/expo/agent-cli-detector.git
synced 2026-11-15 03:28:05 -10:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2f8b90f53 | ||
|
|
e308d262b1 | ||
|
|
09f4c420ad | ||
|
|
9205525ad4 | ||
|
|
cf68eb6e0c | ||
|
|
b7c8ccd0aa | ||
|
|
1cad0bda77 | ||
|
|
8e4fdd3c6f | ||
|
|
238a06ac5f | ||
|
|
2369c9194f | ||
|
|
eecb49a358 |
No files matched your search
@@ -2,11 +2,11 @@
|
||||
|
||||
Detect whether a JavaScript CLI is running inside a coding agent.
|
||||
|
||||
Please [open an issue](https://github.com/davidmokos/detect-agent/issues/new)
|
||||
Please [open an issue](https://github.com/expo/agent-cli-detector/issues/new)
|
||||
if your coding agent isn't properly detected :)
|
||||
|
||||
[](https://www.npmjs.com/package/agent-cli-detector)
|
||||
[](https://github.com/davidmokos/detect-agent/actions)
|
||||
[](https://github.com/expo/agent-cli-detector/actions)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -35,21 +35,33 @@ Officially supported coding agents:
|
||||
| Name | ID | Session ID |
|
||||
| ----------------------------------------------------------------- | ------------- | ---------- |
|
||||
| [Antigravity](https://antigravity.google/) | `antigravity` | ✅ |
|
||||
| [Bolt](https://bolt.new/) | `bolt` | 🚫 |
|
||||
| [Claude Code](https://claude.com/claude-code) | `claude-code` | ✅ |
|
||||
| [Cline](https://cline.bot/) | `cline` | 🚫 |
|
||||
| [Codex](https://developers.openai.com/codex/) | `codex` | ✅ |
|
||||
| [Cursor](https://cursor.com/) | `cursor` | ✅ |
|
||||
| [Devin](https://devin.ai/) | `devin` | 🚫 |
|
||||
| [Factory Droid](https://docs.factory.ai/droid-cli/overview) | `droid` | 🚫 |
|
||||
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | 🚫 |
|
||||
| [GitHub Copilot CLI](https://github.com/github/copilot-cli) | `copilot` | ✅ |
|
||||
| [Grok](https://x.ai/build) | `grok` | ✅ |
|
||||
| [Grok Bot](https://x.ai/bot) | `grok-bot` | ✅ |
|
||||
| [Kilo Code](https://kilocode.ai/) | `kilocode` | ✅ |
|
||||
| [Kiro](https://kiro.dev/) | `kiro` | ✅ |
|
||||
| [Muse Code][muse-code] | `muse` | 🚫 |
|
||||
| [OpenCode](https://opencode.ai/) | `opencode` | 🚫 |
|
||||
| [Pi](https://github.com/badlogic/pi-mono) | `pi` | 🚫 |
|
||||
| [Replit](https://replit.com/) | `replit` | ✅ |
|
||||
| [Rork](https://rork.com/) | `rork` | 🚫 |
|
||||
|
||||
[muse-code]: https://developer.meta.com/ai/products/muse-code/
|
||||
|
||||
Detection is data-driven: the exact environment variables and process
|
||||
patterns for each agent live in [`src/agents.ts`](src/agents.ts).
|
||||
|
||||
OpenCode 1.0.19 and later is detected from environment variables by default.
|
||||
OpenCode 0.x requires experimental process-tree detection.
|
||||
|
||||
## API
|
||||
|
||||
### `detectAgent([options])`
|
||||
@@ -92,7 +104,8 @@ A string containing the agent's session identifier, normalized from
|
||||
agent-specific environment variables such as `CODEX_THREAD_ID`,
|
||||
`CURSOR_CONVERSATION_ID`, `CLAUDE_CODE_SESSION_ID`,
|
||||
`ANTIGRAVITY_TRAJECTORY_ID`, `KIRO_SESSION_ID`, `KILO_RUN_ID`, and
|
||||
`COPILOT_AGENT_SESSION_ID`. Omitted when the agent doesn't expose one
|
||||
`COPILOT_AGENT_SESSION_ID`, `REPLIT_SESSION`, and `GROK_SESSION_ID`.
|
||||
Omitted when the agent doesn't expose one
|
||||
(see the Session ID column in the support table above).
|
||||
|
||||
### `isRunningFromAgent([options])`
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "agent-cli-detector",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "agent-cli-detector",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.8",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"agent-cli-detector": "dist/cli.js"
|
||||
|
||||
+19
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "agent-cli-detector",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.8",
|
||||
"description": "Detect whether a JavaScript CLI is running inside a coding agent.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@@ -8,13 +8,18 @@
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"bin": {
|
||||
"agent-cli-detector": "./dist/cli.js"
|
||||
"agent-cli-detector": "dist/cli.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -44,5 +49,13 @@
|
||||
"@types/node": "^24.0.4",
|
||||
"tsup": "^8.5.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/expo/agent-cli-detector.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/expo/agent-cli-detector/issues"
|
||||
},
|
||||
"homepage": "https://github.com/expo/agent-cli-detector#readme"
|
||||
}
|
||||
@@ -12,6 +12,15 @@ export const defaultAgents = [
|
||||
process: [{ pattern: /^codex$/i }],
|
||||
sessionEnv: ["CODEX_THREAD_ID"]
|
||||
},
|
||||
{
|
||||
id: "grok-bot",
|
||||
name: "Grok Bot",
|
||||
env: [
|
||||
{ name: "SAND_BOX_BOOT_ID" },
|
||||
{ name: "SAND_BOX_STORE_ID" }
|
||||
],
|
||||
sessionEnv: ["CURSOR_CONVERSATION_ID"]
|
||||
},
|
||||
{
|
||||
id: "cursor",
|
||||
name: "Cursor",
|
||||
@@ -35,6 +44,10 @@ export const defaultAgents = [
|
||||
{
|
||||
id: "opencode",
|
||||
name: "OpenCode",
|
||||
env: [
|
||||
{ name: "OPENCODE", value: "1" },
|
||||
{ name: "OPENCODE_PID" }
|
||||
],
|
||||
process: [{ pattern: /^opencode$/i }]
|
||||
},
|
||||
{
|
||||
@@ -42,12 +55,26 @@ export const defaultAgents = [
|
||||
name: "Devin",
|
||||
process: [{ pattern: /devin/i }]
|
||||
},
|
||||
{
|
||||
id: "droid",
|
||||
name: "Factory Droid",
|
||||
env: [{ name: "FACTORY_ENV", value: "production" }]
|
||||
},
|
||||
{
|
||||
id: "gemini",
|
||||
name: "Gemini CLI",
|
||||
env: [{ name: "GEMINI_CLI", value: "1" }],
|
||||
process: [{ pattern: /^gemini$/i }]
|
||||
},
|
||||
{
|
||||
id: "grok",
|
||||
name: "Grok",
|
||||
env: [
|
||||
{ name: "GROK_AGENT", value: "1" },
|
||||
{ name: "GROK_SESSION_ID" }
|
||||
],
|
||||
sessionEnv: ["GROK_SESSION_ID"]
|
||||
},
|
||||
{
|
||||
id: "antigravity",
|
||||
name: "Antigravity",
|
||||
@@ -58,11 +85,31 @@ export const defaultAgents = [
|
||||
process: [{ pattern: /^antigravity$/i }],
|
||||
sessionEnv: ["ANTIGRAVITY_TRAJECTORY_ID"]
|
||||
},
|
||||
{
|
||||
id: "bolt",
|
||||
name: "Bolt",
|
||||
env: [
|
||||
{ name: "BOLT_ENV" },
|
||||
{ name: "BOLT_ORIGIN" },
|
||||
{ name: "BOLT_SERVER_URL" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pi",
|
||||
name: "Pi",
|
||||
env: [{ name: "PI_CODING_AGENT", value: "true" }]
|
||||
},
|
||||
{
|
||||
id: "replit",
|
||||
name: "Replit",
|
||||
env: [{ name: "REPLIT_SESSION" }],
|
||||
sessionEnv: ["REPLIT_SESSION"]
|
||||
},
|
||||
{
|
||||
id: "rork",
|
||||
name: "Rork",
|
||||
env: [{ name: "RORK_API_URL" }]
|
||||
},
|
||||
{
|
||||
id: "kiro",
|
||||
name: "Kiro",
|
||||
@@ -70,6 +117,11 @@ export const defaultAgents = [
|
||||
process: [{ pattern: /kiro/i }],
|
||||
sessionEnv: ["KIRO_SESSION_ID"]
|
||||
},
|
||||
{
|
||||
id: "muse",
|
||||
name: "Muse Code",
|
||||
env: [{ name: "MUSE_RELEASE_INFO" }]
|
||||
},
|
||||
{
|
||||
id: "cline",
|
||||
name: "Cline",
|
||||
|
||||
+8
-1
@@ -1,16 +1,23 @@
|
||||
export type AgentId =
|
||||
| "antigravity"
|
||||
| "bolt"
|
||||
| "cline"
|
||||
| "codex"
|
||||
| "copilot"
|
||||
| "cursor"
|
||||
| "devin"
|
||||
| "droid"
|
||||
| "gemini"
|
||||
| "grok"
|
||||
| "grok-bot"
|
||||
| "kiro"
|
||||
| "kilocode"
|
||||
| "claude-code"
|
||||
| "muse"
|
||||
| "opencode"
|
||||
| "pi";
|
||||
| "pi"
|
||||
| "replit"
|
||||
| "rork";
|
||||
|
||||
export type DetectionStrategyName = "environment" | "process-tree";
|
||||
|
||||
|
||||
@@ -49,6 +49,157 @@ test("detects kiro and normalizes its session id", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("detects replit and normalizes its session id", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
REPLIT_SESSION: "session-123"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "replit",
|
||||
name: "Replit",
|
||||
sessionId: "session-123"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects grok and normalizes its session id", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
GROK_AGENT: "1",
|
||||
GROK_SESSION_ID: "01a035a8-166c-7771-8e5e-f67e0dd98c83"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "grok",
|
||||
name: "Grok",
|
||||
sessionId: "01a035a8-166c-7771-8e5e-f67e0dd98c83"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects Grok Bot from SAND_BOX_BOOT_ID and normalizes the conversation id", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
CURSOR_AGENT: "1",
|
||||
CURSOR_CONVERSATION_ID: "conversation-123",
|
||||
SAND_BOX_BOOT_ID: "boot-456"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "grok-bot",
|
||||
name: "Grok Bot",
|
||||
sessionId: "conversation-123"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects cursor from CURSOR_AGENT without Grok Bot signals", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
CURSOR_AGENT: "1",
|
||||
CURSOR_CONVERSATION_ID: "conversation-123"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "cursor",
|
||||
name: "Cursor",
|
||||
sessionId: "conversation-123"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects Grok Bot from SAND_BOX_STORE_ID alone", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
SAND_BOX_STORE_ID: "store-789"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "grok-bot",
|
||||
name: "Grok Bot"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects Factory Droid from FACTORY_ENV", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
FACTORY_ENV: "production"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "droid",
|
||||
name: "Factory Droid"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects rork from RORK_API_URL", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
RORK_API_URL: "https://api.rork.com"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "rork",
|
||||
name: "Rork"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects Muse Code from MUSE_RELEASE_INFO", () => {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
MUSE_RELEASE_INFO: JSON.stringify({
|
||||
channel: "muse-stable",
|
||||
version: "0.1.0-R708.1"
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "muse",
|
||||
name: "Muse Code"
|
||||
});
|
||||
});
|
||||
|
||||
test("detects bolt from any known environment variable", () => {
|
||||
for (const envName of ["BOLT_ENV", "BOLT_ORIGIN", "BOLT_SERVER_URL"]) {
|
||||
const result = detectAgent({
|
||||
env: {
|
||||
[envName]: "1"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "bolt",
|
||||
name: "Bolt"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("detects opencode from any known environment variable", () => {
|
||||
for (const env of [{ OPENCODE: "1" }, { OPENCODE_PID: "12345" }]) {
|
||||
const result = detectAgent({ env });
|
||||
|
||||
assert.equal(result.detected, true);
|
||||
assert.deepEqual(result.agent, {
|
||||
id: "opencode",
|
||||
name: "OpenCode"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("default strategies only use environment variables", () => {
|
||||
assert.deepEqual(
|
||||
createDefaultStrategies().map((strategy) => strategy.name),
|
||||
|
||||
Reference in new issue
Block a user