commands_cli 0.4.1
commands_cli: ^0.4.1 copied to clipboard
A powerful CLI tool that generates type-safe, project-local command wrappers from YAML definitions for Dart and Flutter developers.
A simple, yet powerful command-line interface (CLI) tool to define and run project-local commands, similar to a Makefile.
This package allows you to create a commands.yaml file in your project's root directory and define a set of keywords, which can then be executed from the command line.
Why use commands instead of Makefile? π€ #
While Makefile is a powerful and widely used tool, commands_cli offers several advantages for modern development workflows, especially for Dart and Flutter projects:
-
Cross-platform Compatibility:
commands_cliis written in Dart and runs on any platform where the Dart SDK is available. This means your commands will work consistently across macOS, Linux, and Windows. -
Simplicity and Readability:
commands.yamluses the clean and human-readable YAML format. This makes your scripts easier to read, write, and maintain, even for teammates who arenβt familiar with traditional Makefiles.# commands.yaml tests: ## Run all tests with coverage script: flutter test --coverage --no-pub$ tests 00:00 +1: All tests passed! -
Structured Parameters:
commands_clilets you define both positional and named parameters in a clear, structured way. Parameters can be required or optional, and you can set default values when needed. This makes your commands self-documenting, easy to use, and far more powerful than Makefile's limited and often clumsy parameter handling.# commands.yaml tell: script: echo "{message} {name}" params: required: - message: optional: - name: '-n, --name'$ tell hello hello $ tell Goodbye -n Makefile Goodbye Makefile $ tell β Missing required positional param: message -
Strong Type System: Unlike Makefile's string-based approach,
commands_clisupports a powerful type system with string, boolean, integer, double, number, and enum types. This provides built-in validation, preventing common errors and making your commands more robust.# commands.yaml deploy: ## Deploy with replicas script: echo "Deploying with {replicas} replicas" params: optional: - replicas: '-r, --replicas' type: int default: 3$ deploy -r 5 Deploying with 5 replicas $ deploy -r abc β Parameter replicas expects an [integer] Got: abc [string] -
Built-in Interactive Pickers: When you define enum parameters or switch commands without defaults,
commands_cliautomatically presents a beautiful interactive menu. No need to parse input manually or write custom promptsβit's all handled for you.# commands.yaml build: ## Build for platform script: echo "Building for {platform}" params: required: - platform: '-p, --platform' values: [ios, android, web]$ build Select value for platform: 1. ios β 2. android 3. web Press number (1-3) or press Esc to cancel: -
Automatic Help Generation: Every command automatically gets a
--help(or-h) parameter. It collects information from your defined parameters and optional comments directly from thecommands.yamlfile, providing clear, up-to-date guidance without any extra work.# commands.yaml hello: ## Prints "Hello {message}" script: echo "Hello {message}" params: required: - message: ## The name to include in the greeting default: "World"$ hello --help hello: Prints "Hello {message}" params: required: message: The name to include in the greeting default: "World" -
Composable, Human-Readable Commands: With
commands_cli, you can define keyword chains that read like plain English. Instead of cryptic flags, you can run natural phrases such as:build iosbuild androidbuild webbuild allrun all testsrun integration tests- β¦and more
This switch-based design makes commands easier to discover, remember, and use.
Getting Started π #
-
Activate the package:
$ dart pub global activate commands_cli -
Create a
commands.yamlfile in the root of your project or type:$ commands createThis will create this
commands.yamlfor you,already pre-filled with a simple
helloexample. -
Define your commands:
# commands.yaml hello: ## Prints "Hello {message}" script: echo "Hello {message}" params: required: - message: default: "World" -
Activate your defined commands:
$ commands β hello: Prints "Hello {message}". Type "hello --help" to learn more. -
Run your defined commands:
$ hello Hello World $ hello dev Hello dev
Usage #
The commands.yaml file has a simple structure:
<command_name>: ## <command_description>
script: |
# Your script goes here
params:
required:
- <param_name>: '<flags>' ## <param_description>
default: <default_value>
optional:
- <param_name>: '<flags>' ## <param_description>
default: <default_value>
<command_name>: The name of your command.<command_description>: An optional description for your command.script: The script to be executed. You can use multi-line scripts using the|character.params: An optional section to define parameters for your command.required: A list of required parameters.optional: A list of optional parameters.<param_name>: The name of the parameter.<flags>: Optional flags for named parameters (e.g.,-n, --name).<param_description>: An optional description for the parameter.<default_value>: An optional default value for the parameter.
Examples #
Here are some examples of how to define and use commands_cli in your commands.yaml file:
Basic Command #
# commands.yaml
hello:
script: echo "Hello, World!"
Activate your defined commands:
$ commands
β
hello: Type "hello --help" to learn more.
Run:
$ hello
Hello, World!
Positional Parameters #
# commands.yaml
greet:
script: echo "{greeting} {name}!"
params:
required:
- greeting:
optional:
- name:
Activate your defined commands:
$ commands
β
greet: Type "greet --help" to learn more.
Run:
$ greet Hi dev
Hi dev!
$ greet Yo
Yo !
$ greet
β Missing required positional param: greeting
Named Parameters #
# commands.yaml
greet:
script: echo "{greeting} {value}!"
params:
required:
- greeting: '-g, --greeting'
optional:
- value: '-n, --name'
Activate your defined commands:
$ commands
β
greet: Type "greet --help" to learn more.
Run:
$ greet --greeting "Hi" --name "Alice"
Hi Alice!
$ greet -g "Hi"
Hi !
$ greet
β Missing required named param: greeting
Optional Parameters with Default Values #
# commands.yaml
goodbye:
script: echo "Goodbye, {name}{punctuation}"
params:
optional:
- name:
default: "World"
- punctuation:
default: "!"
Activate your defined commands:
$ commands
β
goodbye: Type "goodbye --help" to learn more.
Run:
$ goodbye
Goodbye, World!
$ goodbye --name "Bob" -p "."
Goodbye, Bob.
Passthrough Arguments #
Basic Alias
# commands.yaml
d: ## dart alias
script: dart ...args
Here, ...args is a placeholder that automatically forwards any parameters you pass to the alias into the underlying command.
For example, running d --version will expand to dart --version.
This allows you to create concise aliases while still keeping the flexibility to inject flags, options, or arguments dynamically at runtime.
Activate your defined commands:
$ commands
β
d: dart alias. Type "d --help" to learn more.
Run:
$ d --version
Dart SDK version: 3.9.0...
Multiline script with passthrough arguments
# commands.yaml
analyze: ## dart analyze
script: |
echo "Analyzing ignoring warnings..."
dart analyze ...args --no-fatal-warnings
Activate your defined commands:
$ commands
β
analyze: dart analyze. Type "analyze --help" to learn more.
Run:
$ analyze --fatal-infos
Analyzing ignoring warnings...
Analyzing example... 0.5s
No issues found!
Strongly Typed Parameters #
commands_cli supports a powerful type system for parameters, allowing you to define explicit types and constrain values for better validation and user experience.
Supported Types
| Type | Aliases | Description |
|---|---|---|
string |
- | Text values (default if not specified) |
boolean |
bool |
Boolean values (true or false) |
integer |
int |
Whole numbers (no decimal point allowed) |
double |
- | Decimal numbers (must include decimal point) |
number |
num |
Any numeric value (integer or decimal) |
Integer Type
The integer type (or int) accepts only whole numbers. Values with decimal points are rejected.
# commands.yaml
deploy: ## Deploy application
script: echo "Deploying to port {port}"
params:
optional:
- port: '-p, --port'
type: int
default: 3000
Run:
$ deploy -p 8080
Deploying to port 8080
$ deploy -p 3.14
β Parameter port expects an [integer]
Got: 3.14 [double]
$ deploy -p abc
β Parameter port expects an [integer]
Got: abc [string]
Double Type
The double type accepts only decimal numbers. The value must include a decimal point.
# commands.yaml
configure: ## Configure timeout
script: echo "Timeout set to {timeout}s"
params:
optional:
- timeout: '-t, --timeout'
type: double
default: 30.5
Run:
$ configure -t 60.0
Timeout set to 60.0s
$ configure -t 1
β Parameter timeout expects a [double]
Got: 1 [integer]
Number Type
The number type (or num) accepts both integers and decimals - any numeric value.
# commands.yaml
calculate: ## Calculate with value
script: echo "Value is {value}"
params:
optional:
- value: '-v, --value'
type: number
Run:
$ calculate -v 42
Value is 42
$ calculate -v 3.14
Value is 3.14
$ calculate -v abc
β Parameter value expects a [number]
Got: abc [string]
Boolean Type
The boolean type (or bool) accepts only true or false values. When used as a flag without a value, it toggles the default.
# commands.yaml
build: ## Build with options
script: |
echo "verbose={verbose} debug={debug}"
params:
optional:
- verbose: '-v, --verbose'
type: boolean
default: false
- debug: '-d, --debug'
type: boolean
default: true
Run:
$ build
verbose=false debug=true
$ build -v
verbose=true debug=true
$ build -v -d
verbose=true debug=false
Enum Parameters with Default
Restrict parameter values to a predefined set using values:
# commands.yaml
deploy: ## Deploy to environment
script: |
echo "Deploying to {env}"
params:
optional:
- env: '-e, --environment'
values: [dev, staging, prod]
default: staging
Run:
$ deploy
Deploying to staging
$ deploy -e prod
Deploying to prod
$ deploy -e invalid
β Invalid value 'invalid' for parameter env
π‘ Allowed values: dev, staging, prod
Interactive Enum Picker
When you define an enum parameter without a default value, commands_cli will automatically present an interactive picker when the parameter is not provided:
# commands.yaml
build: ## Build for platform
script: |
echo "Building for {platform}"
params:
optional:
- platform: '-p, --platform'
values: [ios, android, web]
Run:
$ build -p ios
Building for ios
$ build
Select value for platform:
1. ios β
2. android
3. web
Press number (1-3) or press Esc to cancel:
Switch Commands (Nested Options) #
The switch feature allows you to create commands with multiple named sub-options, enabling natural command structures like build ios, build android, or run tests.
Basic Switch with Default
# commands.yaml
build: ## Build application
switch:
- ios: ## Build for iOS
script: flutter build ios
- android: ## Build for Android
script: flutter build apk
- web: ## Build for web
script: flutter build web
- default: ios
Activate your defined commands:
$ commands
β
build: Build application. Type "build --help" to learn more.
Run:
$ build
Building iOS...
$ build android
Building Android...
$ build web
Building web app...
Interactive Switch Picker
When no default option is specified, commands_cli presents an interactive menu:
# commands.yaml
deploy: ## Deploy application
switch:
- staging: ## Deploy to staging
script: ./deploy.sh staging
- production: ## Deploy to production
script: ./deploy.sh production
Run:
$ deploy staging
Deploying to staging...
$ deploy
Select an option for deploy:
1. staging β - Deploy to staging
2. production - Deploy to production
Press number (1-2) or press Esc to cancel:
Switch with Parameters
Each switch option can have its own parameters:
# commands.yaml
deploy: ## Deploy with configuration
switch:
- staging: ## Deploy to staging
script: |
echo "Deploying to staging with {replicas} replicas"
params:
optional:
- replicas: '-r, --replicas'
type: int
default: 2
- production: ## Deploy to production
script: |
echo "Deploying to production with {replicas} replicas"
params:
optional:
- replicas: '-r, --replicas'
type: int
default: 5
- default: staging
Run:
$ deploy
Deploying to staging with 2 replicas
$ deploy production -r 10
Deploying to production with 10 replicas
Switch with Enum Parameters
Combine switches with enum parameters for even more powerful command structures:
# commands.yaml
test: ## Run tests
switch:
- unit: ## Run unit tests
script: |
echo "Running unit tests on {platform}"
params:
optional:
- platform: '-p, --platform'
values: [vm, chrome, all]
default: vm
- integration: ## Run integration tests
script: |
echo "Running integration tests on {platform}"
params:
optional:
- platform: '-p, --platform'
values: [ios, android, all]
- default: unit
Run:
$ test unit
Running unit tests on vm
$ test unit -p chrome
Running unit tests on chrome
$ test integration
Select value for platform:
1. ios β
2. android
3. all
Press number (1-3) or press Esc to cancel:
Overriding existing commands #
In order to override commands like: clear, ls, cd, make etc.
You want your .pub-cache/bin dir to be first, not last.
So instead of:
# .zshrc
export PATH="$PATH:$HOME/.pub-cache/bin"
use:
# .zshrc
export PATH="$HOME/.pub-cache/bin:$PATH"
After changing .zshrc, reload it:
$ source ~/.zshrc
zsh (and bash) keep a hash table of command lookups to speed things up, to rehash:
$ hash -r
Define your custom ls command and explicitly mark it as overridable using override: true:
# commands.yaml
ls: ## custom ls
override: true # required when overriding reserved commands
script: echo "ls is overridden!"
Activate your defined commands:
$ commands
β
ls: custom ls. Type "ls --help" to learn more.
Run:
$ ls
ls is overridden!
Overriding test and which keywords
On POSIX shells (bash, zsh, sh), test and which are not just programs in /bin β they're also shell builtins.
That means the shell resolves these commands before looking into $PATH.
So even if you put executables called test or which at the front of your $PATH, the shell will happily use its own builtins instead.
Shadow them with functions. That way:
- Your functions always override the builtins.
- By default, they just delegate to the system binaries.
- If later you drop custom commands, they will be found first in $PATH (just like
lscase).
# .zshrc
# Shadow the builtin "test" with a function
test() {
# Explicitly call the system binary unless PATH provides an override
command test "$@"
}
# Shadow the builtin "which" with a function
which() {
# Explicitly call the system binary unless PATH provides an override
command which "$@"
}
Define your custom commands:
# commands.yaml
test: ## custom test
override: true # required when overriding reserved commands
script: echo "test is overridden!"
which: ## custom which
override: true # required when overriding reserved commands
script: echo "which is overridden!"
Activate your defined commands:
$ commands
β
test: custom test. Type "test --help" to learn more.
β
which: custom which. Type "which --help" to learn more.
Run:
$ test
test is overridden!
$ which
which is overridden!
