Skip to content

Instantly share code, notes, and snippets.

@jdx
Last active October 26, 2025 22:51
Show Gist options
  • Save jdx/c3743f3b412ddaeff26ba38a2fc2c0ad to your computer and use it in GitHub Desktop.
Save jdx/c3743f3b412ddaeff26ba38a2fc2c0ad to your computer and use it in GitHub Desktop.

Improved YAML CLI Spec Format

This shows a refined YAML format for CLI specifications with improvements:

  • Args use plain names without angle brackets
  • Completions use YAML's multiline string syntax with |

KDL (Current Format - for reference)

name "mycli"
bin "mycli"
version "1.0.0"
about "A simple CLI tool"

flag "-v --verbose" help="Enable verbose output" global=#true count=#true
flag "-q --quiet" help="Suppress output" global=#true

cmd "server" help="Manage the server" {
  cmd "start" help="Start the server" {
    flag "-p --port <port>" help="Port to listen on" default="8080"
    flag "-h --host <host>" help="Host to bind to" default="localhost"
    flag "-d --daemon" help="Run in background"
  }

  cmd "stop" help="Stop the server" {
    flag "-f --force" help="Force stop"
  }

  cmd "config" help="Manage server configuration" {
    cmd "set" help="Set a server config value" {
      arg "<key>" help="Configuration key"
      arg "<value>" help="Configuration value"
      flag "--restart" help="Restart server after change"
    }

    cmd "get" help="Get a server config value" {
      arg "<key>" help="Configuration key"
      flag "--json" help="Output as JSON"
    }

    cmd "list" help="List all server config values" {
      flag "--filter <pattern>" help="Filter by pattern"
    }
  }
}

cmd "config" help="Manage global configuration" {
  cmd "set" help="Set a config value" {
    arg "<key>" help="Configuration key"
    arg "<value>" help="Configuration value"
  }

  cmd "get" help="Get a config value" {
    arg "<key>" help="Configuration key"
  }
}

complete "key" run="echo 'host\nport\ndebug'"

YAML (Improved)

name: mycli
bin: mycli
version: 1.0.0
about: A simple CLI tool

flags:
  "-v --verbose":
    help: Enable verbose output
    global: true
    count: true
  "-q --quiet":
    help: Suppress output
    global: true

commands:
  server:
    help: Manage the server
    subcommands:
      start:
        help: Start the server
        flags:
          "-p --port <port>":
            help: Port to listen on
            default: "8080"
          "-h --host <host>":
            help: Host to bind to
            default: localhost
          "-d --daemon":
            help: Run in background

      stop:
        help: Stop the server
        flags:
          "-f --force":
            help: Force stop

      config:
        help: Manage server configuration
        subcommands:
          set:
            help: Set a server config value
            args:
              key:
                help: Configuration key
              value:
                help: Configuration value
            flags:
              "--restart":
                help: Restart server after change

          get:
            help: Get a server config value
            args:
              key:
                help: Configuration key
            flags:
              "--json":
                help: Output as JSON

          list:
            help: List all server config values
            flags:
              "--filter <pattern>":
                help: Filter by pattern

  config:
    help: Manage global configuration
    subcommands:
      set:
        help: Set a config value
        args:
          key:
            help: Configuration key
          value:
            help: Configuration value

      get:
        help: Get a config value
        args:
          key:
            help: Configuration key

completions:
  key:
    run: |
      echo 'host
      port
      debug'

Key Improvements

1. Args without angle brackets

Before:

args:
  "<key>":
    help: Configuration key

After:

args:
  key:
    help: Configuration key

This is cleaner since:

  • Angle brackets are CLI convention for documentation, not actual identifiers
  • The arg name is now a valid YAML key without special characters
  • Easier to type and read

2. Multiline strings with |

Before:

completions:
  key:
    run: echo 'host\nport\ndebug'

After:

completions:
  key:
    run: |
      echo 'host
      port
      debug'

Benefits:

  • More readable for multi-line commands
  • No need to escape newlines with \n
  • Natural formatting for shell scripts
  • Can easily add more complex completion logic

3. Additional multiline examples

For more complex completions:

completions:
  plugin:
    run: |
      mise plugins --core
      mise plugins --user

  task:
    run: |
      if [ -f Taskfile.yml ]; then
        task --list-all | awk '{print $2}'
      fi

Or for long help text:

commands:
  deploy:
    help: |
      Deploy the application to production.

      This will build the app, run tests, and deploy to
      the configured production environment.
    args:
      environment:
        help: Target environment (staging/production)

Format Comparison

Feature KDL YAML (Improved)
Lines 41 71
Arg syntax arg "<key>" key:
Flag syntax flag "-v --verbose" "-v --verbose":
Completions run="echo..." with \n run: | multiline
Nesting Braces { } Indentation
Comments C-style // or # #

Both formats are now quite readable, with KDL being more compact and YAML being more familiar to most developers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment