Skip to content

Lesson 03 · 24 in the route · 4 min read · Level 1

Homebrew and the shell

Homebrew, Claude Code, gh, and a PATH that also works over SSH.

The story behind this lesson Homebrew and the shell file that decides whether SSH works

Instalar Homebrew en una Mac es de las cosas más rutinarias que existen. Lo que no es rutinario es instalarlo pensando en que el que va a usar esas herramientas no soy yo sentado frente a la Terminal, sino un agente que entra por SSH. Ese cambio de punto de vista me enseñó una diferencia de zsh que no conocía. Los comandos exactos están en el paso Homebrew y shell de la guía.

La instalación

El instalador oficial de Homebrew instala por su cuenta las Xcode Command Line Tools si faltan, y pide la contraseña de administrador. Esa parte la hice yo a mano. Es una regla de esta serie: todo lo que pide contraseña lo hago yo, no un agente.

Después instalé Claude Code con su instalador nativo y gh, el cliente de GitHub. Y ahí vino el primer tropiezo: claude: command not found justo después de instalarlo. El instalador deja el binario en ~/.local/bin, que no está en el PATH.

El concepto: qué archivo lee zsh y cuándo

La solución obvia es agregar el PATH al ~/.zshrc. Es lo que hace casi toda la documentación. Pero tiene una trampa que solo se ve cuando llega un comando remoto.

zsh lee archivos distintos según cómo se abra:

ArchivoTerminal de la Macssh mac "comando"
~/.zshenvsísí
~/.zprofilesí (es login)no
~/.zshrcsí (es interactiva)no

Cuando ejecuto ssh mac "comando", zsh arranca como shell no interactivo y no-login. Ese tipo de shell solo lee ~/.zshenv. Si el PATH está en .zshrc, todo funciona en la Terminal de la Mac y todo falla por SSH con command not found. Es un fallo que engaña: pruebas sentado en la Mac, ves que funciona y concluyes que está bien.

Por eso mi ~/.zshenv tiene dos líneas: una que agrega ~/.local/bin al PATH y otra con brew shellenv. La prueba de que quedó bien fue correr desde Windows ssh mac 'command -v git jq tmux shellcheck gh' y ver que cada herramienta se resolvía a su ruta en /opt/homebrew/bin.

Herramientas base, sin reinstalar nada

Los scripts que despachan trabajo necesitan jq, tmux, coreutils, un bash moderno y shellcheck. Si falta una, la tarea falla a mitad de camino y sin nadie mirando. Por eso, antes de instalar, comprobé qué había con brew list --versions. En mi Mac ya estaban todas, así que no instalé nada. Instalar solo lo que falta es lo que hace repetible el procedimiento: otra persona puede correr los mismos comandos y no pisa lo que ya tiene.

Dos avisos más que conviene conocer:

  • brew trust. Para instalar deskflow hubo que confiar explícitamente en su tap de terceros.
  • brew doctor avisó que las Command Line Tools estaban desactualizadas. Se arregla desde Actualización de software, y es el tema del post 05.

Carpetas simétricas

Creé en la Mac la misma estructura de carpetas que tengo en Windows: Documents/Portfolio/<repo>. Varias herramientas resuelven rutas relativas a esa carpeta, y mantener las dos máquinas simétricas me ahorra una clase entera de errores.

El login que caducó

El último tropiezo de esta etapa fue que gh auth status en la Mac decía “The token in default is invalid”. En ese momento lo tomé por un token caducado. Más adelante resultó ser otra cosa: el token estaba guardado en el llavero de macOS, al que una sesión SSH no puede entrar. Esa historia es la parte central de Una noche con un agente desatendido.

Lo que me llevo

Si una herramienta tiene que funcionar para quien llega por SSH, el PATH va en ~/.zshenv. Y cuando pruebes algo en un nodo remoto, pruébalo como lo va a usar el agente: con ssh mac "comando", no sentado en la Terminal.

Siguiente: la llave SSH, y el error que cometí por pegar un bloque de comandos que tenía una contraseña de por medio.

Goal

The Mac with Homebrew, Claude Code, gh and the base tools, all visible to commands that arrive over SSH too (ssh mac 'git --version' answers, it does not say command not found).

Why

Homebrew is the de facto package manager on macOS. The base tools (jq, tmux, coreutils, a modern bash, shellcheck) are the ones used by the scripts that dispatch work: if one is missing, the task fails halfway through with nobody watching.

And one concept decides whether everything works over SSH: zsh reads different files depending on how it is opened. A remote command (ssh mac "cmd") opens a non-interactive, non-login zsh, which only reads ~/.zshenv:

FileMac terminalssh mac "command"
~/.zshenvyesyes
~/.zprofileyes (it is a login shell)no
~/.zshrcyes (it is interactive)no

If the PATH lives in .zshrc, everything works in the Mac’s Terminal and everything fails over SSH.

Steps

1. Homebrew (by hand: it asks for your password)

mac$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

The official installer installs the Xcode Command Line Tools on its own if they are missing, and asks for the password (sudo). When it finishes it prints some “Next steps” lines: do not paste them yet, read step 3 first.

2. Claude Code and GitHub CLI

mac$ curl -fsSL https://claude.ai/install.sh | bash
mac$ brew install gh

The gh login is done with a browser, in the Mac’s Terminal (not over SSH):

mac$ gh auth login -h github.com

(capture of the real setup: not published; see docs/CAPTURAS.md for the demo-mode ones)

The screenshot shows HTTPS, Login with a web browser, a one-time code (covered) and Logged in as …. The last thing it prints is “You were already logged in to this account”: there was a previous login. See “What actually went wrong” for why this matters.

3. The PATH goes in ~/.zshenv

mac$ cat ~/.zshenv
export PATH="$HOME/.local/bin:$PATH"
eval "$(/opt/homebrew/bin/brew shellenv zsh)"

Test, from Windows, that it came out right:

PS> ssh mac 'command -v git jq tmux shellcheck gh'
/opt/homebrew/bin/git
/opt/homebrew/bin/jq
/opt/homebrew/bin/tmux
/opt/homebrew/bin/shellcheck
/opt/homebrew/bin/gh

4. Base tools (idempotent: check first)

PS> ssh mac 'brew list --versions git jq bash coreutils tmux shellcheck gh'
PS> ssh mac 'brew list --cask --versions visual-studio-code ghostty deskflow'

If any is missing: brew install git jq bash coreutils tmux shellcheck and brew install --cask visual-studio-code ghostty. On this Mac they were all already there:

(capture of the real setup: not published; see docs/CAPTURAS.md for the demo-mode ones)

5. Symmetric folders

PS> ssh mac 'mkdir -p ~/Documents/Portfolio'

The Mac uses the same structure as Windows (Documents/Portfolio/<repo>), because several tools resolve paths relative to that folder.

What actually went wrong

  1. claude: command not found right after installing Claude Code. The installer puts it in ~/.local/bin, which was not in the PATH. Fix: PATH and brew shellenv in ~/.zshenv, not in .zshrc (see “Why”).
  2. brew install deskflow → “Refusing to load cask from untrusted tap”. Homebrew requires explicitly trusting third-party taps: brew trust deskflow/tap and then install.
  3. brew doctor: “Your Command Line Tools are too outdated”. Fixed from Settings → General → Software Update (it installs the new CLT version). It came back after updating macOS: see 06-actualizar-macos.md.
  4. gh auth status over SSH: “The token in default is invalid”. Session 1 diagnosed it as “the login expired”. Later it looked like it was probably something else: the token was fine, but stored in the macOS keychain, which an SSH session cannot open. Evidence: screenshot 07 shows a successful gh auth login on the Mac before that session, and later gh auth login answered “you were already logged in”. That the earlier login is the cause of the “invalid” is an inference; what is measured (in the overnight run) is the keychain lock over SSH. Details and options in 09-agente-desatendido.md.

How to verify

PS> ssh mac 'command -v git jq tmux shellcheck gh'

It must print five paths under /opt/homebrew/bin/, and

PS> ssh mac 'claude --version'
2.1.289 (Claude Code)

(Versions as of 2026-10-04: Homebrew 7.0.7, Claude Code 2.1.289.)

Self-check

  1. Why does the PATH go in ~/.zshenv and not in ~/.zshrc?
    Answer`ssh mac "cmd"` opens a non-interactive, non-login zsh, which only reads `~/.zshenv`.
  2. What does “idempotent” mean in step 4?
    AnswerYou check before installing and, if it is already there, leave it alone: running it twice gives the same result as running it once.
  3. gh auth login works in the Mac’s Terminal but fails over SSH. Where do you look?
    AnswerAt where the token was stored: the macOS keychain does not open from an SSH session.
Saved only in this browser.