use crate::command::args::PromptArgs;
use crate::config::MuxMode;
use crate::multiplexer::{create_backend, detect_backend};
use crate::workflow::prompt_loader::{PromptLoadArgs, load_prompt};
use crate::workflow::{SetupOptions, WorkflowContext};
use crate::{config, workflow};
use anyhow::{Context, Result, bail};
pub fn run(
names: &[String],
run_hooks: bool,
force_files: bool,
new_window: bool,
session: bool,
continue_session: bool,
prompt_args: PromptArgs,
) -> Result<()> {
let resolved_names: Vec<String> = if names.is_empty() {
if new_window {
let inferred = super::resolve_name(None).context(
"Could not infer current worktree. Run inside a worktree or provide a name.",
)?;
vec![inferred]
} else {
bail!("Worktree name is required unless --new is provided")
}
} else {
names.to_vec()
};
if resolved_names.len() > 1 && prompt_args.has_any() {
bail!("Prompt arguments (-p, -P, -e) cannot be used when opening multiple worktrees");
}
let (config, config_location) = config::Config::load_with_location(None)?;
let mux = create_backend(detect_backend());
let context = WorkflowContext::new(config, mux, config_location)?;
if session && context.mux.name() != "tmux" {
bail!(
"Session mode (--session) is only supported with tmux.\n\
Current backend: {}. Use window mode instead.",
context.mux.name()
);
}
let preliminary_mode = if session {
MuxMode::Session
} else {
context.config.mode()
};
let prompt = load_prompt(&PromptLoadArgs {
prompt_editor: prompt_args.prompt_editor,
prompt_inline: prompt_args.prompt.as_deref(),
prompt_file: prompt_args.prompt_file.as_ref(),
})?;
let prompt_file_only =
prompt_args.prompt_file_only || context.config.prompt_file_only.unwrap_or(false);
let mut errors: Vec<(String, anyhow::Error)> = Vec::new();
for resolved_name in &resolved_names {
let prompt_file_path = if let Some(ref p) = prompt {
if prompt_file_only {
None
} else {
let unique_name = format!(
"{}-{}",
resolved_name,
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
);
Some(crate::workflow::write_prompt_file(None, &unique_name, p)?)
}
} else {
None
};
let mut options = SetupOptions::new(run_hooks, force_files, true);
options.mode = preliminary_mode;
options.prompt_file_path = prompt_file_path;
options.continue_session = continue_session;
if new_window {
super::announce_hooks(
&context.config,
Some(&options),
super::HookPhase::PostCreate,
);
}
let file_only_prompt = if prompt_file_only {
prompt.as_ref()
} else {
None
};
match workflow::open(
resolved_name,
&context,
options,
new_window,
session,
file_only_prompt,
)
.context("Failed to open worktree environment")
{
Ok(result) => {
let target_type = match result.mode {
MuxMode::Session => "session",
MuxMode::Window => "window",
};
if result.did_switch {
println!(
"✓ Switched to existing tmux {} for '{}'\n Worktree: {}",
target_type,
resolved_name,
result.worktree_path.display()
);
} else {
if result.post_create_hooks_run > 0 {
println!("✓ Setup complete");
}
println!(
"✓ Opened tmux {} for '{}'\n Worktree: {}",
target_type,
resolved_name,
result.worktree_path.display()
);
}
}
Err(e) => {
eprintln!("✗ Failed to open '{}': {:#}", resolved_name, e);
errors.push((resolved_name.clone(), e));
}
}
}
if errors.is_empty() {
Ok(())
} else if errors.len() == resolved_names.len() {
bail!("Failed to open all {} worktrees", errors.len())
} else {
bail!(
"Failed to open {} of {} worktrees",
errors.len(),
resolved_names.len()
)
}
}