use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub struct SessionOptions {
pub resume: Option<String>,
pub continue_conversation: bool,
pub fork_session: bool,
pub session_id: Option<String>,
pub model: Option<String>,
pub fallback_model: Option<String>,
pub max_budget_usd: Option<f64>,
pub max_turns: Option<u64>,
pub system_prompt: Option<String>,
pub append_system_prompt: Option<String>,
pub tools: Vec<String>,
pub allowed_tools: Vec<String>,
pub disallowed_tools: Vec<String>,
pub permission_mode: Option<PermissionMode>,
pub dangerously_skip_permissions: bool,
pub mcp_config: Vec<String>,
pub strict_mcp_config: bool,
pub working_dir: Option<PathBuf>,
pub add_dirs: Vec<PathBuf>,
pub include_partial_messages: bool,
pub effort: Option<String>,
pub no_session_persistence: bool,
pub json_schema: Option<String>,
pub agents: Option<String>,
pub settings: Option<String>,
pub setting_sources: Vec<String>,
pub env_vars: Vec<(String, String)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PermissionMode {
Default,
AcceptEdits,
Plan,
BypassPermissions,
DontAsk,
}
impl PermissionMode {
pub fn as_str(&self) -> &'static str {
match self {
Self::Default => "default",
Self::AcceptEdits => "acceptEdits",
Self::Plan => "plan",
Self::BypassPermissions => "bypassPermissions",
Self::DontAsk => "dontAsk",
}
}
}
impl SessionOptions {
pub fn to_cli_args(&self) -> Vec<String> {
let mut args = Vec::new();
if let Some(ref id) = self.resume {
args.extend(["--resume".to_owned(), id.clone()]);
}
if self.continue_conversation {
args.push("--continue".to_owned());
}
if self.fork_session {
args.push("--fork-session".to_owned());
}
if let Some(ref id) = self.session_id {
args.extend(["--session-id".to_owned(), id.clone()]);
}
if let Some(ref model) = self.model {
args.extend(["--model".to_owned(), model.clone()]);
}
if let Some(ref model) = self.fallback_model {
args.extend(["--fallback-model".to_owned(), model.clone()]);
}
if let Some(budget) = self.max_budget_usd {
args.extend(["--max-budget-usd".to_owned(), budget.to_string()]);
}
if let Some(turns) = self.max_turns {
args.extend(["--max-turns".to_owned(), turns.to_string()]);
}
if let Some(ref prompt) = self.system_prompt {
args.extend(["--system-prompt".to_owned(), prompt.clone()]);
}
if let Some(ref prompt) = self.append_system_prompt {
args.extend(["--append-system-prompt".to_owned(), prompt.clone()]);
}
if !self.tools.is_empty() {
args.extend(["--tools".to_owned(), self.tools.join(",")]);
}
if !self.allowed_tools.is_empty() {
args.push("--allowedTools".to_owned());
for tool in &self.allowed_tools {
args.push(tool.clone());
}
}
if !self.disallowed_tools.is_empty() {
args.push("--disallowedTools".to_owned());
for tool in &self.disallowed_tools {
args.push(tool.clone());
}
}
if let Some(mode) = self.permission_mode {
args.extend(["--permission-mode".to_owned(), mode.as_str().to_owned()]);
}
if self.dangerously_skip_permissions {
args.push("--dangerously-skip-permissions".to_owned());
}
if !self.mcp_config.is_empty() {
args.push("--mcp-config".to_owned());
for cfg in &self.mcp_config {
args.push(cfg.clone());
}
}
if self.strict_mcp_config {
args.push("--strict-mcp-config".to_owned());
}
if !self.add_dirs.is_empty() {
args.push("--add-dir".to_owned());
for dir in &self.add_dirs {
args.push(dir.display().to_string());
}
}
if self.include_partial_messages {
args.push("--include-partial-messages".to_owned());
}
if let Some(ref effort) = self.effort {
args.extend(["--effort".to_owned(), effort.clone()]);
}
if self.no_session_persistence {
args.push("--no-session-persistence".to_owned());
}
if let Some(ref schema) = self.json_schema {
args.extend(["--json-schema".to_owned(), schema.clone()]);
}
if let Some(ref agents) = self.agents {
args.extend(["--agents".to_owned(), agents.clone()]);
}
if let Some(ref settings) = self.settings {
args.extend(["--settings".to_owned(), settings.clone()]);
}
if !self.setting_sources.is_empty() {
args.extend([
"--setting-sources".to_owned(),
self.setting_sources.join(","),
]);
}
args
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_options_produce_no_args() {
let opts = SessionOptions::default();
assert!(opts.to_cli_args().is_empty());
}
#[test]
fn model_and_tools_args() {
let opts = SessionOptions {
model: Some("sonnet".to_owned()),
allowed_tools: vec!["Bash".to_owned(), "Read".to_owned()],
max_turns: Some(5),
..Default::default()
};
let args = opts.to_cli_args();
assert!(args.contains(&"--model".to_owned()));
assert!(args.contains(&"sonnet".to_owned()));
assert!(args.contains(&"--allowedTools".to_owned()));
assert!(args.contains(&"Bash".to_owned()));
assert!(args.contains(&"Read".to_owned()));
assert!(args.contains(&"--max-turns".to_owned()));
assert!(args.contains(&"5".to_owned()));
}
#[test]
fn resume_and_continue_flags() {
let opts = SessionOptions {
resume: Some("abc-123".to_owned()),
fork_session: true,
..Default::default()
};
let args = opts.to_cli_args();
assert!(args.contains(&"--resume".to_owned()));
assert!(args.contains(&"abc-123".to_owned()));
assert!(args.contains(&"--fork-session".to_owned()));
}
#[test]
fn permission_mode_flag() {
let opts = SessionOptions {
permission_mode: Some(PermissionMode::AcceptEdits),
..Default::default()
};
let args = opts.to_cli_args();
assert!(args.contains(&"--permission-mode".to_owned()));
assert!(args.contains(&"acceptEdits".to_owned()));
}
}