diff --git a/images/base/Dockerfile b/images/base/Dockerfile index a6f7cb6..1997e2c 100644 --- a/images/base/Dockerfile +++ b/images/base/Dockerfile @@ -21,8 +21,8 @@ ENV LC_ALL=C.UTF-8 RUN dnf install -y --setopt=install_weak_deps=False \ # Core utilities git curl wget jq openssh-clients ca-certificates tar gzip unzip which \ - # Shell - zsh \ + # Shell & terminal multiplexer + zsh tmux \ # Networking & capabilities procps-ng httpie libcap \ # Build essentials for native modules @@ -304,6 +304,29 @@ export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" ZSHRC +# Tmux Plugin Manager + config +RUN git clone --depth=1 https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm \ + && git clone --depth=1 https://github.com/tmux-plugins/tmux-resurrect ~/.tmux/plugins/tmux-resurrect \ + && git clone --depth=1 https://github.com/tmux-plugins/tmux-continuum ~/.tmux/plugins/tmux-continuum +RUN cat > ~/.tmux.conf << 'TMUXCONF' +bind -n C-k clear-history + +set -g mouse on + +setw -g mode-keys vi + +# List of plugins +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# Automatic restore when tmux starts +set -g @continuum-restore 'on' + +# Initialize TMUX plugin manager (keep this line at the very bottom) +run '~/.tmux/plugins/tpm/tpm' +TMUXCONF + # Claude Code CLI (native installer, auto-updates) RUN curl -fsSL https://claude.ai/install.sh | bash @@ -312,6 +335,8 @@ USER root RUN cp -a /home/developer/.oh-my-zsh /etc/skel/.oh-my-zsh \ && cp -a /home/developer/.nvm /etc/skel/.nvm \ && cp -a /home/developer/.claude /etc/skel/.claude \ + && cp -a /home/developer/.tmux /etc/skel/.tmux \ + && cp /home/developer/.tmux.conf /etc/skel/.tmux.conf \ && cp /home/developer/.zshrc /etc/skel/.zshrc \ && touch /etc/skel/.zsh_history diff --git a/src/cli/args.rs b/src/cli/args.rs index 14da3fb..4dbd6b3 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -216,6 +216,14 @@ pub struct RunArgs { pub command: Vec, } +/// Strip bare `--` separators that clap's `last = true` leaks into the command vec. +/// This happens when the user runs `mino run --` without a trailing command. +pub fn strip_separator(command: &mut Vec) { + if command.len() == 1 && command[0] == "--" { + command.clear(); + } +} + /// Arguments for the list command #[derive(Parser, Debug)] pub struct ListArgs { @@ -769,4 +777,32 @@ mod tests { _ => panic!("expected Completions command"), } } + + #[test] + fn strip_separator_removes_bare_double_dash() { + let mut cmd = vec!["--".to_string()]; + strip_separator(&mut cmd); + assert!(cmd.is_empty()); + } + + #[test] + fn strip_separator_preserves_real_commands() { + let mut cmd = vec!["echo".to_string(), "hello".to_string()]; + strip_separator(&mut cmd); + assert_eq!(cmd, vec!["echo", "hello"]); + } + + #[test] + fn strip_separator_handles_empty_vec() { + let mut cmd: Vec = vec![]; + strip_separator(&mut cmd); + assert!(cmd.is_empty()); + } + + #[test] + fn strip_separator_preserves_double_dash_in_args() { + let mut cmd = vec!["echo".to_string(), "--".to_string(), "hello".to_string()]; + strip_separator(&mut cmd); + assert_eq!(cmd, vec!["echo", "--", "hello"]); + } } diff --git a/src/cli/commands/exec.rs b/src/cli/commands/exec.rs index 9503dc5..3cde995 100644 --- a/src/cli/commands/exec.rs +++ b/src/cli/commands/exec.rs @@ -14,7 +14,8 @@ use tracing::debug; const DEFAULT_SHELL: &str = "/bin/zsh"; /// Execute the exec command -pub async fn execute(args: ExecArgs, config: &Config) -> MinoResult<()> { +pub async fn execute(mut args: ExecArgs, config: &Config) -> MinoResult<()> { + crate::cli::args::strip_separator(&mut args.command); #[cfg(unix)] let _terminal_guard = crate::terminal::TerminalGuard::save(); diff --git a/src/cli/commands/run/mod.rs b/src/cli/commands/run/mod.rs index 5113562..2feeb2a 100644 --- a/src/cli/commands/run/mod.rs +++ b/src/cli/commands/run/mod.rs @@ -48,7 +48,9 @@ struct ImageResolution { } /// Execute the run command -pub async fn execute(args: RunArgs, config: &Config) -> MinoResult<()> { +pub async fn execute(mut args: RunArgs, config: &Config) -> MinoResult<()> { + crate::cli::args::strip_separator(&mut args.command); + // Dispatch to native sandbox if requested let runtime_mode = crate::sandbox::resolve_runtime_mode(args.runtime.as_deref(), &config.general.runtime)?; diff --git a/src/orchestration/native_podman.rs b/src/orchestration/native_podman.rs index 2c4260f..3ce1c37 100644 --- a/src/orchestration/native_podman.rs +++ b/src/orchestration/native_podman.rs @@ -510,7 +510,6 @@ impl ContainerRuntime for NativePodmanRuntime { args.push("-t"); } args.push(container_id); - args.push("--"); args.extend(command.iter().map(String::as_str)); self.exec_interactive(&args).await } diff --git a/src/orchestration/orbstack_runtime.rs b/src/orchestration/orbstack_runtime.rs index 6abc431..5cd0255 100644 --- a/src/orchestration/orbstack_runtime.rs +++ b/src/orchestration/orbstack_runtime.rs @@ -574,7 +574,6 @@ impl ContainerRuntime for OrbStackRuntime { args.push("-t"); } args.push(container_id); - args.push("--"); args.extend(command.iter().map(String::as_str)); self.orbstack.exec_interactive(&args).await }