Highly opinionated development environment for AI safety research. ZSH, Tmux, Vim, SSH, and AI coding assistants across macOS, Linux, and cloud containers.
This setup reflects workflows optimized for ML research: reproducibility, experiment tracking, async API patterns, and rigorous methodology. The AI assistant configurations enforce research discipline—interview before planning, plan before implementing, skepticism of surprisingly good results.
Key highlights:
- 🤖 AI Coding Assistants - Extensively configured Claude Code, plus Codex CLI and Gemini CLI support
- 👻 Ghostty - Fast, GPU-accelerated terminal with sensible defaults
- 📊 htop - Dynamic CPU meter configuration that adapts to your core count
- 🦀 Rust-powered CLI tools - Modern, blazing-fast replacements for standard Unix utilities
- 🧹 Automatic cleanup - Scheduled cleanup of Downloads/Screenshots (macOS, moves to trash)
Originally forked from jplhughes/dotfiles - thanks John for the solid foundation!
These modern alternatives are installed by default and significantly faster than their traditional counterparts:
| Tool | Replaces | Why it's better |
|---|---|---|
bat |
cat |
Syntax highlighting, line numbers, git integration |
eza |
ls |
Colors, icons, git status, tree view built-in |
fd |
find |
Intuitive syntax, respects .gitignore, 5x faster |
ripgrep (rg) |
grep |
Recursive by default, respects .gitignore, 10x+ faster |
delta |
diff |
Side-by-side, syntax highlighting, line numbers |
zoxide |
cd |
Learns your habits, jump with z dirname |
dust |
du |
Intuitive visualization of disk usage |
jless |
less (JSON) |
Interactive JSON viewer with vim keybindings |
Extras (--extras flag):
hyperfine— statistical benchmarking with warmup and multiple runslazygit— TUI for gitcode2prompt— generate LLM prompts from codebases
Install dependencies (e.g. oh-my-zsh and related plugins). The installer auto-detects your OS and applies sensible defaults.
# Install with defaults (recommended)
./install.sh
# Install only specific components (--minimal disables defaults)
./install.sh --minimal --tmux --zshDefaults by platform:
| Platform | Defaults |
|---|---|
| macOS | zsh, tmux, AI tools, cleanup + Rust CLI tools via Homebrew |
| Linux | zsh, tmux, AI tools, create-user + Rust CLI tools via mise |
Installation on macOS requires Homebrew - install from brew.sh first if needed.
Deploy configurations (sources aliases for .zshrc, applies oh-my-zsh settings, etc.)
# Deploy with defaults (recommended)
./deploy.sh
# Deploy with extra aliases (useful for remote machines)
./deploy.sh --aliases=speechmatics
# Deploy only specific components (--minimal disables defaults)
./deploy.sh --minimal --vim --claudeDefaults:
- Git config, VSCode/Cursor settings, vim, Claude Code, Codex CLI, Ghostty, htop, matplotlib styles
- Experimental features (ty type checker)
- Cleanup automation (macOS only)
Flags are additive - e.g., ./deploy.sh --aliases=custom deploys defaults + custom aliases. Use --minimal to disable defaults.
This setup includes extensive Claude Code customization optimized for AI safety research:
./deploy.sh --claude # Symlinks claude/ → ~/.claudeWhat's included:
CLAUDE.md- Global instructions enforcing research discipline:- Zero-tolerance rules (no mock data, no fabrication, no destructive git)
- Research methodology (interview → plan → implement, change one variable at a time)
- Performance patterns (async API calls, caching, 100+ concurrent requests)
- Context management (subagents for large files, efficient exploration)
agents/- Specialized subagents for different tasks:code-reviewer,research-engineer,debugger,performance-optimizerexperiment-designer,research-skeptic,data-analystliterature-scout,paper-writer,clarity-critic
skills/- Custom slash commands:/commit,/run-experiment,/spec-interview-research/read-paper,/review-draft,/reproducibility-report
hooks/- Auto-logging to~/.claude/logs/, desktop notifications, file read warningstemplates/- Reproducibility reports, research specs
Smart merge preserves your data - if ~/.claude already exists, credentials, history, and cache are automatically restored after symlinking.
Codex CLI configuration that reuses Claude Code's skills:
./deploy.sh --codex # Symlinks codex/ → ~/.codexWhat's included:
AGENTS.md- Global instructions (references CLAUDE.md as source of truth)config.toml- Model settings and per-project trust levelsskills/- Symlinked to Claude Code's skills for consistency
The configuration follows the same research discipline as Claude Code but adapted for Codex's execution model.
Gemini CLI can sync with Claude Code configurations:
./scripts/sync_claude_to_gemini.sh # Syncs skills/agents/permissionsWhat it does:
- Symlinks Claude Code skills to
~/.gemini/skills/ - Converts Claude agents to Gemini skill format
- Syncs permissions from
.claude/settings.jsonto Gemini policies - Creates
GEMINI.mdpointer to CLAUDE.md
Note: Gemini CLI uses a different skills format. The sync script adapts Claude's configuration but some features may not translate directly.
Ghostty is a fast, GPU-accelerated terminal written in Zig. Config is symlinked to the platform-specific location:
./deploy.sh --ghostty # Part of defaultsKey settings in config/ghostty.conf:
Cmd+Ctriggers shell-based copy (integrates with tmux)Shift+Enterfor multiline input- Sensible font and color defaults
Config location: macOS ~/Library/Application Support/com.mitchellh.ghostty/config, Linux ~/.config/ghostty/config
Launch new Ghostty windows with different color themes - useful for visually distinguishing contexts:
| Alias | Theme | Character |
|---|---|---|
g1 |
Catppuccin Mocha | Warm purple/pink |
g2 |
TokyoNight | Cool blue |
g3 |
Gruvbox Dark | Retro orange/brown |
g4 |
Nord | Arctic icy blue |
g5 |
Dracula | Purple accents |
g6 |
Rose Pine | Muted rose tones |
g1 # Launch Ghostty with Catppuccin Mocha
gtheme "Tomorrow Night" # Launch with any theme
ghostty +list-themes # See all available themesEach alias opens a single fresh window (no tab restoration) with the specified theme.
Terminal colors automatically change when SSH-ing to help identify which machine you're on. Colors revert when the session ends.
ssh myserver # In Ghostty: colors change automatically
sshc myserver # Explicit color-changing SSH (works in any terminal)Configure per-host colors by editing SSH_HOST_COLORS in config/aliases.sh:
# Format: "background:foreground:cursor" in hex
SSH_HOST_COLORS[prod*]="#3d0000:#ffffff:#ff6666" # Red-tinted for production
SSH_HOST_COLORS[dev*]="#002200:#ffffff:#66ff66" # Green-tinted for dev
SSH_HOST_COLORS[gpu*]="#1a0033:#ffffff:#cc66ff" # Purple for GPU servers
SSH_HOST_COLORS[default]="#0d1926:#c5d4dd:#88c0d0" # Blue-gray fallbackPatterns support wildcards (prod* matches prod1, prod-web, etc.). The default key applies to any host without a specific match.
Dynamic htop configuration that adapts CPU meters to your core count:
./deploy.sh --htop # Part of defaultsThe config in config/htop/htoprc uses a dynamic layout that works across machines with different CPU counts—no manual adjustment needed.
High-contrast color scheme for pdb++, the enhanced Python debugger:
./deploy.sh --pdb # Part of defaultsGlobal config works with per-project installations. The config is deployed to ~/.pdbrc.py (symlinked), but pdb++ is installed per-project via uv add --dev pdbpp. This works because pdb++ reads the global config at runtime.
Auto-detects terminal background using OSC 11 escape sequence:
- Light terminals: Dark colors on light background (solarized-light theme)
- Dark terminals: Bright colors on dark background (monokai theme)
- Fallback: Defaults to dark theme if detection fails (SSH, older terminals)
Detection succeeds in modern terminals (iTerm2, Ghostty, Kitty, Alacritty) and fails gracefully elsewhere.
Test it works:
cd /path/to/project
uv add --dev pdbpp
python -c "import pdb; pdb.set_trace()" <<< "c"
# Should show high-contrast colorsPer-project override (advanced): Create .pdbrc.py in project root. It takes precedence over the global config. See pdb++ docs for details.
Scheduled cleanup of old files from ~/Downloads and ~/Screenshots:
./deploy.sh --cleanup # Part of macOS defaultsHow it works:
- Moves files older than 180 days (configurable) to Trash (not permanent delete)
- Runs monthly via launchd
- Only deletes files not accessed AND not modified in retention period
# Preview what would be cleaned
./scripts/cleanup/cleanup_old_files.sh --dry-run
# Custom retention (90 days) and schedule (weekly)
./scripts/cleanup/install.sh --days 90 --schedule weeklySee scripts/cleanup/README.md for full documentation.
Automatically kills idle Claude Code processes daily at 17:00:
./deploy.sh --claude-cleanup # Part of defaults (both macOS and Linux)How it works:
- Only kills processes with no output activity for 24h (preserves active + tmux sessions)
- Runs daily via launchd (macOS) or cron (Linux)
- Manual control via
clear-claude-codecommand (aliases:ccl,cci,ccf)
# Check status
clear-claude-code --list
# Uninstall
./scripts/cleanup/setup_claude_cleanup.sh --uninstallAutomatically sync secrets with GitHub gist daily at 08:00:
./deploy.sh --secrets # Part of defaultsHow it works:
- Bidirectional sync with GitHub gist (SSH config, authorized_keys, git identity)
- Auto-adds local public key to
authorized_keys(enables SSH between your machines) - Last-modified wins: compares local vs gist timestamps
- Requires
gh auth login(run once for authentication) - Runs daily via launchd (macOS) or cron (Linux)
# Manual sync
sync-secrets
# Uninstall automation
./scripts/cleanup/setup_secrets_sync.sh --uninstallPowerlevel10k provides a fast, feature-rich ZSH prompt. This config includes custom segments for SSH-aware machine identification.
Requirements: Install a Nerd Font for icons.
Reconfigure: Run p10k configure (when prompted, overwrite p10k.zsh but don't apply to .zshrc).
| Segment | Description |
|---|---|
| Remote host | Machine name + emoji (SSH sessions only) |
| Directory | Current path with git root highlighting |
| Git status | Branch, dirty indicator, stash count |
| Right side | Exit code, command duration, Python venv, cloud contexts |
When SSH'd to a remote machine, the prompt shows a consistent machine name derived from your SSH config:
🛜 mats ~/code/project (main) # Instead of: user@ip-172-31-42-17
How it works:
- Looks up your public IP against
~/.ssh/configHostNameentries - Uses the matching
Hostalias as the display name - Falls back to abbreviated hostname if no match
Example SSH config:
Host mats
HostName 203.0.113.42
User yulong
Host hetzner-gpu
HostName 198.51.100.10
User root
SSH to 203.0.113.42 → prompt shows 🛜 mats instead of the IP or hostname.
Customization:
SERVER_NAMEenv var overrides everythingMACHINE_EMOJIenv var changes the icon (default: 🛜)
Claude Code displays a custom statusline with session info. Configuration: claude/statusline.sh
🛜 mats ~/code/project (main*) +12,-3 · 📊 45% · $0.23
│ │ │ │ │ └─ Session cost
│ │ │ │ └─ Context usage (color-coded)
│ │ │ └─ Git insertions/deletions
│ │ └─ Branch (* = dirty)
│ └─ Directory
└─ Machine name (SSH only, same as p10k)
Features:
- Machine name: Uses same
machine-namescript as Powerlevel10k for consistency - Git info: Branch with dirty indicator, line change stats
- Context %: Color-coded usage (green <70%, yellow 70-89%, red 90%+)
- Cost: Running session total in USD
Both the shell prompt and Claude Code statusline use your SSH config aliases, so machine identification is consistent across tools.
Automatically adds your SSH key to ssh-agent on shell startup:
# Automatically enabled when you deploy ZSH config
./deploy.sh # (default: includes ZSH)How it works:
- Checks for
~/.ssh/id_ed25519(customizable viaSSH_KEY_PATHenv var) - Prompts to generate if key doesn't exist (never overwrites existing keys)
- Adds to macOS Keychain (
--apple-use-keychain) or Linux ssh-agent - Only runs in interactive shells
- Skips if key already loaded in agent
First-time setup flow:
- Shell starts → detects no key → prompts "Generate a new ed25519 SSH key now? [y/N]"
- If yes → generates key → shows command to copy public key
- Automatically adds to agent on this and future shell sessions
Custom key path:
export SSH_KEY_PATH=~/.ssh/id_rsa # Use RSA key insteadConfiguration: config/ssh_setup.sh
- Any software or command line tools you need, add them to the install.sh script. Try adding a new command line tool to the install script.
- Any new plugins or environment setup, add them to the config/zshrc.sh script.
- Any aliases you need, add them to the config/aliases.sh script. Try adding your own alias to the bottom of the file. For example, try setting
cd1to your most used git repo so you can just typecd1to get to it.
One-command setup for cloud VMs and containers:
# RunPod (fresh pod, as root)
curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | bash
# After pod restart (recreates user entry)
curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/restart.sh | bash
# Hetzner / standard VPS (persistent /home)
curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | PERSISTENT=/home bashThen SSH as yulong@<ip> (not root). See scripts/cloud/README.md for details.
What it does:
- Creates non-root user in persistent storage (
/workspace/yulongon RunPod) - Installs uv, dotfiles, Claude Code
- Copies SSH keys for direct access