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

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, 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 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, 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 mandates that errors be emitted as JSON objects with a standardized schema:

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 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:

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:

  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:

#!/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
  • 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, 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 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, 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 and facilitate the layered --help system required for machine introspection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →