caltool acts as a scaffolding and workflow layer around cmd-arg-lib, extending it into a full CLI development environment.
CLI package generation includes:
- typed command-line argument definitions
- help screens
- shell completions
- manpages
- snapshot-style CLI tests
- hierarchical command trees
Workflow support includes local installation and removal of executables, shell completions, and manpages during development.
> caltool init --with-completion --directory MyTool --template testing
> cd MyTool
MyTool> swift test
MyTool> swift build -c release
MyTool> caltool install -s zsh fishThis creates, tests, builds and installs a complete CLI tool package with:
- help screens
- shell completions
- snapshot-style CLI testing with diff output
Initialize
Demo> caltool init -t testing --with-completion
Demo> tree -L 3
.
├── Package.swift
├── Sources
│ ├── Demo
│ │ └── Main.swift
│ └── DemoSupport
│ └── DemoSupport.swift
└── Tests
└── DemoTests
└── Tests.swift
Since executable targets cannot be imported directly into test modules, the generated package separates executable entry points from implementation logic:
- Demo - the executable entry point
- DemoSupport - the implementation and testable logic
Test
Demo> swift test
...
◇ Test simpleInput() started.
✔ Test simpleInput() passed after 0.001 seconds.
✔ Test showAnimalsTest() passed after 0.001 seconds.
✔ Test ErrorsTest() passed after 0.001 seconds.
✔ Suite DemoTest passed after 0.001 seconds.
✔ Suite DemoOutputTests passed after 0.001 seconds.
✔ Test run with 3 tests in 2 suites passed after 0.001 seconds.
Build and Install
Demo> swift build -c release && caltool install -s zsh fish
...
Build complete! (0.89s)
demo
installed "demo" in /Users/ps/.local/bin
installed "demo.fish" in /Users/ps/.config/fish/completions
installed "_demo" in /Users/ps/.config/zsh/completions
Run
Demo> demo -iuc2 wolf
1: WOLF
2: WOLF
Demo> demo -iuxyz --count
Errors:
unrecognized options: "-x", "-y" and "-z", in "-iuxyz"
missing expected value after "--count"
missing value: "<animal>"
See "demo --help" for more information.
Use CL Tool Terminal Output as Test Input
import CmdArgLib
import Testing
@testable import DemoSupport
struct DemoOutputTests {
@Test func simpleInputTest() {
let input = "-iuc2 wolf"
let output = """
1: WOLF
2: WOLF
"""
let ok = testOutput(of: DemoSupport.run, with: input, expecting: output)
#expect(ok)
}
@Test func ErrorsTest() {
let input = "-iuxyz --count"
let output = """
Errors:
unrecognized options: "-x", "-y" and "-z", in "-iuxyz"
missing expected value after "--count"
missing a "<animal>"
See "demo --help" for more information.
"""
let ok = testOutput(of: DemoSupport.run, with: input, expecting: output)
#expect(ok)
}
}A Test that Fails
If the following contrived test is added to the generated tests, it will fail, because the expected output is purposely incorrect. (I.e., "an", "an", and "demos", are wrong.)
@Test func FailedErrorsTest() {
let input = "-iuxyz --count"
let output = """
Errors:
unrecognized options: "-x", "-y" and "-z", in "-iuxyz"
missing an expected value after "--count"
missing an "<animal>"
See "demos --help" for more information.
"""
let ok = testOutput(of: DemoSupport.run, with: input, expecting: output)
#expect(ok)
}Failed Test Report
Here is how the bad test shows up when you run swift test
Demo> swift test
...
◇ Test FailedErrorsTest() started.
------ IO MISMATCH at DemoTests/Tests.swift:66
Errors:
unrecognized options: "-x", "-y" and "-z", in "-iuxyz"
- missing expected value after "--count"
- missing value: "<animal>"
- See "demo --help" for more information.
+ missing an expected value after "--count"
+ missing an "<animal>"
+ See "demos --help" for more information.
------ END MISMATCH ("-" and "+" indicate changes required to match expected)
...
✘ Test run with 4 tests in 2 suites failed after 0.001 seconds with 1 issue.
Uninstall
Demo> caltool uninstall
demo
uninstalled "demo" in /Users/ps/.local/bin
uninstalled "demo.fish" in /Users/ps/.config/fish/completions
uninstalled "_demo" in /Users/ps/.config/zsh/completions
caltool has three subcommands:
- init - initialize a new package based on a template
- install - install executables, completions and manual pages (locally)
- uninstall - uninstall executables completions and manual pages (locally)
The available templates are:
| Template | Type | Help Screen | Completion Options | Parameters Option |
|---|---|---|---|---|
| opaque | minimal CLI | no | --with-less | |
| basic | basic CLI | yes | --with-completion | --with-less |
| testing | basic CLI with tests | yes | --with-completion | --with-less |
| manpage | basic CLI with manpages | yes | --with-completion | |
| simple-tree | stateless hierarchical CLI | yes | --with-completion | |
| stateful-tree | stateful hierarchical CLI | yes | --with-completion |
Developing a polished CLI tool involves more than argument parsing.
Projects often need:
- help screens
- manual pages
- shell completions
- testing infrastructure
- installation workflows
Rebuilding this infrastructure for every project slows development and distracts from tool design.
cmd-arg-lib provides the parsing and documentation system.
caltool provides templates, installation workflows, and a terminal-centered development cycle around it.
The project is intentionally opinionated toward terminal-centered development using cmd-arg-lib.
The project is in beta, version 0.1.2, and has only been implemented for macOS.
The project requires Swift 6.2 and macOS 26.1, or later.
Installation requires some setup. See INSTALL.
Feedback and bug reports are welcome.