Toast when the teamclaude proxy switches account (the status line itself is claude/.claude/hooks/statusline.sh)

Cross-platform dotfiles for Ubuntu/WSL, Fedora/Bazzite, Arch/Omarchy and macOS, organized as GNU Stow packages.
Most top-level folders are a Stow package: running stow <package> symlinks its contents into $HOME, preserving the internal directory layout. packages/, scripts/, docs/ and system/ are the exceptions — plain utility/reference dirs, not linked by Stow.
dotfiles/
├── bash/ # .bashrc + .inputrc + .config/bash/*.sh modules (alternative shell)
├── starship/ # .config/starship.toml (prompt shared by bash and zsh)
├── zsh/ # .zshrc (slim loader) + .config/zsh/*.zsh modules
├── git/ # .gitconfig + .config/git/ignore
├── nvim/ # .config/nvim/ (LazyVim distribution)
├── tmux/ # .config/tmux/tmux.conf
├── lazygit/ # .config/lazygit/config.yml (terminal UI for git)
├── shell/ # .profile
├── claude/ # .claude/ (Claude Code settings, agents, global CLAUDE.md; shared across profiles)
├── clef/ # systemd --user units + clef-server for the local decision engine (Linux; docs/decision-engine.md)
├── vscode/ # settings.json (symlinked per-OS by a script)
├── wsl/ # .wslconfig (copied to the Windows profile on WSL)
├── packages/ # package lists (brew/apt/dnf/node/vscode extensions)
├── scripts/ # bootstrap + install/sync helpers
│ ├── lib/ # shared utilities (log, os_detect, sudo keep-alive…)
│ └── modules/ # one installable/optional step per file, sourced by bootstrap.sh
├── system/ # non-$HOME system files (/etc/…), installed by a script, not Stow
├── docs/ # setup guides (macOS, shell, git/ssh, lazygit, …)
└── README.md
The zsh config is modular: zsh/.zshrc is a slim loader that sources focused files from ~/.config/zsh/ (exports, path, plugins, completion, history, keybindings, aliases, functions, integrations). See docs/shell-and-dotfiles.md.
git and stow (the bootstrap installs stow for you if missing):
sudo apt install git stow # Ubuntu/WSL
sudo dnf install git stow # Fedora (Bazzite ships both in the base image)
sudo pacman -S git stow # Arch/Omarchy
brew install git stow # macOS
Clone the repo and run the single bootstrap entry point. It detects the OS, installs base packages, stows the config packages, syncs VS Code, installs global node packages, and applies OS-specific extras (macOS defaults / WSL .wslconfig).
git clone git@github.com:savaf/dotfiles.git ~/dotfiles
cd ~/dotfiles
./scripts/bootstrap.sh
The bootstrap backs up any conflicting real files to ~/.dotfiles-backup/<timestamp>/ before linking.
To link (or unlink) individual packages yourself:
cd ~/dotfiles
stow bash zsh git starship nvim tmux shell lazygit claude # link everything
stow nvim # link just one package
stow -D nvim # unlink (remove symlinks)
stow -R zsh # restow (refresh) after changes
Reproducible package manifests live in packages/:
xargs brew install < packages/brew-cli.txt # macOS CLI
xargs brew install --cask < packages/brew-casks.txt # macOS apps
sudo xargs -a packages/apt-cli.txt apt install -y # Ubuntu/WSL CLI
grep -v '^#' packages/dnf-cli.txt | xargs sudo dnf install -y # Fedora CLI (Bazzite: rpm-ostree install)
grep -v '^#' packages/pacman-cli.txt | xargs sudo pacman -S --needed # Arch/Omarchy CLI
VS Code settings (vscode/settings.json) are symlinked to the OS-specific location by scripts/sync-vscode-settings.sh, and extensions are installed from packages/vs-extensions.txt. See docs/vscode.md.
.wslconfig is read by Windows from your Windows user profile, not the Linux $HOME. The bootstrap copies it to C:\Users\<you>\.wslconfig; apply changes with wsl --shutdown.starship/.config/starship.toml.lzg. See docs/lazygit.md.Step-by-step guides for a fresh machine live in docs/:
| Guide | What it covers |
|---|---|
| shell-and-dotfiles.md | zsh modules, prompt, and applying the dotfiles with Stow |
| ubuntu-wsl.md | Ubuntu & WSL2 setup from scratch (incl. .wslconfig, fonts) |
| bazzite.md | Fedora & Bazzite (immutable) setup — dnf / rpm-ostree |
| omarchy.md | Arch & Omarchy (Hyprland) setup — pacman, default shell (zsh), bash alternative |
| macos-setup.md | macOS system preferences, apps, Homebrew, iTerm2 |
| git-and-ssh.md | Git defaults and GitHub SSH key setup |
| nodejs.md | Node.js via nvm and global modules |
| vscode.md | VS Code settings and extensions |
| nvim.md | Neovim (LazyVim) setup, requirements and basics |
| tmux.md | tmux prefix, panes/windows and copy mode |
| lazygit.md | lazygit keybindings and workflows |
| hardware.md | My machines, for reference |
| storage.md | Games / LIBRARY disks: fstab, mountpoints, Kavita |
hooks/register.ts 36 lines1import type { Register } from 'claude-code'
2
3// Short labels per account email domain; anything else shows the part before "@".
4const LABELS: Record<string, string> = { 'gmail.com': 'personal', 'urbn.com': 'ccw' }
5const POLL_MS = 10_000
6
7type Account = { name: string }
8type Status = { currentAccount?: string; accounts?: Account[] }
9
10const label = (name: string) => LABELS[name.split('@')[1] ?? ''] ?? name.split('@')[0]
11
12export const register: Register = on => {
13 on('session.start', async ($, e, next) => {
14 const base = await $.env.get('ANTHROPIC_BASE_URL')
15 // Solo sesiones por el proxy; la barra de estado la dibuja statusline.sh.
16 if (base && /:345[67]\/?$/.test(base)) {
17 let last: string | undefined
18 const poll = async () => {
19 try {
20 const res = await $.http.fetch(`${base.replace(/\/$/, '')}/teamclaude/status`)
21 const s = JSON.parse(res.text) as Status
22 const a = s.accounts?.find(x => x.name === s.currentAccount)
23 // Failover: avisa cuando el proxy cambia de cuenta (el tráfico pasa a otra org).
24 if (a && last && last !== a.name) $.ui.toast(`Proxy: ahora usando ${label(a.name)}`)
25 last = a?.name
26 } catch {
27 // proxy caído: statusline.sh ya lo muestra; aquí no hay nada que avisar
28 }
29 }
30 void poll()
31 $.clock.every(POLL_MS, () => void poll())
32 }
33 return next(e)
34 })
35}
36