From f1235c67fc1e825e61cf4c1fcdd21c14ce5cfc15 Mon Sep 17 00:00:00 2001 From: Stephen <939775+thecodeassassin@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:57:06 +0000 Subject: [PATCH] feat: add --no-tmux to run agents without the tmux wrapper --- cmd/run.go | 7 +++++++ cmd/run_flags_test.go | 2 ++ internal/run/run.go | 6 ++++++ static/build/docker-entrypoint | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/cmd/run.go b/cmd/run.go index cee76d8..39634af 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -78,6 +78,9 @@ Sessions: Flags (passed after the agent name): -f, --no-firewall Disable network firewall + --no-tmux Run the agent without the tmux wrapper (better for + Kitty-protocol TUIs like Pi; loses tmux status bar, + workspace switcher, and tmux-based session resume) -r, --read-only Mount workspace as read-only -n, --no-env Don't pass host environment variables --name SESSION Name this session (resumes if it already exists) @@ -300,6 +303,7 @@ func runAgent(agentName string, passthrough []string) { WorkspaceOverride: flags.Workspace, EnvProfile: flags.EnvProfile, NoFirewall: flags.NoFirewall, + NoTmux: flags.NoTmux, ReadOnly: flags.ReadOnly, NoEnv: flags.NoEnv, Resume: flags.Resume, @@ -395,6 +399,7 @@ func runAgent(agentName string, passthrough []string) { type parsedFlags struct { NoFirewall bool + NoTmux bool ReadOnly bool NoEnv bool Resume bool @@ -432,6 +437,8 @@ func parseRunFlags(passthrough []string, defaults config.DefaultFlags) parsedFla switch arg { case "-f", "--no-firewall": f.NoFirewall = true + case "--no-tmux": + f.NoTmux = true case "-r", "--read-only": f.ReadOnly = true case "-n", "--no-env": diff --git a/cmd/run_flags_test.go b/cmd/run_flags_test.go index 1c8cc19..4cd292a 100644 --- a/cmd/run_flags_test.go +++ b/cmd/run_flags_test.go @@ -22,6 +22,8 @@ func TestParseRunFlags_BooleanFlags(t *testing.T) { }{ {"short no-firewall", []string{"-f"}, func(f parsedFlags) bool { return f.NoFirewall }}, {"long no-firewall", []string{"--no-firewall"}, func(f parsedFlags) bool { return f.NoFirewall }}, + {"no-tmux", []string{"--no-tmux"}, func(f parsedFlags) bool { return f.NoTmux }}, + {"no-tmux default off", []string{}, func(f parsedFlags) bool { return !f.NoTmux }}, {"short read-only", []string{"-r"}, func(f parsedFlags) bool { return f.ReadOnly }}, {"long read-only", []string{"--read-only"}, func(f parsedFlags) bool { return f.ReadOnly }}, {"short no-env", []string{"-n"}, func(f parsedFlags) bool { return f.NoEnv }}, diff --git a/internal/run/run.go b/internal/run/run.go index 760e18b..72970ef 100644 --- a/internal/run/run.go +++ b/internal/run/run.go @@ -55,6 +55,7 @@ type Options struct { WorkspaceOverride string EnvProfile string NoFirewall bool + NoTmux bool ReadOnly bool NoEnv bool Resume bool @@ -398,6 +399,11 @@ func AgentContainer(rt container.Runtime, opts Options) (int, error) { if opts.ResumeToken != "" { args = append(args, "-e", "EXITBOX_RESUME_TOKEN="+opts.ResumeToken) } + if opts.NoTmux { + // Run the agent directly, without the tmux wrapper, so Kitty-protocol + // TUIs (e.g. Pi) own the terminal and modified keys work natively. + args = append(args, "-e", "EXITBOX_NO_TMUX=1") + } if opts.EnvProfile != "" { args = append(args, "-e", "EXITBOX_ENV_PROFILE="+opts.EnvProfile) } diff --git a/static/build/docker-entrypoint b/static/build/docker-entrypoint index efd078e..13240d0 100755 --- a/static/build/docker-entrypoint +++ b/static/build/docker-entrypoint @@ -1081,6 +1081,10 @@ should_exec_raw() { should_start_tmux() { [[ -n "$AGENT" ]] || return 1 + # Opt-out: run the agent directly (no tmux wrapper) so TUIs that speak the + # Kitty keyboard protocol (e.g. Pi) own the terminal and modified keys like + # Shift+Enter work natively, with no tmux key re-encoding in the way. + [[ -z "${EXITBOX_NO_TMUX:-}" ]] || return 1 [[ -z "${TMUX:-}" ]] || return 1 [[ -t 0 && -t 1 ]] || return 1 command -v tmux >/dev/null 2>&1 || return 1