Skip to content

Fix launch shell startup behavior and inherit environment variables#1231

Open
Jelloeater wants to merge 4 commits into
asheshgoplani:mainfrom
Jelloeater:main
Open

Fix launch shell startup behavior and inherit environment variables#1231
Jelloeater wants to merge 4 commits into
asheshgoplani:mainfrom
Jelloeater:main

Conversation

@Jelloeater
Copy link
Copy Markdown

@Jelloeater Jelloeater commented May 30, 2026

This pull request introduces a new [shell].launch_shell feature to the agent session system, allowing agent commands to be wrapped in an interactive shell so that environment variables from shell startup files (like .zshrc or .bashrc) are available to the agent process. This addresses cases where agents launched from the TUI did not inherit the user's interactive shell environment, causing failures in MCP configs that reference environment variables. The implementation includes per-session and global config support, proper exclusions (sandbox, SSH, shell tools), robust quoting, and comprehensive tests.

The most important changes are:

Core feature: Interactive shell wrapping for agent commands

  • Added a new LaunchShell field to the Instance struct and [shell] config section, with logic to wrap agent commands in an interactive shell invocation ($SHELL -il -c '<command>') when enabled. Bash sessions also explicitly source .bashrc to ensure environment variables are loaded. [1] [2] [3] [4]

Command preparation logic:

  • Updated the prepareCommand method to apply the launch-shell wrapper in the correct order, ensuring shell startup files are loaded before the agent command and any user command wrappers.

Documentation:

  • Documented the new launch_shell option in the configuration reference, including its default value, usage, and caveats. [1] [2]

Testing:

  • Added a comprehensive test suite in issue1218_launch_shell_test.go covering all major behaviors: flag on/off, per-session and global config, exclusions (sandbox, SSH, shell tool), quote escaping, and integration with prepareCommand.

Code comments and clarity:

  • Improved comments in env.go to clarify the order of environment sourcing and where shell wrapping occurs in the startup sequence.

Summary by CodeRabbit

  • New Features
    • Added launch_shell configuration option to wrap session commands in an interactive shell, enabling shell startup files to execute before launching the agent.
    • Supports global configuration and per-session override.
    • Automatically excluded for shell tools, SSH sessions, and sandboxed environments.

Review Change Stack

Claude AI and others added 3 commits May 30, 2026 03:31
feat(shell): add launch_shell option to inherit shell environment variables
Copilot AI review requested due to automatic review settings May 30, 2026 19:10
@Jelloeater Jelloeater requested a review from asheshgoplani as a code owner May 30, 2026 19:10
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7349ff09-6b03-4a89-83f0-556c2dffbf41

📥 Commits

Reviewing files that changed from the base of the PR and between f6bc0f5 and 2950384.

⛔ Files ignored due to path filters (1)
  • skills/agent-deck/references/config-reference.md is excluded by !**/*.md
📒 Files selected for processing (4)
  • internal/session/env.go
  • internal/session/instance.go
  • internal/session/issue1218_launch_shell_test.go
  • internal/session/userconfig.go

📝 Walkthrough

Walkthrough

This PR adds a launch_shell configuration option that wraps agent session commands in an interactive login shell to source user startup files (.bashrc, .zshrc) before the agent executes, with per-session override and global config support, excluding shell tools, SSH, and sandbox sessions.

Changes

Launch Shell Feature

Layer / File(s) Summary
Configuration schema and Instance struct
internal/session/userconfig.go, internal/session/instance.go
ShellSettings gains a LaunchShell *bool TOML field with GetLaunchShell() resolver; Instance struct adds a LaunchShell *bool JSON field to enable per-session override.
Wrapping logic and command preparation integration
internal/session/instance.go, internal/session/env.go
launchShellEnabled() checks per-session override, then global config with false default; wrapLaunchShell() rewrites commands to $SHELL -il -c, explicitly sourcing ~/.bashrc for bash; prepareCommand() applies wrapping after exit-to-shell and before user wrapper, excluding shell tools, SSH remotes, and sandboxed sessions. Documentation clarifies that buildEnvSourceCommand does not handle launch-shell wrapping.
Test infrastructure and comprehensive behavior validation
internal/session/issue1218_launch_shell_test.go
Test helpers isolate HOME and SHELL; PTY-backed runner verifies wrapped commands source startup files; tests cover enabled/disabled states, exclusion rules (shell tool, SSH, sandbox), quote escaping, integration with prepareCommand, global config fallback, per-session override precedence over global config, SHELL unset default to /bin/bash, and combined behavior with ExitToShell.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • asheshgoplani/agent-deck#1203: Both PRs modify internal/session/instance.go's shell-wrapping flow in prepareCommand(), with this PR adding launch-shell wrapping after the exit-to-shell wrapping introduced in that PR.
🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title does not follow the Conventional Commits format requirement; it lacks a type prefix (e.g., 'feat:', 'fix:', 'chore:'). Update the title to follow Conventional Commits format: 'feat(shell): launch shell startup behavior and inherit environment variables' or similar with appropriate type prefix.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 73.33% which is sufficient. The required threshold is 70.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Remote_parity ✅ Passed PR modifies only internal/session/ (not TUI directories internal/ui/ or cmd/agent-deck/), so the check's prerequisite condition is not triggered. Check not applicable.
Test_coverage_per_surface ✅ Passed Feature operates at core Instance layer shared by all surfaces; 12 unit tests comprehensively cover behavior, matching similar exit-to-shell feature pattern.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an opt-in [shell].launch_shell config (issue #1218) that wraps agent spawn commands with an interactive shell invocation ($SHELL -il -c '<cmd>') so environment variables defined in ~/.zshrc/~/.bashrc are inherited by the agent process. Supports per-session override, excludes sandboxed/SSH/shell sessions, and integrates with the existing prepareCommand wrapping chain.

Changes:

  • New LaunchShell *bool field in ShellSettings and per-Instance, with GetLaunchShell()/launchShellEnabled() accessors defaulting to off.
  • New wrapLaunchShell step inserted into prepareCommand, with bash-specific ~/.bashrc sourcing and single-quote escaping.
  • Documentation update in config-reference.md and a new test file covering happy paths, exclusions, config fallback, and interaction with exit_to_shell.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
skills/agent-deck/references/config-reference.md Documents the new launch_shell config key.
internal/session/userconfig.go Adds LaunchShell field and GetLaunchShell() accessor.
internal/session/instance.go Adds Instance.LaunchShell, launchShellEnabled, wrapLaunchShell; wires into prepareCommand.
internal/session/env.go Adds explanatory comment on layering with launch_shell.
internal/session/issue1218_launch_shell_test.go New tests covering wrap behavior, exclusions, config fallback, and exit_to_shell interaction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

//
// The transform is:
//
// $SHELL -il -c '<command>'
Comment on lines +6893 to +6895
if filepath.Base(shell) == "bash" {
return fmt.Sprintf("%s -il -c 'if [ -f ~/.bashrc ]; then source ~/.bashrc; fi; %s'", shell, escaped)
}

// --- Empty SHELL env uses default ---

func TestLaunchShell_DefaultsToBashdWhenSHELLUnset(t *testing.T) {
Comment on lines +224 to +227
if !strings.Contains(wrapped, `opencode --query '"'"'hello world'"'"'`) &&
!strings.Contains(wrapped, `opencode --query '\"'\"'hello world'\"'\"'`) {
t.Fatalf("single quotes must be escaped, got:\n%s", wrapped)
}

// --- Global config fallback ---

func TestLaunchShell_GlobalConfigFallback(t *testing.T) {

// --- Per-session override takes precedence ---

func TestLaunchShell_PerSessionOverride(t *testing.T) {
Comment on lines +6878 to +6881
func (i *Instance) wrapLaunchShell(command string) string {
if command == "" || i.IsSandboxed() || !i.launchShellEnabled() {
return command
}
Comment on lines +30 to +35
// launchShellTestEnv isolates HOME and SHELL for deterministic testing.
func launchShellTestEnv(t *testing.T) {
t.Helper()
origHome := os.Getenv("HOME")
origShell := os.Getenv("SHELL")
os.Setenv("HOME", t.TempDir())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants