bctx-forge 0.1.14

bctx-forge — OS execution substrate, signal capture, BPE tokenizer, SQLite tracker
Documentation
use super::{Substrate, SubstrateCommand, SubstrateError, SubstrateResult};
use std::process::Command;
use std::time::Instant;

pub struct LocalSubstrate;

impl Substrate for LocalSubstrate {
    fn execute(&self, cmd: SubstrateCommand) -> Result<SubstrateResult, SubstrateError> {
        let start = Instant::now();

        let mut builder = Command::new(&cmd.program);
        builder.args(&cmd.args);

        if let Some(ref cwd) = cmd.working_dir {
            builder.current_dir(cwd);
        }

        // Ensure standard PATH is available even when launched as an MCP server
        let inherited_path = std::env::var("PATH").unwrap_or_default();
        let augmented = if inherited_path.is_empty() {
            "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin".to_string()
        } else {
            format!("/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:{inherited_path}")
        };
        builder.env("PATH", augmented);

        for (k, v) in &cmd.env {
            builder.env(k, v);
        }

        let output = builder.output().map_err(|e| {
            if e.kind() == std::io::ErrorKind::NotFound {
                SubstrateError::NotFound(cmd.program.clone())
            } else {
                SubstrateError::Io(e)
            }
        })?;

        let duration_ms = start.elapsed().as_millis() as u64;

        Ok(SubstrateResult {
            stdout: output.stdout,
            stderr: output.stderr,
            exit_code: output.status.code().unwrap_or(-1),
            duration_ms,
        })
    }

    fn is_available(&self) -> bool {
        true
    }
}