# Designing CLIs That Coding Agents Can Run Reliably: Flags, Help, and Idempotency

> Learn to design reliable CLIs for coding agents. Master flags, help, and idempotency for deterministic, non-interactive interfaces and machine-readable output. Enhance your tooling.

- Repository: [Cursor/plugins](https://github.com/cursor/plugins)
- Tags: best-practices
- Published: 2026-05-25

---

**Reliable CLIs for coding agents require deterministic, non-interactive interfaces with machine-readable output, layered help documentation, and idempotent operations supported by a `--dry-run` flag.**

When autonomous coding agents execute command-line tools, they cannot answer interactive prompts or interpret ambiguous error messages. The `cursor/plugins` repository codifies specific patterns in the **CLI for Agents** plugin that transform traditional scripts into automation-safe utilities. These patterns ensure that agents can compose pipelines, retry failed operations, and introspect capabilities without human intervention.

## Non-Interactive Flags First

Agents cannot respond to stdin prompts, making interactive input a fatal blocker for automation. According to the specification in [`cli-for-agent/skills/cli-for-agents/SKILL.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/skills/cli-for-agents/SKILL.md), every required input must be supplied via flags rather than prompts.

The plugin enforces a **flag-first interface** using mandatory arguments like `--input`, `--output`, and `--config`. Any command that blocks waiting for user input is considered a design bug. When the input flag is omitted, the CLI must read from `process.stdin` and exit gracefully if the stream is empty, enabling safe pipeline composition.

## Layered Help Documentation

Agents require both concise usage synopses and exhaustive reference material to generate correct command strings. The [`cli-for-agent/README.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/README.md) defines a two-tier help system:

- **`--help`** prints a concise usage synopsis suitable for quick parsing
- **`--help=full`** (or `--help=examples`) prints exhaustive flag definitions with runnable examples

This layered approach allows agents to validate command structure against the brief help while using the full documentation to handle edge cases. The help content is auto-generated from a declarative flag schema in [`example.ts`](https://github.com/cursor/plugins/blob/main/example.ts), ensuring examples stay synchronized with the implementation.

## Machine-Readable Error Handling

Long stack traces confuse autonomous agents, while structured data enables programmatic error recovery. The specification in [`cli-for-agent/skills/cli-for-agents/SKILL.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/skills/cli-for-agents/SKILL.md) mandates that errors be emitted as JSON objects with a standardized schema:

```typescript
export function formatError(err: unknown): string {
  const code = (err as any).code ?? 'UNKNOWN';
  const message = (err as any).message ?? String(err);
  const hint = (err as any).hint ?? '';
  return JSON.stringify({ code, message, hint });
}

```

When an error occurs, the CLI writes this JSON to stderr and exits with a non-zero status. Agents can parse the `code` field to decide whether to retry the operation, abort the task, or attempt a fallback strategy.

## Idempotent Operations with Dry-Run

Agents frequently retry commands after transient failures, requiring operations that produce the same result on repeated runs. The [`cli-for-agent/README.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/README.md) recommends **pure** operations with no hidden side-effects and mandates a standardized `--dry-run` flag.

The dry-run implementation runs the full validation code path without mutating state:

```typescript
export async function dryRunTask(argv: any): Promise<void> {
  // Validate arguments and input data
  if (argv.input) {
    const data = await readFile(argv.input);
    validateSchema(data);
  } else {
    const stdin = await readStdin();
    validateSchema(stdin);
  }
  // No side-effects – just report that validation passed
}

```

When `--dry-run` is present, the CLI performs all validation checks and exits with success if no errors occur, allowing agents to verify a command would succeed before committing changes.

## Implementation Architecture

The `cli-for-agent` skill prescribes a five-stage execution flow implemented in [`example.ts`](https://github.com/cursor/plugins/blob/main/example.ts):

1. **Argument Parsing** – Use a robust parser like `yargs` or `commander` that validates flags before business logic executes
2. **Dry-Run Branch** – Early-exit after validation when `--dry-run` is present, returning a JSON summary of intended actions
3. **Execution Core** – Implement core logic in pure functions, isolating side-effects behind a thin imperative layer
4. **Error Handling** – Catch all exceptions, translate them into the canonical error JSON, and call `process.exit(1)`
5. **Help Generation** – Auto-generate detailed help pages from the declarative flag schema

This architecture ensures commands follow a predictable **verb-noun** pattern (`tool verb [options]`) and avoid positional arguments that change meaning across versions.

## Complete TypeScript Implementation

Below is a minimal CLI skeleton from the `cursor/plugins` repository that implements these patterns using `yargs`:

```typescript
#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { runTask, dryRunTask } from './task';
import { formatError } from './error';

// Define flags and layered help
yargs(hideBin(process.argv))
  .scriptName('my-tool')
  .command(
    'process',
    'Process input data',
    (y) =>
      y
        .option('input', {
          type: 'string',
          describe: 'Path to input file (or read from stdin if omitted)',
        })
        .option('output', {
          type: 'string',
          describe: 'Path for generated output',
          demandOption: true,
        })
        .option('dry-run', {
          type: 'boolean',
          default: false,
          describe: 'Validate only, do not mutate state',
        })
        .option('help', {
          alias: 'h',
          type: 'boolean',
          describe: 'Show brief usage',
        })
        .option('help=full', {
          type: 'boolean',
          describe: 'Show detailed flag docs with examples',
        }),
    async (argv) => {
      try {
        if (argv['help=full']) {
          // auto-generated detailed help (examples embedded in the schema)
          yargs.showHelp('full');
          return;
        }
        if (argv.dryRun) {
          await dryRunTask(argv);
          console.log(JSON.stringify({ status: 'dry-run-success' }));
          return;
        }
        await runTask(argv);
        console.log(JSON.stringify({ status: 'ok' }));
      } catch (e) {
        console.error(formatError(e));
        process.exit(1);
      }
    },
  )
  .parse();

```

Key implementation details:
- **Flag-first design** eliminates interactive prompts
- **Layered help** supports both quick checks and detailed reference
- **Dry-run branch** short-circuits before side-effects
- **Centralized error formatting** ensures consistent JSON output

## Summary

- **Design CLIs for `cursor/plugins` agents** using non-interactive flags (`--input`, `--output`) rather than prompts, as specified in [`cli-for-agent/skills/cli-for-agents/SKILL.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/skills/cli-for-agents/SKILL.md)
- **Implement layered help** with `--help` for brief usage and `--help=full` for exhaustive examples auto-generated from flag schemas
- **Support stdin pipelines** by reading from `process.stdin` when input flags are omitted, ensuring graceful handling of empty streams
- **Return machine-readable errors** as JSON objects with `code`, `message`, and `hint` fields to enable agent decision-making
- **Ensure idempotency** through pure functions and a standardized `--dry-run` flag that validates without mutating state

## Frequently Asked Questions

### Why must CLIs for coding agents avoid interactive prompts?

Interactive prompts cause agents to hang indefinitely because they cannot provide stdin input during execution. According to the [`cli-for-agent/README.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/README.md), any required data must be supplied via command-line flags (`--config`, `--input`) or environment variables, allowing the agent to construct complete command strings programmatically without human intervention.

### How should a CLI handle errors so that agents can recover automatically?

Agents parse structured error output to determine retry strategies. The specification in [`cli-for-agent/skills/cli-for-agents/SKILL.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/skills/cli-for-agents/SKILL.md) requires emitting errors as JSON objects containing `code`, `message`, and `hint` fields, then exiting with a non-zero status. Agents inspect the `code` field to distinguish between transient failures (worth retrying) and permanent errors (requiring abort).

### What is the purpose of the `--dry-run` flag in agent-friendly CLIs?

The `--dry-run` flag allows agents to validate that a command would succeed before committing changes, preventing partial mutations during failed operations. As implemented in [`example.ts`](https://github.com/cursor/plugins/blob/main/example.ts), the dry-run path executes all validation logic but stops before any state mutation, returning a success status if validation passes. This supports idempotent retry patterns where agents can safely re-attempt commands.

### Which argument parsing libraries work best for agent-friendly CLI design?

The `cursor/plugins` examples use `yargs` (Node.js) and `commander` due to their support for declarative flag schemas, automatic help generation, and strict validation. These libraries enable the **verb-noun** command pattern recommended in [`cli-for-agent/README.md`](https://github.com/cursor/plugins/blob/main/cli-for-agent/README.md) and facilitate the layered `--help` system required for machine introspection.