CLI Architecture
This document describes the architecture of the Radium CLI (rad), including its command structure, execution model, and core patterns.
Overviewβ
The Radium CLI is a comprehensive command-line interface built in Rust using:
- Clap for argument parsing and command structure
- Tokio for async execution
- anyhow for error handling
- colored for terminal output formatting
The CLI provides 30+ command modules organized into logical categories for workspace management, plan execution, agent orchestration, and advanced features.
Command Structureβ
Entry Pointβ
The CLI entry point is apps/cli/src/main.rs, which:
- Parses arguments using Clap's derive macros
- Loads configuration from multiple sources (CLI args, env vars, config files)
- Routes commands to their respective implementations
- Handles errors and provides user feedback
Command Routingβ
Commands are routed through a large match statement in main.rs (lines 459-556). Each command variant maps to an async execution function in its respective module:
match command {
Command::Init { path, use_defaults, ... } => {
init::execute(path, use_defaults, ...).await?;
}
Command::Plan { input, id, name } => {
plan::execute(input, id, name).await?;
}
// ... 30+ more commands
}
Command Module Organizationβ
All command implementations live in apps/cli/src/commands/:
- Core commands:
init.rs,status.rs,clean.rs,doctor.rs - Plan commands:
plan.rs,craft.rs,complete.rs - Agent commands:
agents.rs,step.rs,run.rs,chat.rs - Management:
templates.rs,engines.rs,auth.rs - Advanced:
autonomous.rs,vibecheck.rs,mcp.rs,learning.rs - Infrastructure:
checkpoint.rs,policy.rs,constitution.rs,context.rs - Monitoring:
monitor.rs,stats.rs,budget.rs - Development:
sandbox.rs,custom.rs,extension.rs,hooks.rs,validate.rs
Commands are registered in apps/cli/src/commands/mod.rs and exported for use in main.rs.
Execution Modelβ
Async Architectureβ
All commands are async functions that return anyhow::Result<()>. The CLI uses Tokio's async runtime for I/O-bound operations:
- File system operations
- Network requests (AI model APIs)
- Process execution
- Workspace discovery
Command Signature Patternβ
Commands follow a consistent signature pattern:
pub async fn execute(
// Required arguments
arg1: Type1,
arg2: Type2,
// Optional flags
json: bool,
verbose: bool,
// ... other options
) -> anyhow::Result<()>
Error Handlingβ
Commands use anyhow::Context for error propagation:
let workspace = Workspace::discover()
.context("Failed to discover workspace")?;
Errors are automatically formatted with context chains, providing helpful debugging information.
Common Patternsβ
Workspace Discoveryβ
Most commands need to discover the current workspace:
let workspace = Workspace::discover()
.context("Failed to discover workspace")?;
The Workspace::discover() function searches upward from the current directory for a .radium folder, following the same pattern as Git.
Model Selectionβ
Commands that interact with AI models use ModelSelector:
use radium_models::ModelSelector;
let selector = ModelSelector::new()
.with_override(engine_override)
.with_model_override(model_override);
let model = selector.select().await?;
This provides flexible model selection with CLI argument overrides, configuration file defaults, and environment variable fallbacks.
Output Formattingβ
Commands support both human-readable and JSON output:
pub async fn execute(json_output: bool) -> anyhow::Result<()> {
if json_output {
execute_json().await
} else {
execute_human().await
}
}
Human-readable output uses the colored crate for terminal formatting:
- Success: green checkmarks (β)
- Errors: red text
- Info: cyan/bold headers
- Warnings: yellow text
JSON output uses serde_json for structured data:
let output = json!({
"status": "success",
"data": { /* ... */ }
});
println!("{}", serde_json::to_string_pretty(&output)?);
Configuration Loadingβ
The CLI loads configuration from multiple sources with precedence:
- CLI arguments (highest priority)
- Environment variables
- Local config file (
./.radiumrc) - Global config file (
~/.radium/config.toml) - Defaults (lowest priority)
Configuration is loaded in main.rs before command execution:
let cli_config = config::load_config();
unsafe {
config::apply_config_to_env(&cli_config);
}
Note: The unsafe block is used because environment variables are set before async spawning. This is safe as long as it only happens in single-threaded code before any async operations.
Command Categoriesβ
Core Workflow Commandsβ
init- Initialize workspacestatus- Show workspace statusclean- Clean workspace artifactsdoctor- Environment diagnostics
Plan Generation and Executionβ
plan- Generate plans from specificationscraft- Execute planscomplete- End-to-end workflow (fetch, plan, execute)
Agent Managementβ
agents- List, search, validate, create agentstemplates- Manage workflow templatesstep- Execute single agentrun- Execute agent scriptchat- Interactive chat session
Advanced Featuresβ
autonomous- Autonomous goal executionvibecheck- Metacognitive oversightmcp- Model Context Protocol managementlearning- Learning system operationscheckpoint- Workspace state snapshotspolicy- Policy managementconstitution- Constitutional AI rulescontext- Context managementsandbox- Isolated command executioncustom- User-defined commandsextension- Extension managementhooks- Lifecycle hooksengines- AI model configurationbudget- Cost and token usage trackingvalidate- Configuration validation
Monitoring and Analyticsβ
monitor- Real-time monitoringstats- Statistics and analytics
Shell Completionβ
The CLI supports shell completion generation via the RADIUM_GENERATE_COMPLETIONS environment variable:
RADIUM_GENERATE_COMPLETIONS=bash rad > radium.bash
Supported shells:
- Bash
- Zsh
- Fish
- PowerShell
- Elvish
Completion generation uses clap_complete to generate completions from the Clap command structure.
Testingβ
See Testing Patterns for detailed information about CLI testing.
Extension Pointsβ
The CLI is designed for extensibility:
- Custom Commands - Users can define custom commands via
rad custom - Extensions - Plugin system for adding commands, hooks, and MCP servers
- Hooks - Lifecycle hooks for workspace events
- MCP Integration - Model Context Protocol for tool integration
Performance Considerationsβ
- Commands use async I/O for non-blocking operations
- Workspace discovery is cached where possible
- Model selection is lazy (only when needed)
- JSON output is streamed for large datasets
Security Considerationsβ
- Credentials stored in
~/.radium/auth/credentials.jsonwith 0600 permissions - Environment variable access is limited to single-threaded initialization
- Input validation prevents path traversal
- Command injection prevention for shell execution
See Security Documentation for more details.