modular_cli_sdk 0.2.0
modular_cli_sdk: ^0.2.0 copied to clipboard
Command-centric SDK for building modular CLIs with Dart — Command/Input/Output contract, structured errors, output formatting, and automatic TTY detection. Built on cli_router.
modular_cli_sdk #
Command-centric SDK for building modular CLIs with Dart.
Define Command classes (input → validate → execute → output), connect them to CLI routes, and get automatic output formatting with JSON and plain text modes.
Also see: modular_api — the HTTP counterpart with the same architecture.
Quick start #
import 'package:cli_router/cli_router.dart';
import 'package:modular_cli_sdk/modular_cli_sdk.dart';
void main(List<String> args) async {
final cli = ModularCli();
// Root-level commands (no module prefix)
cli.command<VersionInput, VersionOutput>(
'version',
(req) => VersionCommand(VersionInput.fromCliRequest(req)),
description: 'Print version info',
);
// Module-scoped commands
cli.module('greetings', (m) {
m.command<HelloInput, HelloOutput>(
'hello',
(req) => HelloCommand(HelloInput.fromCliRequest(req)),
description: 'Say hello to someone',
);
});
final code = await cli.run(args);
exit(code);
}
dart run bin/main.dart version
# version: 0.2.0
dart run bin/main.dart version --json
# {"version": "0.2.0"}
dart run bin/main.dart greetings hello --name World
# greeting: Hello, World!
dart run bin/main.dart greetings hello --name World --json
# {"greeting": "Hello, World!"}
See example/ for a full working example with root commands and two modules (greetings + math).
Features #
Command<I, O>— pure business logic, no I/O concernsInput/Output— typed DTOs for command I/OCommandException— structured errors with code, message, exit code, and retryable flagModularCli+ModuleBuilder— module registration and routing- Root commands — register commands without a module prefix via
cli.command() --jsonglobal flag — machine-readable JSON output--quietglobal flag — suppress informational messages- TTY detection — automatic format selection
- Semantic exit codes — 0 (OK), 1 (error), 4 (not found), 5 (unauthorized), 7 (validation), 64 (usage)
- Built on
cli_router— GNU flags, middleware, modular mounting
Installation #
dart pub add modular_cli_sdk
Or add it manually to pubspec.yaml:
dependencies:
modular_cli_sdk: ^0.2.0
dart pub add modular_cli_sdk
Error handling #
@override
Future<MyOutput> execute() async {
final ticket = await repository.findById(input.ticketId);
if (ticket == null) {
throw CommandException(
code: 'TICKET_NOT_FOUND',
message: 'Ticket #${input.ticketId} not found',
exitCode: ExitCode.notFound,
);
}
return ShowTicketOutput(ticket: ticket);
}
Error: Ticket #42 not found [TICKET_NOT_FOUND]
With --json:
{"error": "TICKET_NOT_FOUND", "message": "Ticket #42 not found", "exitCode": 4, "isRetryable": false}
Architecture #
dart:io / Process — I/O primitive
↓
cli_router — routing engine (routes, GNU flags, middleware)
↓
modular_cli_sdk — SDK/framework
↓
ModularCli → Module → Command → Business Logic → Output → formatted terminal output
- Command layer — pure logic, independent of output format
- Output adapter — turns Output into JSON or plain text based on flags/TTY
- Middleware — cross-cutting concerns (logging, auth, metrics)
Documentation #
- API reference — generated dartdoc on pub.dev
- doc/architecture.md — architecture overview and symmetry with modular_api
- doc/roadmap.md — planned features for upcoming releases
- AGENTS.md — framework guide (AI-optimized)
Compile to executable #
dart compile exe bin/main.dart -o build/my-cli
The compiled binary includes the Dart runtime and runs without the SDK installed.
License #
MIT © ccisne.dev