> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/wevm/incur/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Detection

> Detect when commands are invoked by agents vs humans

The `run` context includes an `agent` boolean that indicates whether the command is being invoked by an agent or a human.

## Overview

```ts theme={null}
cli.command('deploy', {
  args: z.object({ env: z.enum(['staging', 'production']) }),
  run(c) {
    if (!c.agent) console.log(`Deploying to ${c.args.env}...`)
    return { url: `https://${c.args.env}.example.com` }
  },
})
```

## How It Works

`c.agent` is `true` when:

* **stdout is not a TTY** — output is piped, redirected, or consumed programmatically
* **`--json` or `--format` is used** — explicit format flags indicate agent consumption
* **`--mcp` is used** — running as an MCP stdio server

Otherwise, `c.agent` is `false` (human mode).

## Use Cases

### Progress Messages

Show progress to humans without polluting agent output:

```ts theme={null}
cli.command('build', {
  async run(c) {
    if (!c.agent) console.log('Building...')
    const result = await build()
    if (!c.agent) console.log('Done!')
    return result
  },
})
```

```sh theme={null}
# Human mode
$ my-cli build
# Building...
# Done!
# → files: 42
# → duration: 3.2s

# Agent mode (no progress messages)
$ my-cli build --json
# → {"ok":true,"data":{"files":42,"duration":3.2}}
```

### Interactive Prompts

Skip prompts when invoked by agents:

```ts theme={null}
import prompts from 'prompts'

cli.command('deploy', {
  args: z.object({ env: z.enum(['staging', 'production']).optional() }),
  async run(c) {
    let env = c.args.env
    if (!env && !c.agent) {
      const response = await prompts({
        type: 'select',
        name: 'env',
        message: 'Choose environment',
        choices: [
          { title: 'Staging', value: 'staging' },
          { title: 'Production', value: 'production' },
        ],
      })
      env = response.env
    }
    if (!env) return c.error({ code: 'MISSING_ENV', message: 'env is required' })
    return { deployed: true, env }
  },
})
```

### Colored Output

Use colors for humans, plain text for agents:

```ts theme={null}
import chalk from 'chalk'

cli.command('status', {
  run(c) {
    const status = 'online'
    if (!c.agent) console.log(chalk.green(`Status: ${status}`))
    return { status }
  },
})
```

### Logging

Log to stderr in human mode, stay silent in agent mode:

```ts theme={null}
cli.command('sync', {
  run(c) {
    if (!c.agent) console.error('[sync] Starting...')
    const result = doSync()
    if (!c.agent) console.error(`[sync] Synced ${result.files} files`)
    return result
  },
})
```

### Spinners and Animations

Only show spinners in human mode:

```ts theme={null}
import ora from 'ora'

cli.command('install', {
  async run(c) {
    const spinner = c.agent ? null : ora('Installing...').start()
    try {
      const result = await install()
      spinner?.succeed('Installed!')
      return result
    } catch (error) {
      spinner?.fail('Install failed')
      throw error
    }
  },
})
```

## Detection Heuristics

incur uses the same heuristics as standard CLI tools:

```ts theme={null}
const agent = process.stdout.isTTY !== true
```

This is `true` when:

* Output is piped: `my-cli deploy | jq`
* Output is redirected: `my-cli deploy > output.txt`
* Running in an agent tool call (MCP, skill invocation)

This is `false` when:

* Running in a terminal emulator (iTerm, Terminal.app, etc.)
* Running in an SSH session with a TTY

## Middleware Access

Middleware also has access to `c.agent`:

```ts theme={null}
import { middleware } from 'incur'

const logger = middleware((c, next) => {
  if (!c.agent) console.error(`[${c.command}] Starting...`)
  const result = await next()
  if (!c.agent) console.error(`[${c.command}] Done`)
  return result
})

cli.use(logger)
```

## Best Practices

### Do

* Use `console.log()` or `console.error()` for human-facing messages
* Use `return` for structured data that goes to both agents and humans
* Check `c.agent` before prompting for input
* Check `c.agent` before showing progress indicators

### Don't

* Don't change the return value based on `c.agent` — return the same data structure in both modes
* Don't use `c.agent` to hide errors — errors should always be shown
* Don't use `c.agent` for authentication — it's a UI hint, not a security boundary

## Related

* See [Output Policy](/advanced/output-policy) for suppressing data output in human mode
* See [TOON Output](/features/toon-output) for token-efficient output formatting
