feat(cli): use user's login shell for command execution to ensure environment consistency

This commit is contained in:
teknium1 2026-02-27 15:10:27 -08:00
parent 4f3cb98e5e
commit f14ff3e041
2 changed files with 16 additions and 5 deletions

View file

@ -1,6 +1,7 @@
"""Local execution environment with interrupt support and non-blocking I/O.""" """Local execution environment with interrupt support and non-blocking I/O."""
import os import os
import shutil
import signal import signal
import subprocess import subprocess
import threading import threading
@ -17,6 +18,7 @@ class LocalEnvironment(BaseEnvironment):
- Background stdout drain thread to prevent pipe buffer deadlocks - Background stdout drain thread to prevent pipe buffer deadlocks
- stdin_data support for piping content (bypasses ARG_MAX limits) - stdin_data support for piping content (bypasses ARG_MAX limits)
- sudo -S transform via SUDO_PASSWORD env var - sudo -S transform via SUDO_PASSWORD env var
- Uses bash login shell so user env (.profile/.bashrc) is available
""" """
def __init__(self, cwd: str = "", timeout: int = 60, env: dict = None): def __init__(self, cwd: str = "", timeout: int = 60, env: dict = None):
@ -32,9 +34,14 @@ class LocalEnvironment(BaseEnvironment):
exec_command = self._prepare_command(command) exec_command = self._prepare_command(command)
try: try:
# Use the user's login shell so that rc files (.profile, .bashrc,
# .zprofile, .zshrc, etc.) are sourced and user-installed tools
# (nvm, pyenv, cargo, etc.) are available. Without this, Python's
# Popen(shell=True) uses /bin/sh which is dash on Debian/Ubuntu
# and old bash on macOS — neither sources the user's environment.
user_shell = os.environ.get("SHELL") or shutil.which("bash") or "/bin/bash"
proc = subprocess.Popen( proc = subprocess.Popen(
exec_command, [user_shell, "-lc", exec_command],
shell=True,
text=True, text=True,
cwd=work_dir, cwd=work_dir,
env=os.environ | self.env, env=os.environ | self.env,

View file

@ -32,6 +32,7 @@ Usage:
import json import json
import logging import logging
import os import os
import shutil
import signal import signal
import subprocess import subprocess
import threading import threading
@ -127,8 +128,9 @@ class ProcessRegistry:
# Try PTY mode for interactive CLI tools # Try PTY mode for interactive CLI tools
try: try:
import ptyprocess import ptyprocess
user_shell = os.environ.get("SHELL") or shutil.which("bash") or "/bin/bash"
pty_proc = ptyprocess.PtyProcess.spawn( pty_proc = ptyprocess.PtyProcess.spawn(
["bash", "-c", command], [user_shell, "-lc", command],
cwd=session.cwd, cwd=session.cwd,
env=os.environ | (env_vars or {}), env=os.environ | (env_vars or {}),
dimensions=(30, 120), dimensions=(30, 120),
@ -160,9 +162,11 @@ class ProcessRegistry:
logger.warning("PTY spawn failed (%s), falling back to pipe mode", e) logger.warning("PTY spawn failed (%s), falling back to pipe mode", e)
# Standard Popen path (non-PTY or PTY fallback) # Standard Popen path (non-PTY or PTY fallback)
# Use the user's login shell for consistency with LocalEnvironment --
# ensures rc files are sourced and user tools are available.
user_shell = os.environ.get("SHELL") or shutil.which("bash") or "/bin/bash"
proc = subprocess.Popen( proc = subprocess.Popen(
command, [user_shell, "-lc", command],
shell=True,
text=True, text=True,
cwd=session.cwd, cwd=session.cwd,
env=os.environ | (env_vars or {}), env=os.environ | (env_vars or {}),