Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/next/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Fixed
- Tab bar status commands now remove ESC-prefixed terminal control sequences instead of displaying their sequence bodies as text. (#3001)
- Unix plugin pane commands now default `PWD` to their resolved working directory, so direct popup tools open at explicit `--cwd` paths while preserving caller-provided `PWD` values. (#2984)

## [0.8.2] - 2026-08-19

Expand Down
34 changes: 20 additions & 14 deletions src/app/api/plugins/panes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ impl App {
pane: PluginManifestPane,
) -> String {
let context = self.current_plugin_context("plugin-pane");
let cwd = self.plugin_pane_cwd(plugin, params.cwd);
let extra_env =
match self.plugin_pane_launch_env(plugin, &pane.id, params.env.clone(), &context) {
match self.plugin_pane_launch_env(plugin, &pane.id, &cwd, params.env, &context) {
Ok(env) => env,
Err((code, message)) => return encode_error(id, &code, message),
};
let cwd = Some(self.plugin_pane_cwd(plugin, params.cwd));
let width = params.width.or(pane.width);
let height = params.height.or(pane.height);
if let Err(err) = self.spawn_popup_argv_command(
&pane.command,
cwd,
Some(cwd),
extra_env,
crate::app::popup::PopupGeometry { width, height },
) {
Expand All @@ -49,17 +49,21 @@ impl App {
pane: PluginManifestPane,
) -> String {
let context = self.current_plugin_context("plugin-pane");
let cwd = self.plugin_pane_cwd(plugin, params.cwd);
let extra_env =
match self.plugin_pane_launch_env(plugin, &pane.id, params.env.clone(), &context) {
match self.plugin_pane_launch_env(plugin, &pane.id, &cwd, params.env, &context) {
Ok(env) => env,
Err((code, message)) => return encode_error(id, &code, message),
};
let cwd = Some(self.plugin_pane_cwd(plugin, params.cwd));
let (ws_idx, new_pane) =
match self.spawn_overlay_argv_command(&pane.command, cwd, extra_env, Vec::new()) {
Ok(result) => result,
Err(err) => return encode_error(id, "plugin_pane_open_failed", err.to_string()),
};
let (ws_idx, new_pane) = match self.spawn_overlay_argv_command(
&pane.command,
Some(cwd),
extra_env,
Vec::new(),
) {
Ok(result) => result,
Err(err) => return encode_error(id, "plugin_pane_open_failed", err.to_string()),
};
let layout_tab_idx = self
.overlay_panes
.get(&new_pane.pane_id)
Expand Down Expand Up @@ -98,8 +102,9 @@ impl App {
);
};
let context = self.plugin_context_for_pane(ws_idx, target_pane, "plugin-pane");
let cwd = self.plugin_pane_cwd(plugin, params.cwd);
let extra_env =
match self.plugin_pane_launch_env(plugin, &pane.id, params.env.clone(), &context) {
match self.plugin_pane_launch_env(plugin, &pane.id, &cwd, params.env, &context) {
Ok(env) => env,
Err((code, message)) => return encode_error(id, &code, message),
};
Expand All @@ -110,7 +115,6 @@ impl App {
crate::api::schema::SplitDirection::Right => Direction::Horizontal,
crate::api::schema::SplitDirection::Down => Direction::Vertical,
};
let cwd = Some(self.plugin_pane_cwd(plugin, params.cwd));
let (rows, cols) = self.state.estimate_pane_size();
let previous_focus = self.state.current_pane_focus_target();
let Some(ws) = self.state.workspaces.get_mut(ws_idx) else {
Expand All @@ -121,7 +125,7 @@ impl App {
direction,
rows.max(4),
cols.max(10),
cwd,
Some(cwd),
&pane.command,
extra_env,
self.state.pane_scrollback_limit_bytes,
Expand Down Expand Up @@ -187,7 +191,7 @@ impl App {
let cwd = self.plugin_pane_cwd(plugin, params.cwd);
let context = self.plugin_context_for_workspace(ws_idx, "plugin-pane");
let extra_env =
match self.plugin_pane_launch_env(plugin, &pane.id, params.env.clone(), &context) {
match self.plugin_pane_launch_env(plugin, &pane.id, &cwd, params.env, &context) {
Ok(env) => env,
Err((code, message)) => return encode_error(id, &code, message),
};
Expand Down Expand Up @@ -233,10 +237,12 @@ impl App {
&self,
plugin: &InstalledPluginInfo,
entrypoint: &str,
cwd: &std::path::Path,
env: std::collections::HashMap<String, String>,
context: &PluginInvocationContext,
) -> Result<Vec<(String, String)>, (String, String)> {
let mut env = super::super::env::normalize_launch_env(env)?;
crate::platform::set_default_plugin_pane_pwd(&mut env, cwd);
let context_json = serde_json::to_string(&context)
.map_err(|err| ("invalid_plugin_context".to_string(), err.to_string()))?;
super::env::ensure_plugin_user_dirs(plugin)
Expand Down
10 changes: 10 additions & 0 deletions src/platform/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ use std::process::Command;

use super::{ClipboardImage, ForegroundJob, Signal};

#[cfg(unix)]
pub(crate) use super::unix_common::set_default_plugin_pane_pwd;

#[cfg(not(unix))]
pub(crate) fn set_default_plugin_pane_pwd(
_env: &mut Vec<(String, String)>,
_cwd: &std::path::Path,
) {
}

pub(crate) fn remote_ssh_config_paths() -> super::RemoteSshConfigPaths {
super::RemoteSshConfigPaths {
user_config: std::env::var_os("HOME")
Expand Down
3 changes: 2 additions & 1 deletion src/platform/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ pub(crate) use super::unix_common::{
configure_status_command, create_remote_private_dir, create_remote_ssh_config_dir,
create_remote_ssh_config_file, hostname, local_datetime, remote_bridge_endpoint_path,
remote_private_temp_base, remote_reattach_argument, remote_reattach_program,
remote_ssh_config_paths, status_commands_supported, StatusCommandGuard,
remote_ssh_config_paths, set_default_plugin_pane_pwd, status_commands_supported,
StatusCommandGuard,
};

const WSL_MARKER_ENV_VARS: &[&str] = &["WSL_DISTRO_NAME", "WSL_INTEROP"];
Expand Down
3 changes: 2 additions & 1 deletion src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ pub(crate) use super::unix_common::{
configure_status_command, create_remote_private_dir, create_remote_ssh_config_dir,
create_remote_ssh_config_file, hostname, local_datetime, remote_bridge_endpoint_path,
remote_private_temp_base, remote_reattach_argument, remote_reattach_program,
remote_ssh_config_paths, status_commands_supported, StatusCommandGuard,
remote_ssh_config_paths, set_default_plugin_pane_pwd, status_commands_supported,
StatusCommandGuard,
};

const PROC_PGRP_ONLY: u32 = 2;
Expand Down
18 changes: 18 additions & 0 deletions src/platform/unix_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,28 @@ fn datetime_from_tm(value: &libc::tm) -> Option<time::PrimitiveDateTime> {
Some(time::PrimitiveDateTime::new(date, time))
}

pub(crate) fn set_default_plugin_pane_pwd(env: &mut Vec<(String, String)>, cwd: &std::path::Path) {
if !env.iter().any(|(key, _)| key == "PWD") {
env.push(("PWD".to_string(), cwd.display().to_string()));
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn plugin_pane_pwd_defaults_to_cwd_without_overriding_explicit_env() {
let cwd = Path::new("/plugin-cwd");
let mut derived = vec![("OTHER".to_string(), "value".to_string())];
set_default_plugin_pane_pwd(&mut derived, cwd);
assert!(derived.contains(&("PWD".to_string(), "/plugin-cwd".to_string())));

let mut explicit = vec![("PWD".to_string(), "/caller-pwd".to_string())];
set_default_plugin_pane_pwd(&mut explicit, cwd);
assert_eq!(explicit, [("PWD".to_string(), "/caller-pwd".to_string())]);
}

#[test]
fn remote_ssh_config_dir_rejects_overlong_control_socket_name() {
let err = create_remote_ssh_config_dir(&"x".repeat(200)).unwrap_err();
Expand Down
6 changes: 6 additions & 0 deletions src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ use std::{

mod clipboard_image;

pub(crate) fn set_default_plugin_pane_pwd(
_env: &mut Vec<(String, String)>,
_cwd: &std::path::Path,
) {
}

use windows_sys::{
Wdk::System::Threading::{NtQueryInformationProcess, ProcessBasicInformation},
Win32::{
Expand Down
Loading