Skip to content

Commit a9bd448

Browse files
authored
feat: allow starting a session with an initial command (#5543)
* feat: allow starting a session with an initial command * add pr
1 parent 52f1210 commit a9bd448

19 files changed

Lines changed: 525 additions & 88 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1515
* fix: properly fire visibility event to plugins when their tab/floating-layer is hidden/shown (https://github.com/zellij-org/zellij/pull/5518 and https://github.com/zellij-org/zellij/pull/5534)
1616
* feat: allow setting explicit_theme_hue (light/dark) (https://github.com/zellij-org/zellij/pull/5536)
1717
* fix: input mode updates when switching tabs (https://github.com/zellij-org/zellij/pull/5535)
18+
* feat: allow starting a session with an initial command, eg. `zellij attach -a my-session -- htop` (https://github.com/zellij-org/zellij/pull/5543)
1819

1920
## [0.45.0] - 2026-08-20
2021
* feat: allow tabs to have different sizes if clients aren't focused on the same one (https://github.com/zellij-org/zellij/pull/5133)

src/commands.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use zellij_utils::{
3737
data::ConnectToSession,
3838
envs,
3939
input::{
40-
actions::Action,
40+
actions::{initial_panes_from_cli, Action},
4141
config::{Config, ConfigError},
4242
options::Options,
4343
},
@@ -229,7 +229,7 @@ pub(crate) fn start_web_server(
229229
}
230230

231231
fn create_new_client() -> ClientInfo {
232-
ClientInfo::New(generate_unique_session_name_or_exit(), None, None)
232+
ClientInfo::New(generate_unique_session_name_or_exit(), None, None, None)
233233
}
234234

235235
#[cfg(feature = "web_server_capability")]
@@ -559,7 +559,7 @@ fn attach_with_session_name(
559559
match &session_name {
560560
Some(session) if create => match session_exists(session) {
561561
Ok(true) => ClientInfo::Attach(session_name.unwrap(), config_options),
562-
Ok(false) => ClientInfo::New(session_name.unwrap(), None, None),
562+
Ok(false) => ClientInfo::New(session_name.unwrap(), None, None, None),
563563
Err(kind) => {
564564
eprintln!("{}", session_listing_error_message(kind));
565565
process::exit(1);
@@ -665,6 +665,9 @@ pub(crate) fn start_client(opts: CliArgs) {
665665
forget: false,
666666
ca_cert: None,
667667
insecure: false,
668+
initial_command: vec![],
669+
close_on_exit: false,
670+
start_suspended: false,
668671
}));
669672
} else {
670673
opts.command = None;
@@ -700,6 +703,9 @@ pub(crate) fn start_client(opts: CliArgs) {
700703
forget,
701704
ca_cert,
702705
insecure,
706+
initial_command,
707+
close_on_exit,
708+
start_suspended,
703709
})) = opts.command.clone()
704710
{
705711
if let Some(remote_session_url) = session_name.as_ref().and_then(|s| {
@@ -719,6 +725,11 @@ pub(crate) fn start_client(opts: CliArgs) {
719725
std::process::exit(2);
720726
}
721727

728+
if !initial_command.is_empty() {
729+
eprintln!("Cannot run an initial command on a remote session.");
730+
std::process::exit(2);
731+
}
732+
722733
#[cfg(feature = "web_server_capability")]
723734
if let Err(e) = zellij_client::start_remote_client(
724735
Box::new(os_input.clone()),
@@ -803,6 +814,18 @@ pub(crate) fn start_client(opts: CliArgs) {
803814
client.set_cwd(new_session_cwd);
804815
}
805816

817+
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
818+
if let Some(initial_panes) = initial_panes_from_cli(
819+
initial_command,
820+
None,
821+
Some(current_dir.clone()),
822+
current_dir,
823+
close_on_exit,
824+
start_suspended,
825+
) {
826+
client.set_initial_panes(initial_panes);
827+
}
828+
806829
let tab_position_to_focus = reconnect_to_session
807830
.as_ref()
808831
.and_then(|r| r.tab_position.clone());
@@ -829,7 +852,7 @@ pub(crate) fn start_client(opts: CliArgs) {
829852
opts,
830853
config,
831854
config_options,
832-
ClientInfo::New(session_name, layout_info, new_session_cwd),
855+
ClientInfo::New(session_name, layout_info, new_session_cwd, None),
833856
None,
834857
None,
835858
is_a_reconnect,
@@ -876,7 +899,12 @@ pub(crate) fn start_client(opts: CliArgs) {
876899
opts,
877900
config,
878901
config_options.clone(),
879-
ClientInfo::New(session_name.clone(), layout_info, new_session_cwd),
902+
ClientInfo::New(
903+
session_name.clone(),
904+
layout_info,
905+
new_session_cwd,
906+
None,
907+
),
880908
None,
881909
None,
882910
is_a_reconnect,
@@ -899,7 +927,7 @@ pub(crate) fn start_client(opts: CliArgs) {
899927
opts,
900928
config,
901929
config_options,
902-
ClientInfo::New(session_name, layout_info, new_session_cwd),
930+
ClientInfo::New(session_name, layout_info, new_session_cwd, None),
903931
None,
904932
None,
905933
is_a_reconnect,

zellij-client/src/lib.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ use zellij_utils::cli::CliArgs;
157157
use zellij_utils::{
158158
channels::{self, ChannelWithContext, SenderWithContext},
159159
consts::{set_permissions, ZELLIJ_SOCK_DIR},
160-
data::{ClientId, ConnectToSession, KeyWithModifier, LayoutInfo, LayoutMetadata},
160+
data::{
161+
ClientId, CommandOrPlugin, ConnectToSession, KeyWithModifier, LayoutInfo, LayoutMetadata,
162+
},
161163
envs,
162164
errors::{ClientContext, ContextType, ErrorInstruction},
163165
input::{
@@ -503,7 +505,12 @@ pub fn spawn_server(socket_path: &Path, debug: bool) -> io::Result<()> {
503505
#[derive(Debug, Clone)]
504506
pub enum ClientInfo {
505507
Attach(String, Options),
506-
New(String, Option<LayoutInfo>, Option<PathBuf>), // PathBuf -> explicit cwd
508+
New(
509+
String,
510+
Option<LayoutInfo>,
511+
Option<PathBuf>,
512+
Option<Vec<CommandOrPlugin>>,
513+
), // PathBuf -> explicit cwd
507514
Resurrect(String, PathBuf, bool, Option<PathBuf>), // (name, path_to_layout, force_run_commands, cwd)
508515
Watch(String, Options), // Watch mode (read-only)
509516
}
@@ -512,24 +519,30 @@ impl ClientInfo {
512519
pub fn get_session_name(&self) -> &str {
513520
match self {
514521
Self::Attach(ref name, _) => name,
515-
Self::New(ref name, _layout_info, _layout_cwd) => name,
522+
Self::New(ref name, _layout_info, _layout_cwd, _initial_panes) => name,
516523
Self::Resurrect(ref name, _, _, _) => name,
517524
Self::Watch(ref name, _) => name,
518525
}
519526
}
520527
pub fn set_layout_info(&mut self, new_layout_info: LayoutInfo) {
521528
match self {
522-
ClientInfo::New(_, layout_info, _) => *layout_info = Some(new_layout_info),
529+
ClientInfo::New(_, layout_info, _, _) => *layout_info = Some(new_layout_info),
523530
_ => {},
524531
}
525532
}
526533
pub fn set_cwd(&mut self, new_cwd: PathBuf) {
527534
match self {
528-
ClientInfo::New(_, _, cwd) => *cwd = Some(new_cwd),
535+
ClientInfo::New(_, _, cwd, _) => *cwd = Some(new_cwd),
529536
ClientInfo::Resurrect(_, _, _, cwd) => *cwd = Some(new_cwd),
530537
_ => {},
531538
}
532539
}
540+
pub fn set_initial_panes(&mut self, new_initial_panes: Vec<CommandOrPlugin>) {
541+
match self {
542+
ClientInfo::New(_, _, _, initial_panes) => *initial_panes = Some(new_initial_panes),
543+
_ => {},
544+
}
545+
}
533546
}
534547

535548
#[derive(Debug, Clone)]
@@ -1031,6 +1044,7 @@ pub fn start_client(
10311044
force_run_layout_commands: false,
10321045
cwd: None,
10331046
host_terminal_env: host_terminal_env(),
1047+
initial_panes: None,
10341048
};
10351049
(
10361050
ClientToServerMsg::AttachClient {
@@ -1077,6 +1091,7 @@ pub fn start_client(
10771091
force_run_layout_commands: force_run_commands,
10781092
cwd,
10791093
host_terminal_env: host_terminal_env(),
1094+
initial_panes: None,
10801095
};
10811096

10821097
os_input.update_session_name(name);
@@ -1101,7 +1116,7 @@ pub fn start_client(
11011116
ipc_pipe,
11021117
)
11031118
},
1104-
ClientInfo::New(name, layout_info, layout_cwd) => {
1119+
ClientInfo::New(name, layout_info, layout_cwd, initial_panes) => {
11051120
envs::set_session_name(name.clone());
11061121

11071122
let cli_assets = CliAssets {
@@ -1134,6 +1149,7 @@ pub fn start_client(
11341149
force_run_layout_commands: false,
11351150
cwd: layout_cwd,
11361151
host_terminal_env: host_terminal_env(),
1152+
initial_panes,
11371153
};
11381154

11391155
os_input.update_session_name(name);
@@ -1652,6 +1668,7 @@ pub fn start_server_detached(
16521668
force_run_layout_commands: force_run_commands,
16531669
cwd,
16541670
host_terminal_env: host_terminal_env(),
1671+
initial_panes: None,
16551672
};
16561673

16571674
os_input.update_session_name(name);
@@ -1676,7 +1693,7 @@ pub fn start_server_detached(
16761693
ipc_pipe,
16771694
)
16781695
},
1679-
ClientInfo::New(name, layout_info, layout_cwd) => {
1696+
ClientInfo::New(name, layout_info, layout_cwd, initial_panes) => {
16801697
envs::set_session_name(name.clone());
16811698

16821699
let cli_assets = CliAssets {
@@ -1710,6 +1727,7 @@ pub fn start_server_detached(
17101727
force_run_layout_commands: false,
17111728
cwd: layout_cwd,
17121729
host_terminal_env: host_terminal_env(),
1730+
initial_panes,
17131731
};
17141732

17151733
os_input.update_session_name(name);

zellij-client/src/web_client/session_management.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub fn create_first_message(
103103
force_run_layout_commands: false,
104104
cwd: None,
105105
host_terminal_env: Default::default(),
106+
initial_panes: None,
106107
};
107108

108109
ClientToServerMsg::FirstClientConnected {
@@ -123,6 +124,7 @@ pub fn create_first_message(
123124
force_run_layout_commands: false,
124125
cwd: None,
125126
host_terminal_env: Default::default(),
127+
initial_panes: None,
126128
};
127129
let is_web_client = true;
128130

zellij-integration-tests/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ pub use nested::{
2323
host_descended_bar_with_ascend_keys_settled, wait_for_settled_composite,
2424
NestedDepthThreeHarness, NestedHarness,
2525
};
26-
pub use runner::{assert_same_rendered_grid, normalized, TestClient, TestRunner, TestSession};
26+
pub use runner::{
27+
assert_same_rendered_grid, normalized, BackgroundTestSession, TestClient, TestRunner,
28+
TestSession,
29+
};
2730
pub use zellij_utils::data::LayoutInfo;
2831
pub use zellij_utils::pane_size::Size;
2932

0 commit comments

Comments
 (0)