Library API (Rust)

shuck is also available as a Rust library (shuck on crates.io). Embed PTY-based command execution directly in your Rust programs — ideal for building agent runtimes, automation frameworks, and custom tooling.

Add to Cargo.toml

[dependencies]
shuck = "0.1"

Basic Usage

use shuck::{run, Config};

fn main() -> Result<(), shuck::Error> {
    let result = run(Config {
        command: "python3".into(),
        args: vec!["-c".into(), "print('hello from shuck')".into()],
        ..Config::default()
    })?;

    println!("stdout: {}", result.stdout);
    println!("exit_code: {}", result.exit_code);
    println!("duration_ms: {}", result.duration_ms);
    Ok(())
}

Config Struct

pub struct Config {
    /// The command to run
    pub command: String,
    
    /// Arguments to pass to the command
    pub args: Vec<String>,
    
    /// Input to feed to stdin
    pub input: Option<String>,
    
    /// Timeout in seconds (None = no timeout)
    pub timeout_secs: Option<u64>,
    
    /// Don't strip ANSI sequences
    pub raw: bool,
    
    /// PTY column width (default: 80)
    pub cols: u16,
    
    /// PTY row count (default: 24)
    pub rows: u16,
    
    /// Extra environment variables
    pub env: Vec<(String, String)>,
    
    /// Suppress stderr from child
    pub quiet: bool,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            command: String::new(),
            args: vec![],
            input: None,
            timeout_secs: Some(30),
            raw: false,
            cols: 80,
            rows: 24,
            env: vec![],
            quiet: false,
        }
    }
}

Result Struct

pub struct ShuckResult {
    /// Captured stdout (ANSI-stripped unless raw=true)
    pub stdout: String,
    
    /// Captured stderr from child
    pub stderr: String,
    
    /// Child exit code (or shuck error code)
    pub exit_code: i32,
    
    /// Wall-clock execution time
    pub duration_ms: u64,
    
    /// Whether the timeout was reached
    pub timed_out: bool,
}

Error Type

pub enum Error {
    /// Command not found in PATH
    CommandNotFound(String),
    
    /// Command found but not executable
    NotExecutable(String),
    
    /// PTY allocation failed
    PtyAllocation(std::io::Error),
    
    /// I/O error during execution
    Io(std::io::Error),
    
    /// Child process error
    Process(std::io::Error),
}

Examples

With Input

let result = run(Config {
    command: "python3".into(),
    input: Some("print(2 + 2)".into()),
    ..Config::default()
})?;
assert_eq!(result.stdout.trim(), "4");

With Timeout

let result = run(Config {
    command: "sleep".into(),
    args: vec!["10".into()],
    timeout_secs: Some(1),
    ..Config::default()
})?;
assert_eq!(result.exit_code, 124);
assert!(result.timed_out);

Using the Stripper Directly

use shuck::strip;

let raw = "\x1b[31mred text\x1b[0m\nnormal text";
let clean = strip(raw);
assert_eq!(clean, "red text\nnormal text");

Async Usage

shuck’s run is synchronous. For async contexts, use tokio::task::spawn_blocking:

use tokio::task;

let result = task::spawn_blocking(|| {
    shuck::run(Config {
        command: "my-tool".into(),
        ..Config::default()
    })
}).await??;

Embedding in Agent Runtimes

The library API is designed for embedding shuck in agent runtimes. Use it to give your agent framework direct access to interactive CLI tools:

use shuck::{run, Config};

/// Agent tool: run an interactive CLI and return structured output.
fn agent_run_cli(
    command: &str,
    args: &[&str],
    input: Option<&str>,
    timeout: u64,
) -> Result<shuck::ShuckResult, shuck::Error> {
    run(Config {
        command: command.into(),
        args: args.iter().map(|s| s.to_string()).collect(),
        input: input.map(|s| s.to_string()),
        timeout_secs: Some(timeout),
        ..Config::default()
    })
}

// Agent checks database schema
let schema = agent_run_cli("psql", &["-U", "postgres", "mydb"], Some("\\dt"), 30)?;

// Agent checks package health
let health = agent_run_cli("pip", &["check"], None, 30)?;

// Agent inspects container
let status = agent_run_cli("docker", &["compose", "ps"], None, 15)?;

Source

github.com/srinitude/shuck docs.rs/shuck