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!
This project offers two quickstart paths: Local and Cloud.
For setting up on your personal machine (macOS, Linux, desktop/laptop):
git clone https://github.com/yulonglin/dotfiles.git && cd dotfiles
# 1. Install dependencies (zsh, tmux, CLI tools, AI assistants)
./install.sh
# 2. Deploy configurations (symlinks, shell config, secrets, automation)
./deploy.sh
# 3. Restart your shell
source ~/.zshrcinstall.shinstalls required software.deploy.shdeploys config files and settings.- Both scripts are idempotent and safe to re-run.
All configuration options are stored in config.sh. Flags are additive (e.g., --mouseless adds that feature to defaults). Use --minimal to disable most options.
For cloud environments (RunPod, Hetzner, Lambda Labs, etc):
- SSH into your new remote machine as root.
- Run the one-liner:
This creates a non-root user, installs dependencies, clones dotfiles, and runs
# RunPod (fresh pod) curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | bash # Hetzner / standard VPS (persistent /home) curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | USER_HOME=/home bash
install.sh+deploy.shautomatically. It will prompt for GitHub auth and an optional age key (for encrypted secrets). - Reconnect as your user:
ssh yulong@<ip>
- (Optional) After pod restart (RunPod recreates
/etc/passwd):curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/restart.sh | bash - (Optional) Customize components:
Edit
config.shto disable resource-intensive options (AI assistants, cleanup automation, etc.) before running install/deploy.
Tip: The setup auto-detects cloud providers and adjusts accordingly (persistent storage paths, SSH config, no macOS-only features). See scripts/cloud/README.md for details.
- Quickstart
- Adopting These Dotfiles
- Rust CLI Tools
- Installation
- AI Assistants
- Terminal & Shell
- Dev Tools
- Secrets & Security
- Automation
- Cloud Setup
- Getting to Know These Dotfiles
This repo is highly personal — it reflects one person's workflow, opinions, and tooling choices. The best way to use it is to point a coding agent at this repo and ask it to extract the parts you find useful into your own dotfiles.
What's generalizable vs personal:
| Generalizable (worth extracting) | Personal (skip or replace) |
|---|---|
| Shell config (zsh/tmux/p10k) | Claude Code plugins/agents/skills |
| Modern CLI tools (bat, eza, fd, rg, etc.) | Website alias, SSH host colors |
| Git config + global gitignore/gitattributes | Mouseless config |
| Editor settings (VSCode/Cursor merge logic) | Ghostty theme aliases |
| Cleanup automation (Downloads/Screenshots) | Specific API keys and gist IDs |
| Gist sync (bidirectional SSH config/identity sync) | Cloud setup scripts (RunPod user) |
| SOPS + age encrypted secrets workflow | Plugin marketplace selections |
All personal values are centralized in [config.sh](./config.sh) — edit DOTFILES_USERNAME, DOTFILES_REPO, GIST_SYNC_ID, GIT_USER_NAME, and GIT_USER_EMAIL to make it yours.
These modern alternatives are installed by default and significantly faster than their traditional counterparts:
| Tool | Replaces | Why it's better |
|---|---|---|
[bat](https://github.com/sharkdp/bat) |
cat |
Syntax highlighting, line numbers, git integration |
[eza](https://github.com/eza-community/eza) |
ls |
Colors, icons, git status, tree view built-in |
[fd](https://github.com/sharkdp/fd) |
find |
Intuitive syntax, respects .gitignore, 5x faster |
[ripgrep](https://github.com/BurntSushi/ripgrep) (rg) |
grep |
Recursive by default, respects .gitignore, 10x+ faster |
[delta](https://github.com/dandavison/delta) |
diff |
Side-by-side, syntax highlighting, line numbers |
[zoxide](https://github.com/ajeetdsouza/zoxide) |
cd |
Learns your habits, jump with z dirname |
[dust](https://github.com/bootandy/dust) |
du |
Intuitive visualization of disk usage |
[jless](https://github.com/PaulJuliusMartinez/jless) |
less (JSON) |
Interactive JSON viewer with vim keybindings |
Extras (--extras flag):
[hyperfine](https://github.com/sharkdp/hyperfine)— statistical benchmarking with warmup and multiple runs[gitui](https://github.com/extrawurst/gitui)— TUI for git[code2prompt](https://github.com/mufeedvh/code2prompt)— 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
./install.sh --minimal --tmux --zsh # --minimal disables all defaultsDefaults 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.). All settings live in [config.sh](./config.sh) — edit once, deploy everywhere.
# Deploy with defaults (recommended)
./deploy.sh
# Profiles
./deploy.sh --profile=server # Safe base for shared machines
./deploy.sh --profile=minimal # Nothing enabled — specify what you want
# Deploy only specific components
./deploy.sh --only vim claude # Only vim and claude, nothing else
# Add to defaults
./deploy.sh --mouseless # Defaults + mouselessDefault components:
- Shell: ZSH, tmux, vim, Powerlevel10k
- Editors: VSCode/Cursor settings (merged, not overwritten),
.editorconfig,.curlrc,.inputrc - AI tools: Claude Code, Codex CLI, Ghostty terminal
- Git: gitconfig, global gitignore/gitattributes, global git hooks (secret detection)
- Dev tools: htop, pdb++, matplotlib styles
- Secrets: GitHub gist sync, SOPS-encrypted API keys
- Automation: file cleanup (macOS), Claude Code session cleanup, AI tools auto-update, package auto-update, keyboard repeat enforcement (macOS)
Flags are additive — e.g., ./deploy.sh --mouseless deploys defaults + mouseless. Use --minimal to disable all defaults, then specify only what you want.
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 warnings**templates/**- Reproducibility reports, research specs
Smart merge preserves your data - if ~/.claude already exists, credentials, history, and cache are automatically restored after symlinking.
Claude Code supports community plugin marketplaces. These are worth exploring independently:
| Marketplace | What's in it |
|---|---|
| superpowers (official) | TDD, brainstorming, code review, agent teams, worktree workflows |
| ui-ux-pro-max | 50 design styles, 21 palettes, production-grade frontend |
| ai-safety-plugins | Research experiments, paper writing, literature review |
| productivity-tools | Hookify, plugin dev tools |
Profiles are managed via the claude-tools context CLI — compose multiple profiles to control which plugins load per-project:
claude-tools context code # Software projects
claude-tools context code frontend python # Compose multiple profiles
claude-tools context --list # Show active plugins and available profilesCodex 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 levels**skills/**- 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.
Powerlevel10k 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
Each machine gets a unique emoji based on its name hash, so you can visually distinguish machines at a glance.
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
- Hashes the name to assign a stable emoji from a curated palette
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 overrides the auto-assigned emoji
Claude Code displays a custom statusline with session info. Configuration: claude/settings.json (statusLine.command = "claude-tools statusline").
🌊 mats [code python] ~/code/project (main*) · 📊 45% · $0.23 · 12m
│ │ │ │ │ │ └─ Session duration
│ │ │ │ │ └─ Session cost
│ │ │ │ └─ Context usage (color-coded)
│ │ │ └─ Branch (* = dirty)
│ │ └─ Active Claude context profiles
│ └─ 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
- Context %: Color-coded usage (green <70%, yellow 70-89%, red 90%+)
- Cost: Running session total in USD
- Duration: Session runtime in minutes/hours
ccusage statusline is not wired into the live Claude hook path because it can OOM on large local histories; guard logic still uses lightweight ccusage blocks --active --json where available.
Both the shell prompt and Claude Code statusline use your SSH config aliases, so machine identification is consistent across tools.
claude-tools ignore manages per-repo .gitignore and .ignore patterns interactively.
claude-tools ignore # Launch TUI (same as `ignore apply`)
claude-tools ignore apply # Interactive pattern selection
claude-tools ignore apply --dry-run # Preview without writing
claude-tools ignore apply --non-interactive # Apply defaults without TUI
claude-tools ignore status # Show current managed patternsThe TUI shows patterns grouped by category with tri-state toggles:
[ ]skip — pattern not applied[ G ]gitignore — added to.gitignoreonly[G+S]gitignore + searchable — added to.gitignoreAND negated in.ignore
Patterns in [G+S] state are git-ignored but remain searchable by rg, fd, Claude Code, and Cursor. Pattern definitions live in config/ignore/patterns.
Codex uses built-in status items configured in codex/config.toml under [tui].status_line (for example: model, current dir, git branch, weekly/5h limits, context remaining).
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](config/ssh_setup.sh)
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](./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 --uninstallDaily automatic updates for Claude Code, Gemini CLI, and Codex CLI at 06:00:
./deploy.sh --ai-update # Part of defaultsRuns via launchd (macOS) or cron (Linux). Uninstall with scripts/cleanup/setup_ai_update.sh --uninstall.
Weekly package upgrade + cleanup on Sundays at 05:00:
./deploy.sh --brew-update # Part of defaultsSupports Homebrew (macOS), apt, dnf, and pacman (Linux). Includes cleanup of stale caches.
Bidirectional sync between macOS text replacements and Alfred snippets. Runs daily at 09:00:
./deploy.sh --text-replacements # macOS only, part of defaultsmacOS uses raw shortcuts; Alfred applies a collection prefix at runtime (e.g., fm.hi). Requires Full Disk Access for your terminal app.
SOPS (Secrets OPerationS, by Mozilla) encrypts file values while keeping keys/structure visible — you can git diff and review encrypted files. age provides the keypair (modern, simple alternative to PGP). Works offline, git-versioned, no service dependency.
How it works:
age keypair (one-time setup)
├── Private key: ~/.config/sops/age/keys.txt ← secret, stored in Bitwarden
└── Public key: extracted from private key ← stored in private secrets repo .sops.yaml
Encryption: plaintext env vars → sops -e → ~/.config/dotfiles-secrets/secrets.env.enc (private git repo)
Decryption: private secrets repo → sops -d → process memory / repo-scoped .envrc exports
File locations:
| File | Location | Purpose | Git status |
|---|---|---|---|
DOTFILES_SECRETS_DIR |
~/.config/dotfiles-secrets by default |
Private repo/path for the encrypted dotfiles secrets store | Private |
.sops.yaml |
$DOTFILES_SECRETS_DIR/.sops.yaml |
SOPS config for the private secrets repo | Private |
secrets.env.enc |
$DOTFILES_SECRETS_DIR/secrets.env.enc |
Encrypted API keys (values encrypted, key names visible) | Private |
keys.txt |
~/.config/sops/age/keys.txt |
age private key (paste from Bitwarden on new machines) | Not in repo |
.secrets is now treated as a legacy migration artifact rather than the normal runtime path. The intended flow is encrypted-at-rest plus on-demand decryption.
Commands:
secrets-init # First-time setup: generate age keypair + initialize $DOTFILES_SECRETS_DIR
secrets-edit # Edit the encrypted dotenv file in place (no plaintext runtime file needed)
secrets-paths # Show the resolved private secrets repo paths
secrets-init-project # Bootstrap per-project: .sops.yaml + secrets.env.enc + .envrcNew machine setup:
- Install sops + age (
./install.shhandles this) - Clone or create your private secrets repo at
$DOTFILES_SECRETS_DIR(default~/.config/dotfiles-secrets) - Paste age private key from Bitwarden:
secrets-init(or manually to~/.config/sops/age/keys.txt) - Run
./deploy.sh— verifies the encrypted store can be decrypted on demand
Least-privilege hardening now runs automatically in secrets-init, secrets-edit, secrets-updatekeys, secrets-rotate-data-key, setup-envrc, and deploy.sh. secrets-fix-perms remains available as a manual repair command for the private secrets repo (700 dir, 600 files) and repo-local secret state (.env, .envrc, .claude/channels/telegram/, local .sops.yaml, secrets.env.enc).
Per-project usage: Run setup-envrc in any repo to create a .envrc that selectively exposes only the secrets that repo should see. It supports direct exports (KEY), renamed exports (ENV_VAR=SECRET_NAME), and a repo-specific Telegram plugin binding (--telegram-secret SECRET_NAME) that materializes .claude/channels/telegram/.env only when Claude launches. If local .env files already exist, the TUI scans the repo root recursively, flags drift against the encrypted store, and can offer to delete selected files. Use secrets-init-project only when the repo needs its own SOPS-managed secrets file.
setup-envrc tries direnv allow automatically. If that cannot update direnv's allowlist (for example in a sandboxed or read-only environment), it now prints the manual direnv allow . command and still completes the rest of the setup.
Further reading: SOPS README · age README · SOPS + age tutorial
Automatically sync config 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-gist
# Uninstall automation
./scripts/cleanup/setup_gist_sync.sh --uninstallNote: Secret gists are unlisted, not encrypted. Only non-secret config (SSH config, authorized_keys, git identity) should be synced via gist.
Pre-commit hooks for secret detection across all repositories:
./deploy.sh --git-hooks # Part of defaultsScans staged files for API keys, tokens, and credentials before each commit.
Multi-layer defense against npm/PyPI supply chain attacks (axios 2026, litellm 2026, shai-hulud 2025). Deployed automatically with ./deploy.sh.
What it does:
| Layer | Defense | What it blocks |
|---|---|---|
| 7-day quarantine | min-release-age on all package managers |
Freshly-published malicious versions (caught within days) |
| Script blocking | ignore-scripts=true in npm/pnpm |
Postinstall scripts that exfiltrate secrets or install RATs |
| Credential isolation | API keys scoped per-project via direnv | Compromised package in project A can't read project B's keys |
| Lockfile scanning | Pre-commit hook checks changed lockfiles | Known-bad packages entering your lockfile |
| Weekly audit | Scans all repos for known-bad IOCs | Packages you already have that were later found compromised |
| Claude Code hook | Warns before any npm install / pip install |
AI assistant installing packages without checking them first |
Day-to-day workflow:
# Installing packages works normally — quarantine is transparent
npm install express # Works (express is >7 days old)
bun add zod # Works
uv add httpx # Works
# New packages published <7 days ago are blocked (intentional)
npm install some-brand-new-pkg
# Error: min-release-age — package was published 2 days ago
# Override for a specific install (after checking it's safe)
npm install --min-release-age=0 some-brand-new-pkg # npm
bun add --minimumReleaseAge=0 some-brand-new-pkg # bun
UV_EXCLUDE_NEWER= uv pip install some-brand-new-pkg # uvCredential isolation:
API keys stay in $DOTFILES_SECRETS_DIR/secrets.env.enc and are NOT globally exported. Each project gets only the keys it needs:
# Interactive picker (fzf)
cd ~/code/my-project
setup-envrc # Select keys with TAB, confirm with ENTER
# → Creates .envrc with eval-based exports, direnv auto-loads on cd
# Non-interactive
setup-envrc ANTHROPIC_API_KEY OPENAI_API_KEY
# Map a namespaced secret into the env var your app expects
setup-envrc ANTHROPIC_API_KEY TELEGRAM_BOT_TOKEN=NUDGE_TELEGRAM_BOT_TOKEN
# Claude Telegram plugin: keep the token canonical in dotfiles-secrets,
# and generate .claude/channels/telegram/.env only at launch time
setup-envrc --telegram-secret AMBASSADOR_TELEGRAM_BOT_TOKEN
# Check what's configured
setup-envrc --list # Show keys in current .envrc
setup-envrc --clean # Remove .envrc
# One-off command with selected keys (no .envrc needed)
with-secrets ANTHROPIC_API_KEY OPENAI_API_KEY -- python my_script.pyManual audit:
dep-audit # Scan all repos for known-bad packages now
# Runs automatically every Sunday at 10 AMConfig files deployed:
| File | Deployed to | Purpose |
|---|---|---|
config/npmrc |
~/.npmrc |
ignore-scripts=true + min-release-age=7 |
config/bunfig.toml |
~/.bunfig.toml |
minimumReleaseAge=604800 (seconds) |
config/pnpmrc |
~/Library/Preferences/pnpm/rc |
minimum-release-age=10080 (minutes) |
config/uv.toml |
~/.config/uv/uv.toml |
exclude-newer (via UV_EXCLUDE_NEWER env var) |
Selective deploy:
./deploy.sh --only pkg-configs # Just package manager configs
./deploy.sh --no-pkg-configs # Everything except package configs
./deploy.sh --only dep-audit # Just the weekly audit- 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. - Utility functions in
config/modern_tools.sh:mkd(mkdir+cd),cdf(cd to Finder window, macOS),targz(smart compression),dataurl,digga(DNS lookup),getcertnames(SSL certs),o(cross-platform open),server(quick HTTP server) - System aliases in
config/aliases.sh:flush(DNS cache),afk(lock screen, macOS),week(ISO week number)
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 | USER_HOME=/home bashThen SSH as yulong@<ip> (not root). See [scripts/cloud/README.md](./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