Skip to content

Composable Sub-commands

Gunshi makes it easy to create CLIs with multiple sub-commands, allowing you to build complex command-line applications with a modular structure. This approach is similar to tools like Git, where commands like git commit and git push are sub-commands of the main git command.

Why Use Sub-commands?

Sub-commands are useful when your CLI needs to perform different operations that warrant separate commands. Benefits include:

  • Organization: Group related functionality logically
  • Scalability: Add new commands without modifying existing ones
  • User experience: Provide a consistent interface for different operations
  • Help system: Each sub-command can have its own help documentation

Basic Structure

A CLI with sub-commands typically has this structure:

sh
cli <command> [command options]

For example:

sh
cli create --name my-resource

Creating Sub-commands

Here's how to create a CLI with sub-commands in Gunshi:

js
import { cli } from 'gunshi'

// Define sub-commands
const createCommand = {
  name: 'create',
  description: 'Create a new resource',
  options: {
    name: { type: 'string', short: 'n' }
  },
  run: ctx => {
    console.log(`Creating resource: ${ctx.values.name}`)
  }
}

const listCommand = {
  name: 'list',
  description: 'List all resources',
  run: () => {
    console.log('Listing all resources...')
  }
}

// Create a Map of sub-commands
const subCommands = new Map()
subCommands.set('create', createCommand)
subCommands.set('list', listCommand)

// Define the main command
const mainCommand = {
  name: 'manage',
  description: 'Manage resources',
  run: () => {
    console.log('Use one of the sub-commands: create, list')
  }
}

// Run the CLI with composable sub-commands
await cli(process.argv.slice(2), mainCommand, {
  name: 'my-app',
  version: '1.0.0',
  subCommands
})

Type-Safe Sub-Commands

When working with sub-commands, you can maintain type safety:

ts
import { cli } from 'gunshi'
import type { ArgOptions, Command } from 'gunshi'

// Define type-safe sub-commands
const createCommand: Command<ArgOptions> = {
  name: 'create',
  options: {
    name: { type: 'string', short: 'n' }
  },
  run: ctx => {
    console.log(`Creating: ${ctx.values.name}`)
  }
}

const listCommand: Command<ArgOptions> = {
  name: 'list',
  run: () => {
    console.log('Listing items...')
  }
}

// Create a Map of sub-commands
const subCommands = new Map<string, Command<ArgOptions>>()
subCommands.set('create', createCommand)
subCommands.set('list', listCommand)

// Define the main command
const mainCommand: Command<ArgOptions> = {
  name: 'app',
  run: () => {
    console.log('Use a sub-command: create, list')
  }
}

// Execute with type-safe sub-commands
await cli(process.argv.slice(2), mainCommand, {
  subCommands
})

Released under the MIT License.