AGENTS.md@skills/development/commanderjs · git:20261004.cc33b37 · 2026-10-04 · sha256 1827ecdd96ec8454

AGENTS.md@skills/development/commanderjs git:20261004.cc33b37A

Immutable. This exact content never changes and is served at /api/v1/blob/1827ecdd96ec8454.

# Commander.js Reference Guide

Complete reference for building command-line interfaces with Commander.js.

## Core Patterns

### Modular Command Structure (Recommended)

Always export Command instances from modules and use `.addCommand()`:

```typescript
// commands/build.ts
import { Command } from 'commander';

export const buildCommand = new Command('build')
  .description('Build project')
  .option('-e, --env <name>', 'Environment', 'development')
  .action((options) => {
    console.log('Building:', options);
  });

// index.ts
import { Command } from 'commander';
import { buildCommand } from './commands/build';
import packageJson from './package.json' with { type: 'json' };

const program = new Command();
program
  .name('my-cli')
  .version(packageJson.version)
  .addCommand(buildCommand);

await program.parseAsync(process.argv);
```

### Typed Options Pattern (REQUIRED)

Always pass typed option objects from commands to service layer:

```typescript
// types/build-options.ts
export type BuildOptions = {
  env: string;
  output: string;
  sourcemap: boolean;
};

// services/build-service.ts
export const buildProject = (options: BuildOptions): void => {
  console.log('Building with:', options);
};

// commands/build.ts
import { Command } from 'commander';
import { buildProject } from '../services/build-service';
import type { BuildOptions } from '../types/build-options';

export const buildCommand = new Command('build')
  .description('Build project')
  .option('-e, --env <name>', 'Environment', 'development')
  .option('-o, --output <dir>', 'Output directory', 'dist')
  .option('--sourcemap', 'Generate sourcemaps', false)
  .action((options: BuildOptions) => {
    buildProject(options); // Type-safe!
  });
```

**Why:** Type safety prevents errors, documents API contracts, enables refactoring.

### Subcommands with Barrel Exports

```typescript
// commands/db/migrate.ts
export const migrateCommand = new Command('migrate')
  .description('Run migrations')
  .action(() => { /* ... */ });

// commands/db/seed.ts
export const seedCommand = new Command('seed')
  .description('Seed database')
  .action(() => { /* ... */ });

// commands/db/index.ts
import { Command } from 'commander';
import { migrateCommand } from './migrate';
import { seedCommand } from './seed';

export const dbCommand = new Command('db')
  .description('Database operations')
  .addCommand(migrateCommand)
  .addCommand(seedCommand);

// index.ts
import { dbCommand } from './commands/db';
program.addCommand(dbCommand);
```

## Essential Patterns

### Program Setup

```typescript
import { Command } from 'commander';
import packageJson from './package.json' with { type: 'json' };

const program = new Command();

program
  .name('my-cli')
  .description('CLI tool')
  .version(packageJson.version); // Load from package.json

await program.parseAsync(process.argv);
```

### Options

```typescript
program
  .option('-p, --port <number>', 'Port', '3000')
  .option('-f, --force', 'Force operation')
  .requiredOption('-c, --config <path>', 'Config file');
```

### Arguments

```typescript
program
  .argument('<source>', 'Source file')
  .argument('[destination]', 'Destination file')
  .argument('<files...>', 'Files to process');
```

### Action Handlers (Async)

```typescript
program
  .command('deploy')
  .action(async (options) => {
    try {
      await deploy(options);
    } catch (error) {
      console.error('Deployment failed:', error.message);
      process.exit(1);
    }
  });

await program.parseAsync(process.argv); // Must use parseAsync
```

### TypeScript Types with Service Layer

```typescript
// types/build-options.ts
export type BuildOptions = {
  env: string;
  minify: boolean;
};

// services/build-service.ts
export const buildProject = (options: BuildOptions): void => {
  console.log('Building:', options);
};

// commands/build.ts
import type { BuildOptions } from '../types/build-options';
import { buildProject } from '../services/build-service';

export const buildCommand = new Command('build')
  .option('-e, --env <name>', 'Environment', 'development')
  .option('-m, --minify', 'Minify', false)
  .action((options: BuildOptions) => {
    buildProject(options); // Pass all options to service
  });
```

## Directory Structure

```
src/
├── commands/
│   ├── build.ts          # Export buildCommand
│   ├── deploy.ts         # Export deployCommand
│   ├── db/
│   │   ├── migrate.ts    # Export migrateCommand
│   │   ├── seed.ts       # Export seedCommand
│   │   └── index.ts      # Export dbCommand
│   └── index.ts          # Barrel exports
└── index.ts              # Main program
```

## Key Rules

1. **Export Command instances** - Never use inline command definitions for large CLIs
2. **Use `.addCommand()`** - Attach subcommands using this method
3. **Create barrel exports** - Export commands from `commands/index.ts`
4. **One command per file** - Follow single function per module pattern
5. **Use `parseAsync()`** - Always use async parsing for async handlers
6. **Type your options** - Define TypeScript interfaces for options
7. **Handle errors** - Use try/catch in action handlers
8. **Validate inputs** - Check arguments and options in handlers
9. **Use kebab-case** - All CLI flags should use kebab-case
10. **Provide defaults** - Give sensible defaults for optional options

## Common Anti-Patterns

❌ **Inline subcommands** (harder to test/maintain):
```typescript
const db = program.command('db');
db.command('migrate').action(() => {});
```

✅ **Modular subcommands**:
```typescript
export const migrateCommand = new Command('migrate').action(() => {});
export const dbCommand = new Command('db').addCommand(migrateCommand);
```

❌ **Using `parse()` with async**:
```typescript
program.action(async () => {});
program.parse(process.argv); // Wrong
```

✅ **Using `parseAsync()`**:
```typescript
program.action(async () => {});
await program.parseAsync(process.argv); // Correct
```

❌ **No error handling**:
```typescript
program.action(async () => {
  await deploy(); // What if this throws?
});
```

✅ **Proper error handling**:
```typescript
program.action(async () => {
  try {
    await deploy();
  } catch (error) {
    console.error('Error:', error.message);
    process.exit(1);
  }
});
```

## Testing Pattern

```typescript
import { Command } from 'commander';

export const createProgram = (): Command => {
  const program = new Command();
  program.exitOverride(); // Throw instead of exit
  
  // Add commands
  
  return program;
};

// In tests
describe('CLI', () => {
  it('should parse options', async () => {
    const program = createProgram();
    await program.parseAsync(['node', 'test', '--port', '8080']);
    expect(program.opts().port).toBe('8080');
  });
});
```

## Reference Files

For detailed documentation, see:
- `references/core-basics.md` - Program setup, parsing, help
- `references/options-flags.md` - Options, defaults, validation
- `references/commands-structure.md` - Modular commands, subcommands
- `references/actions-handlers.md` - Async handlers, error handling
- `references/typescript-setup.md` - TypeScript integration, types
- `references/practices-patterns.md` - Patterns, testing, best practices

## Quick Reference

| Concept | Pattern | Example |
|---------|---------|---------|
| Program | `new Command()` | `const program = new Command()` |
| Command | Export from module | `export const cmd = new Command('name')` |
| Attach | `.addCommand()` | `program.addCommand(buildCommand)` |
| Option | `.option()` | `.option('-p, --port <n>', 'Port', '3000')` |
| Required | `.requiredOption()` | `.requiredOption('-c, --config <path>')` |
| Argument | `.argument()` | `.argument('<source>', 'Source file')` |
| Action | `.action()` | `.action(async (opts) => { /* ... */ })` |
| Parse | `parseAsync()` | `await program.parseAsync(process.argv)` |
| Type | Interface | `action((opts: BuildOptions) => {})` |

## External Resources

- GitHub: <https://github.com/tj/commander.js>
- Documentation: <https://github.com/tj/commander.js/blob/master/Readme.md>
- Examples: <https://github.com/tj/commander.js/tree/master/examples>