Skip to main content

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.

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

Overview

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:
cli.command('build', {
  async run(c) {
    if (!c.agent) console.log('Building...')
    const result = await build()
    if (!c.agent) console.log('Done!')
    return result
  },
})
# 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:
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:
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:
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:
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:
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:
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