commands_cli 0.3.2
commands_cli: ^0.3.2 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 int, double, boolean, 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: optional: - 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}" -
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
Run:
$ hello
Hello, World!
Positional Parameters #
# commands.yaml
greet:
script: echo "{greeting} {name}!"
params:
required:
- greeting:
optional:
- name:
Activate your defined commands:
$ commands
β
greet
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
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
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
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
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.
Numeric Types
You can explicitly specify int or double types for numeric parameters:
# commands.yaml
deploy: ## Deploy application
script: |
echo "Deploying to port {port} with timeout {timeout}s"
params:
optional:
- port: '-p, --port'
type: int
default: 3000
- timeout: '-t, --timeout'
type: double
default: 30.5
Run:
$ deploy -p 8080 -t 60.0
Deploying to port 8080 with timeout 60.0s
$ deploy -p abc
β Parameter port expects an [integer]
Got: "abc" [string]
Boolean Flags
Boolean parameters can be toggled on/off:
# commands.yaml
build: ## Build with options
script: |
echo "verbose={verbose} debug={debug}"
params:
optional:
- verbose: '-v, --verbose'
default: false
- debug: '-d, --debug'
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
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
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
β
which: custom which
Run:
$ test
test is overridden!
$ which
which is overridden!
