AI Agent Detection
Experimental
The gunshi/agent entry point is experimental. The agent detection rules are delegated to std-env, and this API may change as the upstream detector list and conventions evolve.
gunshi/agent provides a minimal utility, getAgentProfile(), that lets your CLI adjust its UX when running on an AI coding agent (Claude Code, Cursor, Codex, Replit, etc.). It does not change Gunshi's behavior automatically. The detection result is a hint that your command, plugin, or renderer can opt into.
Why a separate utility?
AI coding agents typically benefit from terser, machine-friendly CLI output, while human users expect rich, interactive output. getAgentProfile() gives you a single switch you can branch on, without spreading environment variable checks across your code.
gunshi/agent is intentionally small:
- it does not re-export
std-env's API - it does not alter Gunshi core behavior
- it is not a security boundary — environment variables can be spoofed
Installation
gunshi/agent ships with the gunshi package — no extra install is required.
npm install gunshiAPI
getAgentProfile()
The only public export.
import { getAgentProfile } from 'gunshi/agent'
const profile = getAgentProfile()
if (profile.isAgent) {
// adjust output for AI agent execution
}The returned profile has just two fields:
interface AgentProfile {
isAgent: boolean
name?: string // detected agent name (when known)
}To get the type without exposing the internal AgentProfile interface, use ReturnType:
import { getAgentProfile } from 'gunshi/agent'
type AgentProfile = ReturnType<typeof getAgentProfile>Supported agents
Detection is delegated to std-env. The current detector list and detection rules are documented there. As of writing, std-env recognizes:
claude(Claude Code)cursorcodexreplitgeminiauggieopencodekirogoosepidevin
If you need any of these to behave differently, prefer reading the value at runtime via getAgentProfile().name rather than hard-coding environment variable checks.
AI_AGENT override
std-env honors an AI_AGENT environment variable as an explicit override:
AI_AGENT=codex my-cli ...This is useful for testing agent-aware behavior locally, or for declaring a custom agent name. Gunshi passes the name through verbatim — it does not normalize it.
// AI_AGENT=my-agent
getAgentProfile()
// → { isAgent: true, name: 'my-agent' }Using it in a command
import { define } from 'gunshi'
import { getAgentProfile } from 'gunshi/agent'
export default define({
name: 'deploy',
args: {
target: { type: 'string', required: true }
},
run: ctx => {
const profile = getAgentProfile()
if (profile.isAgent) {
// emit machine-friendly output
ctx.log(JSON.stringify({ ok: true, target: ctx.values.target }))
return
}
ctx.log(`Deployed to ${ctx.values.target} ✨`)
}
})Using it in a plugin
gunshi/agent itself is not a plugin, but you can build one on top of it.
import { plugin } from 'gunshi/plugin'
import { getAgentProfile } from 'gunshi/agent'
export default function agentAwareRenderer() {
return plugin({
id: 'g:agent-aware-renderer',
setup: ctx => {
const profile = getAgentProfile()
if (!profile.isAgent) {
return
}
ctx.decorateUsageRenderer(async base => {
const rendered = await base()
// for example, drop ANSI colors and decorations for agent output
return rendered.replaceAll(/\u001B\[[0-9;]*m/g, '')
})
}
})
}Branching strategy
Prefer the broad isAgent flag over agent-specific names:
// ✅ Recommended
if (profile.isAgent) {
emitJson()
}
// ⚠️ Avoid unless you have a known agent-specific constraint
if (profile.name === 'codex' || profile.name === 'claude') {
emitJson()
}Agent-specific branches are appropriate only when you need to work around a known constraint of a specific agent (for example, an output limit or a missing TTY feature). Otherwise, isAgent keeps your code agnostic of the upstream detector list.
Why Gunshi does not change output automatically
getAgentProfile() is a hint. Gunshi's core does not switch usage / validation error / prompt rendering on its own, even when an agent is detected. There are several reasons:
- Spoofable: Agent detection relies on environment variables and is not a security signal.
- App-specific: The "right" agent-friendly output differs per CLI (JSON, NDJSON, plain text, etc.).
- Backward compatibility: Existing CLIs should not silently change their output when run under an agent.
- Explicit opt-in: Renderers, commands, and plugins can opt into agent-aware behavior where it actually matters.
If you need a richer integration (custom renderer decoration, a --json flag, etc.), build it on top of getAgentProfile() in a plugin. A first-party @gunshi/plugin-agent may be added in the future once the desired shape is clearer.
Need the raw std-env API?
gunshi/agent only exposes getAgentProfile(). If you need agentInfo, detectAgent(), agent, or isAgent directly, install std-env and import from it explicitly:
npm install std-envimport { agent, agentInfo, detectAgent, isAgent } from 'std-env'This keeps gunshi/agent's API surface small and stable, while letting power users reach for the underlying detector.
