Skip to main content

Crate scriptty

Crate scriptty 

Source
Expand description

§Scriptty

A PTY scripting engine for automating interactive terminal sessions.

Scriptty lets you write simple scripts that drive any interactive terminal program with precise control over input timing and output presentation. It is useful for testing CLI tools, building reproducible terminal walkthroughs, and constructing terminal automation pipelines.

§Quick start

use scriptty::{Engine, parse_str};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let script = r#"
expect "$ "
type "echo hello"
key Enter
expect "hello"
send "exit"
key Enter
"#;

    let commands = parse_str(script)?;
    let mut engine = Engine::spawn("bash", &[] as &[&str])?;
    engine.execute(commands).await?;
    Ok(())
}

§Parsing scripts

Use parse_str to parse a script from an in-memory string, or parse_file to read one from a file path. Both return a Vec<Box<dyn ScripttyCommand>> ready to pass to Engine::execute.

§Script syntax

CommandDescription
type "text"Simulate typing with per-character delays
send "text"Send text to the program immediately (no typing simulation)
key EnterSend a key press (supports Ctrl+, Alt+, Shift+ modifiers)
show "text"Write text directly to the output handler
expect "pattern"Wait until pattern appears in the program output
expect "pattern" 5sWait up to 5 seconds for the pattern
wait 500msPause for a duration (ms or s units, floats allowed)
# commentFull-line or inline comment

§Custom output handling

By default Engine::spawn writes all output to stdout. Use Engine::spawn_with_handler to redirect output to any sink:

use scriptty::{Engine, parse_str};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let commands = parse_str(r#"type "hello""#)?;

    let captured = std::sync::Arc::new(std::sync::Mutex::new(Vec::<u8>::new()));
    let sink = captured.clone();

    let mut engine = Engine::spawn_with_handler("bash", &[] as &[&str], move |data| {
        sink.lock().unwrap().extend_from_slice(data);
    })?;

    engine.execute(commands).await?;
    println!("{}", String::from_utf8_lossy(&captured.lock().unwrap()));
    Ok(())
}

§Implementing a custom command

Implement ScripttyCommand to add new commands to the engine:

use scriptty::command::{Context, ScripttyCommand};
use async_trait::async_trait;
use anyhow::Result;

pub struct Beep;

impl Beep {
    pub const NAME: &'static str = "beep";
}

#[async_trait(?Send)]
impl ScripttyCommand for Beep {
    fn name(&self) -> &'static str { Self::NAME }

    fn parse(_args: &str) -> Result<Self> {
        Ok(Self)
    }

    async fn execute(&self, ctx: &mut Context) -> Result<()> {
        ctx.emit(b"\x07"); // BEL character
        Ok(())
    }
}

Re-exports§

pub use command::Context;
pub use command::ScripttyCommand;
pub use commands::Expect;
pub use commands::KeyPress;
pub use commands::SendInput;
pub use commands::Show;
pub use commands::TypeText;
pub use commands::Wait;
pub use engine::Engine;
pub use parser::parse_file;
pub use parser::parse_str;

Modules§

command
The ScripttyCommand trait and the Context type commands receive when executed.
commands
engine
The Engine that executes ScripttyCommand sequences against a live PTY process.
parser
Script parser for the scriptty scripting language.