Files
gh-vanman2024-cli-builder-p…/skills/oclif-patterns/examples/basic-cli-example.md
2025-11-30 09:04:14 +08:00

3.4 KiB

Basic CLI Example

Complete example of a simple oclif CLI with multiple commands.

Project Structure

mycli/
├── package.json
├── tsconfig.json
├── src/
│   ├── commands/
│   │   ├── hello.ts
│   │   ├── goodbye.ts
│   │   └── config.ts
│   └── index.ts
├── test/
│   └── commands/
│       └── hello.test.ts
└── bin/
    └── run.js

Step-by-Step Setup

1. Initialize Project

mkdir mycli && cd mycli
npm init -y
npm install @oclif/core
npm install --save-dev @oclif/test @types/node typescript ts-node oclif

2. Create package.json Configuration

Add oclif configuration:

{
  "name": "mycli",
  "version": "1.0.0",
  "oclif": {
    "bin": "mycli",
    "commands": "./lib/commands",
    "plugins": [
      "@oclif/plugin-help"
    ]
  }
}

3. Create Hello Command

File: src/commands/hello.ts

import { Command, Flags, Args } from '@oclif/core'

export default class Hello extends Command {
  static description = 'Say hello to someone'

  static examples = [
    '<%= config.bin %> <%= command.id %> Alice',
    '<%= config.bin %> <%= command.id %> Bob --greeting="Hi"',
  ]

  static flags = {
    greeting: Flags.string({
      char: 'g',
      description: 'Greeting to use',
      default: 'Hello',
    }),
    excited: Flags.boolean({
      char: 'e',
      description: 'Add exclamation',
      default: false,
    }),
  }

  static args = {
    name: Args.string({
      description: 'Name to greet',
      required: true,
    }),
  }

  async run(): Promise<void> {
    const { args, flags } = await this.parse(Hello)

    const punctuation = flags.excited ? '!' : '.'
    this.log(`${flags.greeting}, ${args.name}${punctuation}`)
  }
}

4. Build and Test

npm run build
./bin/run.js hello World
# Output: Hello, World.

./bin/run.js hello World --greeting="Hi" --excited
# Output: Hi, World!

5. Add Help Documentation

./bin/run.js hello --help

# Output:
# Say hello to someone
#
# USAGE
#   $ mycli hello NAME [-g <value>] [-e]
#
# ARGUMENTS
#   NAME  Name to greet
#
# FLAGS
#   -e, --excited          Add exclamation
#   -g, --greeting=<value> [default: Hello] Greeting to use

Testing

File: test/commands/hello.test.ts

import { expect, test } from '@oclif/test'

describe('hello', () => {
  test
    .stdout()
    .command(['hello', 'World'])
    .it('says hello', ctx => {
      expect(ctx.stdout).to.contain('Hello, World.')
    })

  test
    .stdout()
    .command(['hello', 'Alice', '--excited'])
    .it('says hello with excitement', ctx => {
      expect(ctx.stdout).to.contain('Hello, Alice!')
    })

  test
    .stdout()
    .command(['hello', 'Bob', '--greeting=Hi'])
    .it('uses custom greeting', ctx => {
      expect(ctx.stdout).to.contain('Hi, Bob.')
    })
})

Run Tests

npm test

Distribution

Package for npm

npm pack
npm publish

Install Globally

npm install -g .
mycli hello World

Key Concepts Demonstrated

  1. Command Structure: Basic command with flags and args
  2. Flag Types: String and boolean flags with defaults
  3. Arguments: Required string argument
  4. Help Documentation: Auto-generated from metadata
  5. Testing: Using @oclif/test for command testing
  6. Build Process: TypeScript compilation to lib/
  7. CLI Binary: bin/run.js entry point