ModCLI is a lightweight, modular CLI framework for Rust. A fully customizable and feature-rich system for registering commands, styling output, and running interactive shells with zero bloat. Built to speed up CLI development and get powerful tools running fast—without rewriting the basics.
Add the library to your Cargo.toml
:
[dependencies]
mod-cli = "0.5.0"
Add the library to your Cargo.toml
with features:
[dependencies]
mod-cli = { version = "0.5.0", features = ["plugins"] }
Feature | Description |
---|---|
internal-commands |
Enables built-in test/dev commands like ping , hello |
custom-commands |
Enables CLI custom command creation. |
json-loader |
Enables external command loading from JSON config |
plugins |
Enables plugin support for dynamic runtime command injection |
use modcli::ModCli;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
cli.run(args);
}
use modcli::ModCli;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
// Set a custom prefix
cli.set_prefix("myCLI");
cli.run(args);
}
let teal = colors::get("teal"); // always returns a Color (or fallback)
let demo = build()
.part("Color Demo:").space()
.part("Teal").color(teal).bold().get();
print::line(&demo, 0);
use modcli::output::{
gradient,
print,
RED, ORANGE
};
let gradient_text = gradient::two_color("Two color gradient", RED, ORANGE);
print::line(&gradient_text);
use modcli::output::{
gradient,
print,
BLUE, GREEN, YELLOW
};
let gradient_text = gradient::three_color("Three color gradient", BLUE, GREEN, YELLOW);
print::line(&gradient_text);
use modcli::output::{
gradient,
print,
RED, ORANGE, YELLOW, GREEN, BLUE
};
let gradient_text = gradient::multi_color("Multi-color gradient", vec![RED, ORANGE, YELLOW, GREEN, BLUE]);
print::line(&gradient_text);
use modcli::output::{
gradient,
print
};
let gradient_text = gradient::two_color(
"Gradient Output",
Color::Rgb { r: 255, g: 0, b: 0 },
Color::Rgb { r: 0, g: 0, b: 255 },
);
print::line(&gradient_text);
use modcli::output::{
print,
build,
BLUE
};
// 📦 Progress Bar Demo
let testing = build()
.part("Testing")
.color(BLUE)
.bold()
.get();
print::line(&testing);
// Outputs "Testing" in bold/blue.
use modcli::output::{
gradient,
print,
build,
BLUE, LIGHT_BLUE
};
// 📦 Progress Bar Demo
let testing = build()
.part("Label:").color(BLUE).bold().space()
.part("This content has").space()
.part("multiple").color(LIGHT_BLUE).bold().space()
.part("styles").underline().space()
.part("and").italic().space()
.part("colors").underline().space()
.part("!")
.get();
print::line(&testing);
use modcli::output::{
print,
build,
BLUE, GREEN
};
let gradient_text = gradient::two_color("Gradient Output", BLUE, GREEN);
let testing = build()
.part(&gradient_text).bold().space()
.part("+ Styled!")
.get();
print::line(&testing);
use modcli::output::{
progress::{
show_progress_bar,
}
};
show_progress_bar("Testing", 45, 1500);
Displays
Label [#############################################] 100% Done!
use modcli::output::{
build,
progress::{
ProgressBar,
ProgressStyle,
},
LIGHT_BLUE
};
// Progress Bar Demo
let label = build()
.part("Loading")
.color(LIGHT_BLUE)
.bold()
.get();
let mut bar = ProgressBar::new(30, ProgressStyle {
fill: '■',
done_label: "Complete!",
color: Some(LIGHT_BLUE),
..Default::default()
});
bar.set_label(&label);
bar.start_auto(2000); // auto-fill in 2 seconds
use std::time::Duration;
use modcli::output::{
progress::{
ProgressBar,
ProgressStyle
},
ORANGE
};
use modcli::output::input::console::run_interactive_console;
let mut bar = ProgressBar::new(10, ProgressStyle {
fill: '■',
done_label: "Done!",
color: Some(ORANGE),
..Default::default()
});
bar.set_label("Syncing");
for _ in 0..10 {
bar.tick();
std::thread::sleep(Duration::from_millis(200));
}
println!(" {}", bar.style.done_label);
use modcli::output::{
progress::{
show_spinner
}
};
show_spinner("Loading", 20, 100);
use std::thread::sleep;
use std::time::Duration;
use modcli::output::{
progress::{
show_percent_progress
}
};
for i in (0..=100).step_by(10) {
show_percent_progress("Loading", i);
sleep(Duration::from_millis(100));
}
println!();
use crate::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Flex, TableStyle::Heavy);
Outputs
┏━━━━━━━━┳━━━━━━━━┳━━━━━━━━┓
┃Name ┃Age ┃Role ┃
┣━━━━━━━━╋━━━━━━━━╋━━━━━━━━┫
┃Alice ┃29 ┃Engineer┃
┃Bob ┃35 ┃Manager ┃
┃Charlie ┃41 ┃CTO ┃
┗━━━━━━━━┻━━━━━━━━┻━━━━━━━━┛
use crate::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Fixed(15), TableStyle::Rounded);
Outputs
╭───────────────┬───────────────┬───────────────╮
│Name │Age │Role │
├───────────────┼───────────────┼───────────────┤
│Alice │29 │Engineer │
│Bob │35 │Manager │
│Charlie │41 │CTO │
╰───────────────┴───────────────┴───────────────╯
use crate::output::table::{render_table, TableMode, TableStyle};
let headers = ["Name", "Age", "Role"];
let rows = vec![
vec!["Alice", "29", "Engineer"],
vec!["Bob", "35", "Manager"],
vec!["Charlie", "41", "CTO"],
];
render_table(&headers, &rows, TableMode::Fixed(15), TableStyle::Ascii);
Outputs
+---------------+---------------+---------------+
|Name |Age |Role |
+---------------+---------------+---------------+
|Alice |29 |Engineer |
|Bob |35 |Manager |
|Charlie |41 |CTO |
+---------------+---------------+---------------+
my_project/
├── src/
│ ├── commands/
│ │ └── greet.rs ← define `GreetCommand` here
Create a commands folder in src/, then put the command in its own file:
use modcli::command::Command;
pub struct GreetCommand;
impl Command for GreetCommand {
fn name(&self) -> &str {
"greet"
}
fn aliases(&self) -> &[&str] {
&["hi"]
}
fn help(&self) -> Option<&str> {
Some("Greets the user.")
}
fn validate(&self, _args: &[String]) -> Result<(), String> {
Ok(())
}
fn execute(&self, _args: &[String]) {
println!("Greetings!");
}
}
greet.rs
mod commands;
use modcli::ModCli;
use commands::greet::GreetCommand;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut cli = ModCli::new();
// Register function
cli.registry.register(Box::new(GreetCommand));
cli.run(args);
}
$ myCLI greet
Greetings!
$ myCLI help
List of available commands...
use modcli::config::CliConfig;
use modcli::console::run_shell;
fn main() {
let config = CliConfig::load(None);
run_shell(&config);
}
use modcli::ModCli;
use modcli::shell_commands::{register, ShellCommand};
fn greet_handler(_input: &str) -> bool {
println!("👋 Hello from shell command!");
true
}
fn main() {
register(ShellCommand {
name: "greet",
aliases: &["hi", "wave"],
help: "Greets the user with a friendly hello",
handler: greet_handler,
});
}
{
"modcli": {
"name" : "mod-cli",
"prefix": "mod",
"banner": "Welcome to ModCLI",
"delay" : 0,
"theme" : "default",
"strict": false,
"force_shell": false,
"shell": {
"prompt": "Tool >",
"welcome": ["Welcome to the console."],
"goodbye": ["Bye!"]
},
"messages": {
"no_command": "No command provided.",
"not_found": "Command not found."
}
}
}
Default location:
project_root/config.json
use modcli::config;
fn main() {
config::set_path(("my/custom/config.json");
Warning
Pre-release: This project is in active development. The core is stable but features are evolving. Production use is possible, but interfaces may still evolve until 1.0.
Licensed under the Apache License, version 2.0 (the "License"); you may not use this software, including, but not limited to the source code, media files, ideas, techniques, or any other associated property or concept belonging to, associated with, or otherwise packaged with this software except in compliance with the License.
You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the LICENSE file included with this project for the specific language governing permissions and limitations under the License.
COPYRIGHT © 2025 JAMES GOBER.