use crate::{
config::{Config, DEFAULT_RESUME_COMMAND},
session::Session,
};
use anyhow::Context;
use std::process::Command;
pub fn command(cfg: &Config, session: &Session) -> String {
cfg.resume_command
.replace("{directory}", "e(&session.directory.to_string_lossy()))
.replace("{session_id}", "e(&session.id))
}
pub fn run(cfg: &Config, session: &Session) -> anyhow::Result<()> {
if cfg.resume_command == DEFAULT_RESUME_COMMAND {
let status = Command::new("opencode")
.arg(&session.directory)
.arg("--session")
.arg(&session.id)
.status()
.context("run opencode")?;
if !status.success() {
anyhow::bail!("opencode exited with {status}");
}
return Ok(());
}
let cmd = command(cfg, session);
let status = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(["/C", &cmd])
.status()
.with_context(|| cmd.clone())?
} else {
Command::new("sh")
.args(["-lc", &cmd])
.status()
.with_context(|| cmd.clone())?
};
if !status.success() {
anyhow::bail!("resume command exited with {status}: {cmd}");
}
Ok(())
}
fn quote(value: &str) -> String {
if cfg!(target_os = "windows") {
return format!("\"{}\"", value.replace('"', "\\\""));
}
format!("'{}'", value.replace('\'', "'\\''"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::session::Session;
use std::path::PathBuf;
#[test]
fn command_shell_quotes_placeholders() {
let session = Session {
id: "ses_1".into(),
title: "title".into(),
directory: PathBuf::from("/tmp/a b's"),
path: None,
agent: None,
model: None,
project: None,
worktree: None,
updated: 0,
created: 0,
db: PathBuf::from("db"),
preview: None,
};
let command = command(&Config::default(), &session);
if cfg!(target_os = "windows") {
assert!(command.contains("\"/tmp/a b's\""));
} else {
assert!(command.contains("'/tmp/a b'\\''s'"));
}
}
}