-
Notifications
You must be signed in to change notification settings - Fork 28
🤖 feat: add ~/.mux/bashrc for shell environment customization #830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ammar-agent
wants to merge
1
commit into
main
Choose a base branch
from
mux-bashrc-primitive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+305
−4
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| # Shell Environment (`~/.mux/bashrc`) | ||
|
|
||
| Customize the shell environment for agent bash commands by creating a `~/.mux/bashrc` file. | ||
|
|
||
| ## Why This Exists | ||
|
|
||
| When mux runs bash commands (via `bash -c "command"`), the shell is **non-interactive** and **doesn't source `~/.bashrc`**. Most users have interactivity guards in their bashrc that skip content in non-interactive shells: | ||
|
|
||
| ```bash | ||
| # Common pattern in ~/.bashrc that skips setup for non-interactive shells | ||
| [[ $- != *i* ]] && return | ||
| ``` | ||
|
|
||
| This means: | ||
|
|
||
| - **Launching from Applications** — PATH is minimal (`/usr/bin:/bin:/usr/sbin:/sbin`) | ||
| - **Nix/direnv users** — Shell customizations aren't applied | ||
| - **Homebrew/pyenv/rbenv** — Tools not in PATH | ||
|
|
||
| The `~/.mux/bashrc` file is **always sourced** before every bash command, giving you a place to set up the environment reliably. | ||
|
|
||
| ## Setup | ||
|
|
||
| Create `~/.mux/bashrc`: | ||
|
|
||
| ```bash | ||
| mkdir -p ~/.mux | ||
| touch ~/.mux/bashrc | ||
| ``` | ||
|
|
||
| Add your shell customizations: | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
|
|
||
| # Add Homebrew to PATH (macOS) | ||
| eval "$(/opt/homebrew/bin/brew shellenv)" | ||
|
|
||
| # Add ~/bin to PATH | ||
| export PATH="$HOME/bin:$PATH" | ||
| ``` | ||
|
|
||
| ## Examples | ||
|
|
||
| ### Nix Users | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
|
|
||
| # Source nix profile | ||
| if [ -e "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then | ||
| . "$HOME/.nix-profile/etc/profile.d/nix.sh" | ||
| fi | ||
|
|
||
| # Enable direnv (auto-loads .envrc per directory) | ||
| eval "$(direnv hook bash)" | ||
| ``` | ||
|
|
||
| ### Python (pyenv) | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
| export PYENV_ROOT="$HOME/.pyenv" | ||
| export PATH="$PYENV_ROOT/bin:$PATH" | ||
| eval "$(pyenv init -)" | ||
| ``` | ||
|
|
||
| ### Node.js (nvm) | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
| export NVM_DIR="$HOME/.nvm" | ||
| [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" | ||
| ``` | ||
|
|
||
| ### Ruby (rbenv) | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
| eval "$(rbenv init -)" | ||
| ``` | ||
|
|
||
| ### Multiple Tools (asdf) | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
| . "$HOME/.asdf/asdf.sh" | ||
| ``` | ||
|
|
||
| ## Behavior | ||
|
|
||
| - **Sourced before every bash command** — including init hooks | ||
| - **Silently skipped if missing** — no bashrc file = no effect | ||
| - **Errors propagate** — if your bashrc has errors, they appear in command output | ||
| - **SSH workspaces** — the remote `~/.mux/bashrc` is sourced (you manage it on the remote host) | ||
|
|
||
| ## Comparison with Init Hooks | ||
|
|
||
| | Feature | `~/.mux/bashrc` | `.mux/init` | | ||
| | ------- | --------------------- | -------------------------- | | ||
| | When | Every bash command | Once at workspace creation | | ||
| | Scope | Global (all projects) | Per-project | | ||
| | Purpose | Shell environment | Project build/install | | ||
| | Errors | Command fails | Logged, non-blocking | | ||
|
|
||
| Use **bashrc** for environment (PATH, tools, direnv hooks). | ||
| Use **init hooks** for project setup (install dependencies, build). | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Commands Not Finding Tools | ||
|
|
||
| If tools aren't found, check that bashrc is being sourced: | ||
|
|
||
| ```bash | ||
| # In mux, run: | ||
| echo "bashrc: $MUX_BASHRC_SOURCED" | ||
| ``` | ||
|
|
||
| If empty, your bashrc might not exist. If you want to confirm it's being sourced, add to your bashrc: | ||
|
|
||
| ```bash | ||
| # ~/.mux/bashrc | ||
| export MUX_BASHRC_SOURCED=1 | ||
| ``` | ||
|
|
||
| ### Bashrc Errors | ||
|
|
||
| Errors in your bashrc will cause commands to fail. Test your bashrc: | ||
|
|
||
| ```bash | ||
| bash -c '[ -f "$HOME/.mux/bashrc" ] && . "$HOME/.mux/bashrc" && echo ok' | ||
| ``` | ||
|
|
||
| ### Performance | ||
|
|
||
| The bashrc runs before every command. Keep it fast: | ||
|
|
||
| - Avoid expensive operations (network calls, slow init scripts) | ||
| - Use lazy loading where possible | ||
| - Profile with `time bash -c '. ~/.mux/bashrc'` | ||
|
|
||
| ## Related | ||
|
|
||
| - [Init Hooks](./init-hooks.md) — Per-project initialization scripts | ||
| - [Project Secrets](./project-secrets.md) — Environment variables for API keys | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { describe, expect, it } from "bun:test"; | ||
| import { getMuxBashrcSourceSnippet } from "./paths"; | ||
|
|
||
| describe("getMuxBashrcSourceSnippet", () => { | ||
| it("should return a bash snippet that sources ~/.mux/bashrc if it exists", () => { | ||
| const snippet = getMuxBashrcSourceSnippet(); | ||
|
|
||
| // Should check for file existence with -f | ||
| expect(snippet).toContain("[ -f"); | ||
| // Should reference $HOME/.mux/bashrc | ||
| expect(snippet).toContain('$HOME/.mux/bashrc"'); | ||
| // Should source the file with . (dot command) | ||
| expect(snippet).toContain(". "); | ||
| }); | ||
|
|
||
| it("should use $HOME for portability across local and SSH runtimes", () => { | ||
| const snippet = getMuxBashrcSourceSnippet(); | ||
|
|
||
| // Should not use ~ (tilde) which doesn't expand in all contexts | ||
| expect(snippet).not.toContain("~/"); | ||
| // Should use $HOME which expands reliably | ||
| expect(snippet).toContain("$HOME/"); | ||
| }); | ||
|
|
||
| it("should silently skip if file doesn't exist and return success", () => { | ||
| const snippet = getMuxBashrcSourceSnippet(); | ||
|
|
||
| // Should use the pattern: [ -f file ] && . file || true | ||
| // - If file doesn't exist, [ -f file ] returns 1, && short-circuits, || true returns 0 | ||
| // - If file exists, [ -f file ] returns 0, && sources file, || is skipped | ||
| // The || true is critical for SSH runtime where commands are joined with && | ||
| expect(snippet).toContain("] && ."); | ||
| expect(snippet).toContain("|| true"); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
direnv hook bashsetsPROMPT_COMMANDwhich I believe means that direnv will not be activated until bash prompt is shown, which is never when running commands withbash -cAssuming bashrc is sourced from the project directory with
.envrcfile then I guess runningdirenv export bashinstead might work better (it's what direnv hook seems to run.)