Skip to content

feat(shell_alias): add shell_alias support for cross-shell aliases#7316

Merged
jdx merged 6 commits intomainfrom
feat/shell-alias
Dec 15, 2025
Merged

feat(shell_alias): add shell_alias support for cross-shell aliases#7316
jdx merged 6 commits intomainfrom
feat/shell-alias

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Dec 15, 2025

Summary

Adds [shell_alias] configuration to define shell aliases that work across bash, zsh, and fish. Aliases update dynamically when entering/leaving directories, similar to environment variables.

  • Adds [shell_alias] section to mise.toml for defining shell aliases
  • Deprecates [alias] in favor of [tool_alias] for tool version aliases (clearer naming)
  • Aliases are set/unset dynamically via hook-env as you change directories
  • Supports bash, zsh, and fish shells

Example configuration:

[shell_alias]
ll = "ls -la"
myTool = "path/to/my_tool.sh --default-option"

[tool_alias.node.versions]
lts = "22.0.0"

Closes #3551

Changes

  • src/shell/mod.rs: Added set_alias/unset_alias methods to Shell trait
  • src/shell/bash.rs: Bash implementation for alias commands
  • src/shell/zsh.rs: Zsh implementation (delegates to Bash)
  • src/shell/fish.rs: Fish implementation using alias/functions -e
  • src/config/config_file/mise_toml.rs: Added shell_alias and tool_alias fields with deprecation warning for [alias]
  • src/config/config_file/mod.rs: Added shell_aliases() to ConfigFile trait
  • src/config/mod.rs: Added shell_aliases loading to Config
  • src/hook_env.rs: Added alias tracking in session and build_alias_commands() function
  • src/cli/hook_env.rs: Integrated alias output into hook-env command

Test plan

  • Run mise run lint-fix - passes
  • Run mise run test:e2e test_shell_alias test_tool_alias - all pass
  • Run cargo test --all-features - 377 tests pass

🤖 Generated with Claude Code


Note

Adds cross-shell dynamic shell aliases via [shell_alias] and renames [alias] to [tool_alias], with hook-env integration, config loading, docs, and tests.

  • Core/CLI:
    • Hook env now computes and emits alias diffs via hook_env::build_alias_commands and tracks aliases in HookEnvSession (src/cli/hook_env.rs, src/hook_env.rs).
    • Deactivation clears prior aliases (hook_env::clear_aliases).
  • Config:
    • Parse and expose [shell_alias]; load into Config.shell_aliases with sources (src/config/config_file/mise_toml.rs, src/config/mod.rs).
    • Introduce [tool_alias]; merge with legacy [alias] (deprecated) with precedence and warning.
  • Shells:
    • Extend Shell trait with set_alias/unset_alias; implement for bash, zsh, fish (src/shell/*).
  • Docs:
    • Add Shell Aliases page and sidebar entry; rename "Aliases" to "Tool Aliases"; document [tool_alias] and [shell_alias] (docs/*).
  • Tests:
    • New e2e: test_shell_alias (set/update/unset behavior) and test_tool_alias (fallback, precedence).

Written by Cursor Bugbot for commit 210b215. This will update automatically on new commits. Configure here.

Adds [shell_alias] configuration to define shell aliases that work across
bash, zsh, and fish. Aliases update dynamically when entering/leaving
directories, similar to environment variables.

Also deprecates [alias] in favor of [tool_alias] for tool version aliases,
providing clearer separation between shell aliases and tool version aliases.

Changes:
- Add set_alias/unset_alias methods to Shell trait with implementations
  for bash, zsh, and fish
- Add shell_alias and tool_alias fields to mise.toml config parsing
- Add deprecation warning when [alias] is used (recommends [tool_alias])
- Track aliases in HookEnvSession for dynamic updates
- Add e2e tests for shell_alias and tool_alias features

Example configuration:
```toml
[shell_alias]
ll = "ls -la"
myTool = "path/to/my_tool.sh --default-option"

[tool_alias.node.versions]
lts = "22.0.0"
```

Closes #3551

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings December 15, 2025 13:45
@jdx jdx changed the title feat(config): add shell_alias support for cross-shell aliases feat(shell_alias): add shell_alias support for cross-shell aliases Dec 15, 2025
Copy link
Copy Markdown
Contributor

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

This PR introduces cross-shell alias support via [shell_alias] configuration and deprecates [alias] in favor of [tool_alias] for better semantic clarity. Shell aliases are dynamically managed through the hook-env system as users navigate directories, similar to environment variables.

  • Added [shell_alias] section for defining shell aliases that work across bash, zsh, and fish
  • Renamed [alias] to [tool_alias] for tool version aliases with backward compatibility
  • Integrated alias management into the hook-env workflow with tracking and diff-based updates

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/shell/mod.rs Added set_alias and unset_alias trait methods with default implementations
src/shell/bash.rs Implemented bash alias commands using alias and unalias
src/shell/zsh.rs Delegated zsh alias handling to bash implementation
src/shell/fish.rs Implemented fish alias commands using alias and functions -e
src/config/config_file/mise_toml.rs Added shell_alias and tool_alias fields with deprecation warning for [alias]
src/config/config_file/mod.rs Added shell_aliases() trait method to ConfigFile
src/config/mod.rs Added shell_aliases field and load_shell_aliases function
src/hook_env.rs Added aliases tracking to HookEnvSession and build_alias_commands function
src/cli/hook_env.rs Integrated alias output into hook-env command execution
e2e/cli/test_tool_alias Added e2e tests for tool_alias functionality
e2e/cli/test_shell_alias Added e2e tests for shell_alias functionality

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

Comment thread src/shell/mod.rs
Comment on lines +82 to +84
fn set_alias(&self, name: &str, cmd: &str) -> String {
// Default implementation returns empty string (unsupported)
let _ = (name, cmd);
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

The parameter silencing pattern let _ = (name, cmd); is unconventional. Use the standard _ prefix for unused parameters instead: change the function signature to fn set_alias(&self, _name: &str, _cmd: &str) and remove line 84.

Suggested change
fn set_alias(&self, name: &str, cmd: &str) -> String {
// Default implementation returns empty string (unsupported)
let _ = (name, cmd);
fn set_alias(&self, _name: &str, _cmd: &str) -> String {
// Default implementation returns empty string (unsupported)

Copilot uses AI. Check for mistakes.
Comment thread src/shell/mod.rs
Comment on lines +89 to +91
fn unset_alias(&self, name: &str) -> String {
// Default implementation returns empty string (unsupported)
let _ = name;
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

The parameter silencing pattern let _ = name; is unconventional. Use the standard _ prefix for unused parameters instead: change the function signature to fn unset_alias(&self, _name: &str) and remove line 91.

Suggested change
fn unset_alias(&self, name: &str) -> String {
// Default implementation returns empty string (unsupported)
let _ = name;
fn unset_alias(&self, _name: &str) -> String {
// Default implementation returns empty string (unsupported)

Copilot uses AI. Check for mistakes.
Comment thread src/shell/bash.rs
}

fn unset_alias(&self, name: &str) -> String {
let name = shell_escape::unix::escape(name.into());
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

The error suppression pattern 2>/dev/null || true masks all errors. Consider handling the case where the alias doesn't exist more explicitly, or document why this pattern is necessary for robustness.

Suggested change
let name = shell_escape::unix::escape(name.into());
let name = shell_escape::unix::escape(name.into());
// Suppress errors if the alias does not exist. This is intentional and safe,
// as 'unalias' only errors if the alias is missing, which is not a problem here.

Copilot uses AI. Check for mistakes.
- Add new docs/shell-aliases.md page documenting the shell_alias feature
- Update docs/configuration.md to add shell_alias section and rename alias to tool_alias
- Update docs/dev-tools/aliases.md to use tool_alias with deprecation notice
- Update sidebar to add Shell Aliases under Environments section
- Rename "Aliases" to "Tool Aliases" in sidebar for clarity

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment thread src/hook_env.rs
}

output
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Aliases not set when switching between shell types

When switching from a shell that doesn't support aliases (nushell, elvish, etc.) to one that does (bash, zsh, fish) while inheriting __MISE_SESSION, aliases won't be set. The session stores which aliases were in the config regardless of whether they were actually set in the shell. The build_alias_commands function compares PREV_SESSION.aliases with new_aliases - if they're equal, no commands are generated. However, the previous shell may have returned empty strings from set_alias. Since HookEnvSession doesn't track the shell type, there's no way to detect this shell change and force re-setting aliases.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Bug: Shell aliases not cleaned up during deactivation

When mise deactivate is called, the build_deactivation_script function clears environment variables via clear_old_env but does not clear shell aliases. The PREV_SESSION.aliases contains all aliases mise has set, but these are never unset during deactivation. This causes aliases like ll='ls -la' to persist in the shell after deactivation, which is inconsistent with environment variable cleanup behavior. Users would have dangling aliases after running mise deactivate.

src/shell/mod.rs#L117-L126

mise/src/shell/mod.rs

Lines 117 to 126 in a42c00c

pub fn build_deactivation_script(shell: &dyn Shell) -> String {
if !env::is_activated() {
return String::new();
}
let mut out = hook_env::clear_old_env(shell);
out.push_str(&shell.deactivate());
out
}

src/hook_env.rs#L335-L356

mise/src/hook_env.rs

Lines 335 to 356 in a42c00c

pub fn clear_old_env(shell: &dyn Shell) -> String {
let mut patches = env::__MISE_DIFF.reverse().to_patches();
// For fish shell, filter out PATH operations from the reversed diff because
// fish has its own PATH management that conflicts with ours.
if shell.to_string() == "fish" {
patches.retain(|p| match p {
EnvDiffOperation::Add(k, _)
| EnvDiffOperation::Change(k, _)
| EnvDiffOperation::Remove(k) => k != &*PATH_KEY,
});
// Fish also needs PATH restored during deactivation
let new_path = compute_deactivated_path();
patches.push(EnvDiffOperation::Change(PATH_KEY.to_string(), new_path));
} else {
// For non-fish shells, we need to preserve user-added paths while removing mise paths
let new_path = compute_deactivated_path();
patches.push(EnvDiffOperation::Change(PATH_KEY.to_string(), new_path));
}
build_env_commands(shell, &patches)
}

Fix in Cursor Fix in Web


Shell aliases were not being cleaned up when `mise deactivate` was called.
The `clear_old_env` function cleared environment variables but not aliases
from `PREV_SESSION.aliases`, causing dangling aliases to persist in the shell.

This fix adds alias clearing to `clear_old_env` so that all aliases are
properly unset during deactivation, matching the behavior of environment
variable cleanup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Dec 15, 2025

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.12.7 x -- echo 20.2 ± 0.3 19.6 22.4 1.00
mise x -- echo 20.3 ± 0.8 19.1 22.5 1.01 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.12.7 env 20.3 ± 0.7 19.3 29.1 1.04 ± 0.06
mise env 19.5 ± 0.9 18.5 28.4 1.00

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.12.7 hook-env 19.8 ± 0.6 19.3 30.3 1.02 ± 0.04
mise hook-env 19.4 ± 0.5 18.6 23.2 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.12.7 ls 17.0 ± 0.4 16.1 18.0 1.00
mise ls 17.0 ± 0.4 16.3 18.7 1.00 ± 0.03

xtasks/test/perf

Command mise-2025.12.7 mise Variance
install (cached) 107ms 108ms +0%
ls (cached) 65ms 65ms +0%
bin-paths (cached) 71ms 71ms +0%
task-ls (cached) 436ms ⚠️ 2253ms -80%

⚠️ Warning: task-ls cached performance variance is -80%

@jdx jdx enabled auto-merge (squash) December 15, 2025 14:17
Comment thread src/hook_env.rs
@jdx jdx disabled auto-merge December 15, 2025 14:39
The previous fix for alias clearing during deactivation incorrectly
cleared aliases on every hook-env run by adding the clear logic to
`clear_old_env()`. This function is called both during deactivation
AND on every hook-env invocation, causing unchanged aliases to be
cleared unnecessarily.

This fix separates alias clearing into its own `clear_aliases()`
function that is only called from `build_deactivation_script()`,
ensuring aliases are properly maintained during normal hook-env
runs while still being cleaned up during deactivation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment thread src/config/mod.rs
The load_shell_aliases function was iterating over config_files.values()
without calling .rev(), causing parent and global config files to
override child directory configs. This is backwards from the documented
behavior which states "A child directory can override a parent's alias."

This fix adds .rev() to iterate in reverse order (global -> local) so
that child directories properly take precedence, matching the pattern
used by load_env.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) December 15, 2025 15:06
@jdx jdx merged commit ae8c1ec into main Dec 15, 2025
29 checks passed
@jdx jdx deleted the feat/shell-alias branch December 15, 2025 15:16
jdx pushed a commit that referenced this pull request Dec 15, 2025
### 🚀 Features

- **(conda)** add dependency resolution for conda packages by @jdx in
[#7280](#7280)
- **(go)** add created_at support to ls-remote --json by @jdx in
[#7305](#7305)
- **(hook-env)** add hook_env.cache_ttl and hook_env.chpwd_only settings
for NFS optimization by @jdx in
[#7312](#7312)
- **(hooks)** add MISE_TOOL_NAME and MISE_TOOL_VERSION to
preinstall/postinstall hooks by @jdx in
[#7311](#7311)
- **(shell_alias)** add shell_alias support for cross-shell aliases by
@jdx in [#7316](#7316)
- **(tool)** add security field to mise tool --json by @jdx in
[#7303](#7303)
- add --before flag for date-based version filtering by @jdx in
[#7298](#7298)

### 🐛 Bug Fixes

- **(aqua)** support cosign v3 bundle verification by @jdx in
[#7314](#7314)
- **(config)** use correct config_root in tera context for hooks by @jdx
in [#7309](#7309)
- **(nu)** fix nushell deactivation script on Windows by @fu050409 in
[#7213](#7213)
- **(python)** apply uv_venv_create_args in auto-venv code path by @jdx
in [#7310](#7310)
- **(shell)** escape exe path in activation scripts for paths with
spaces by @jdx in [#7315](#7315)
- **(task)** parallelize exec_env loading to fix parallel task execution
by @jdx in [#7313](#7313)
- track downloads for python and java by @jdx in
[#7304](#7304)
- include full tool ID in download track by @jdx in
[#7320](#7320)

### 📚 Documentation

- Switch `postinstall` code to be shell-agnostic by @thejcannon in
[#7317](#7317)

### 🧪 Testing

- **(e2e)** disable debug mode by default for windows-e2e by @jdx in
[#7318](#7318)

### New Contributors

- @fu050409 made their first contribution in
[#7213](#7213)
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.

2 participants