---
name: shell-profiler
description: Diagnostic skill that profiles shell startup time, identifies bottlenecks from tool managers (NVM, pyenv, rbenv, conda), and provides lazy-loading optimizations. Use this skill when Claude Code Bash tool calls feel slow or a user wants to optimize their shell startup.
---

# Shell Profiler Skill

**Version**: 1.0.0
**Category**: Performance
**Type**: Diagnostic (script-assisted, prompt-driven analysis)
**Session Duration**: 2-5 minutes

## The Problem

Every time Claude Code invokes the Bash tool, it spawns a new shell process. That shell sources the user's profile files (`.zshrc`, `.bashrc`, `.bash_profile`, `.zprofile`), which often load heavyweight tool managers:

- **NVM** (Node Version Manager): typically adds 1-3 seconds
- **pyenv**: typically adds 0.5-2 seconds
- **rbenv**: typically adds 0.3-1 second
- **conda/miniconda**: typically adds 1-4 seconds
- **Homebrew completions**: typically adds 0.5-1.5 seconds
- **Oh My Zsh plugins**: varies widely, 0.5-5+ seconds

For a user with NVM + pyenv + Oh My Zsh, every single Bash tool call can take 3-8 seconds of pure shell initialization overhead before the actual command runs. Over a coding session with hundreds of Bash calls, this compounds into minutes of wasted time.

**Target**: Sub-1 second non-interactive shell startup. Reference: David Hatch documented an 8s to 500ms optimization.

## When to Use This Skill

- User reports Claude Code feels slow or laggy
- User asks about shell startup optimization
- User invokes `/afx-profile-shell`
- You observe that simple Bash commands (like `echo hello`) take multiple seconds

## Execution Flow

### Step 1: Run the Profiling Script

Execute the bundled profiling script:

```bash
bash ~/.claude/skills/afx-shell-profiler/afx-profile-shell.sh
```

This script will:
1. Measure overall startup times for both zsh and bash
2. Identify the user's default shell
3. Report the top slowest initializations
4. Detect known heavy tool managers

Present the output to the user clearly. If the startup time is already under 1 second, congratulate them and note there is little to optimize.

### Step 2: Analyze Shell Profile Files

Read the user's shell configuration files to understand what is being loaded:

**For zsh users**, read (if they exist):
- `~/.zshrc`
- `~/.zprofile`
- `~/.zshenv`

**For bash users**, read (if they exist):
- `~/.bashrc`
- `~/.bash_profile`
- `~/.profile`

Identify and categorize each heavy initialization block:

| Pattern | Tool | Typical Cost |
|---------|------|-------------|
| `export NVM_DIR` / `nvm.sh` | NVM | 1-3s |
| `pyenv init` | pyenv | 0.5-2s |
| `rbenv init` | rbenv | 0.3-1s |
| `conda init` / `conda.sh` | Conda | 1-4s |
| `eval "$(brew shellenv)"` + completions | Homebrew | 0.5-1.5s |
| `source $ZSH/oh-my-zsh.sh` with many plugins | Oh My Zsh | 0.5-5s |
| `eval "$(starship init zsh)"` | Starship prompt | 0.1-0.5s |
| `eval "$(direnv hook zsh)"` | direnv | 0.1-0.3s |
| `source ~/.rvm/scripts/rvm` | RVM | 0.5-2s |

### Step 3: Present Findings

Summarize what was found in a clear report:

```
SHELL STARTUP PROFILE
=====================

Default shell: [zsh/bash]
Current startup time: [X.XXs]
Target: < 1.0s

Detected bottlenecks:
  1. [tool] — ~[X.X]s — [file:line]
  2. [tool] — ~[X.X]s — [file:line]
  3. [tool] — ~[X.X]s — [file:line]

Estimated savings: ~[X.X]s
Projected startup: ~[X.X]s
```

### Step 4: Suggest Optimizations

For each detected bottleneck, provide a specific lazy-loading replacement. Present the suggestions as concrete code blocks the user can adopt.

#### NVM Lazy Loading

Replace the standard NVM initialization:

```bash
# BEFORE (slow — runs on every shell start):
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

# AFTER (fast — loads NVM only when needed):
export NVM_DIR="$HOME/.nvm"
# Add NVM's current node to PATH without loading NVM
[ -s "$NVM_DIR/alias/default" ] && PATH="$NVM_DIR/versions/node/$(cat $NVM_DIR/alias/default)/bin:$PATH"

nvm() {
  unset -f nvm node npm npx
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  nvm "$@"
}

node() {
  unset -f nvm node npm npx
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  node "$@"
}

npm() {
  unset -f nvm node npm npx
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  npm "$@"
}

npx() {
  unset -f nvm node npm npx
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  npx "$@"
}
```

#### pyenv Lazy Loading

```bash
# BEFORE (slow):
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

# AFTER (fast — adds pyenv shims to PATH, defers full init):
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"

pyenv() {
  unset -f pyenv
  eval "$(command pyenv init -)"
  eval "$(command pyenv virtualenv-init -)" 2>/dev/null
  pyenv "$@"
}
```

#### rbenv Lazy Loading

```bash
# BEFORE (slow):
eval "$(rbenv init -)"

# AFTER (fast — adds rbenv shims to PATH, defers full init):
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"

rbenv() {
  unset -f rbenv
  eval "$(command rbenv init -)"
  rbenv "$@"
}
```

#### Conda Lazy Loading

```bash
# BEFORE (slow — conda init block):
# >>> conda initialize >>>
# ... large block ...
# <<< conda initialize <<<

# AFTER (fast — defer until needed):
__conda_setup_lazy() {
  unset -f conda activate deactivate
  local conda_path="${HOME}/miniconda3"  # Adjust to your conda install path
  eval "$("${conda_path}/bin/conda" "shell.$(basename $SHELL)" hook 2>/dev/null)"
}

conda() { __conda_setup_lazy; conda "$@"; }
activate() { __conda_setup_lazy; activate "$@"; }
deactivate() { __conda_setup_lazy; deactivate "$@"; }
```

#### Homebrew Completions

```bash
# BEFORE (slow — loads all completions at startup):
if type brew &>/dev/null; then
  FPATH="$(brew --prefix)/share/zsh/site-functions:${FPATH}"
  autoload -Uz compinit && compinit
fi

# AFTER (fast — hardcode the prefix, lazy compinit):
FPATH="/opt/homebrew/share/zsh/site-functions:${FPATH}"

# Lazy compinit — only rebuild once a day
autoload -Uz compinit
if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then
  compinit
else
  compinit -C  # Use cached completions
fi
```

#### Oh My Zsh Plugin Reduction

```bash
# BEFORE (slow — loading many plugins):
plugins=(git nvm pyenv rbenv docker kubectl aws gcloud terraform vault)

# AFTER (fast — keep only lightweight plugins, lazy-load the rest):
plugins=(git)
# NVM, pyenv, rbenv: use lazy-loading patterns above instead of OMZ plugins
# Docker/kubectl completions: load on demand or use cached compinit
```

### Step 5: Offer to Apply and Verify

After presenting recommendations:

1. **Ask** which optimizations the user wants to apply
2. **Create a backup** of the original profile file before making changes:
   ```bash
   cp ~/.zshrc ~/.zshrc.backup.$(date +%Y%m%d%H%M%S)
   ```
3. **Apply** the selected optimizations by editing the profile file
4. **Run the profiling script again** to measure improvement:
   ```bash
   bash ~/.claude/skills/afx-shell-profiler/afx-profile-shell.sh
   ```
5. **Report** the before/after comparison:
   ```
   OPTIMIZATION RESULTS
   ====================
   Before: [X.XX]s
   After:  [X.XX]s
   Saved:  [X.XX]s ([XX]% improvement)
   ```

## Important Notes

- **Always back up** before modifying shell profiles. Never overwrite without a backup.
- **Test that tools still work** after applying lazy loading. Run `node --version`, `python --version`, etc. to verify.
- **Non-interactive vs interactive**: Claude Code Bash tool calls run in non-interactive mode. Some profile files are only sourced for interactive shells. Focus on the files that affect non-interactive startup.
- **Do not remove tools**: The goal is lazy loading, not removal. Every tool should still work -- it just loads on first use instead of every shell startup.
- **macOS specifics**: On macOS, zsh is the default shell. Terminal.app sources `.zprofile` for login shells. iTerm2 behavior may differ. Claude Code spawns non-login, non-interactive shells by default.

## When NOT to Use This Skill

- Shell startup is already under 1 second
- The user is experiencing slowness unrelated to shell startup (network, disk, large repos)
- The user prefers not to modify their shell configuration
