Agent Skill
2/7/2026

api-experiments

Use when running API-heavy experiments (LLM evals, batch processing). Covers memory-efficient patterns, batch APIs vs async, and avoiding the parallel process anti-pattern.

Y
yulonglin
3GitHub Stars
1Views
npx skills add yulonglin/dotfiles

SKILL.md

Nameapi-experiments
DescriptionUse when running API-heavy experiments (LLM evals, batch processing). Covers memory-efficient patterns, batch APIs vs async, and avoiding the parallel process anti-pattern.

dotfiles

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!

AI agents working here: start with CLAUDE.md — it has a Quick Reference, deploy-component table, and operational gotchas. This README is human-oriented onboarding; CLAUDE.md is the operational doc.

Quickstart

This project offers two quickstart paths: Local and Cloud.


Local Quickstart

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 ~/.zshrc
  • install.sh installs required software.
  • deploy.sh deploys 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.


Cloud Quickstart

For cloud environments (RunPod, Hetzner, Lambda Labs, etc):

  1. SSH into your new remote machine as root.
  2. Run the one-liner:
    # 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
    
    This creates a non-root user, installs dependencies, clones dotfiles, and runs install.sh + deploy.sh automatically. It will prompt for GitHub auth and an optional age key (for encrypted secrets).
  3. Reconnect as your user:
    ssh yulong@<ip>
    
  4. (Optional) After pod restart (RunPod recreates /etc/passwd):
    curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/restart.sh | bash
    
  5. (Optional) Customize components: Edit config.sh to 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.

Table of Contents

Adopting 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/gitattributesMouseless 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 workflowPlugin marketplace selections

All personal values are centralized in config.sh — edit DOTFILES_USERNAME, DOTFILES_REPO, GIST_SYNC_ID, GIT_USER_NAME, and GIT_USER_EMAIL to make it yours.

Rust CLI Tools

These modern alternatives are installed by default and significantly faster than their traditional counterparts:

ToolReplacesWhy it's better
batcatSyntax highlighting, line numbers, git integration
ezalsColors, icons, git status, tree view built-in
fdfindIntuitive syntax, respects .gitignore, 5x faster
ripgrep (rg)grepRecursive by default, respects .gitignore, 10x+ faster
deltadiffSide-by-side, syntax highlighting, line numbers
zoxidecdLearns your habits, jump with z dirname
dustduIntuitive visualization of disk usage
jlessless (JSON)Interactive JSON viewer with vim keybindings

Extras (--extras flag):

  • hyperfine — statistical benchmarking with warmup and multiple runs
  • gitui — TUI for git
  • code2prompt — generate LLM prompts from codebases

Installation

Step 1: Install dependencies

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 defaults

Defaults by platform:

PlatformDefaults
macOSzsh, tmux, AI tools, cleanup + Rust CLI tools via Homebrew
Linuxzsh, tmux, AI tools, create-user + Rust CLI tools via mise

Installation on macOS requires Homebrew - install from brew.sh first if needed.

Step 2: Deploy configurations

Deploy configurations (sources aliases for .zshrc, applies oh-my-zsh settings, etc.). All settings live in 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 + mouseless

Default components:

  • Shell: ZSH, tmux, vim, Powerlevel10k
  • Editors: VSCode/Cursor/Antigravity (merged settings), Zed (symlinked config + keymap), .editorconfig, .curlrc, .inputrc
  • AI tools: Claude Code, Codex CLI, Serena MCP, Ghostty terminal
  • Git: gitconfig, global gitignore/gitattributes, global git hooks (secret detection)
  • Dev tools: htop, pdb++, matplotlib styles, claude-tools Rust binary
  • Secrets: GitHub gist sync, SOPS+age (or BWS) encrypted secrets
  • Supply chain: 7-day quarantine for npm/bun/pnpm/uv, weekly dep-audit
  • Automation: file cleanup (macOS), Claude Code session cleanup, AI tools auto-update, package auto-update, text replacements sync (macOS)

Flags are additive — e.g., ./deploy.sh --mouseless deploys defaults + mouseless. Use --minimal to disable all defaults, then specify only what you want.

AI Assistants

Claude Code (Primary AI Assistant)

This setup includes extensive Claude Code customization optimized for AI safety research:

./deploy.sh --claude  # Symlinks claude/ → ~/.claude

What's included:

  • CLAUDE.md — Slim identity file (~120 lines) pointing at modular rules and docs
  • rules/ — 19 auto-loaded behavioral rules (safety, git, agents, refusal alternatives, supply-chain, browser automation, etc.)
  • docs/ — On-demand knowledge loaded by skills (research methodology, async patterns, tmux, agent teams)
  • agents/ — Personal agents (kept lean — most specialized agents live in plugins like ai-safety-plugins)
  • skills/ — Project-level slash commands: /commit, /merge-worktree, /jobs, /modal, /log-gap, /recall-feedback, /mv-repo, etc.
  • hooks/ — 40+ PreToolUse/PostToolUse/SessionStart scripts: auto-classify, secret blocking, modern-tool nudges, post-rebase guards, network audit
  • templates/ — Context profiles (contexts/profiles.yaml), research spec template

Smart merge preserves your data - if ~/.claude already exists, credentials, history, and cache are automatically restored after symlinking.

Claude Code Plugin Marketplaces

Claude Code supports community plugin marketplaces. These are registered in claude/templates/contexts/profiles.yaml and synced via claude-tools context --sync:

MarketplaceWhat's in it
claude-plugins-officialSuperpowers, hookify, plugin-dev, commit-commands, productivity, engineering
ai-safety-pluginsResearch, writing, code, workflow, viz — for AI safety work
productivity-toolsPersonal productivity utilities
ui-ux-pro-max-skillDesign styles, palettes, production-grade frontend
alignment-hiveAlignment research utilities
dev-browser-marketplaceBrowser automation for development
openai-codexCodex CLI integration plugins
rust-skillsRust ownership, concurrency, error handling skills

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 profiles

Codex CLI (OpenAI)

Codex CLI configuration that reuses Claude Code's skills:

./deploy.sh --codex  # Symlinks codex/ → ~/.codex

What's included:

  • AGENTS.md — Global instructions (references CLAUDE.md as source of truth)
  • config.toml — Model settings, status line config, and per-project trust levels
  • rules/ — Behavioral rule files synced from Claude Code's rules/
  • skills/ → symlink to claude/skills/ so both CLIs share the same skill set

The configuration follows the same research discipline as Claude Code but adapted for Codex's execution model.

Gemini CLI (Google)

Gemini CLI can sync with Claude Code configurations:

./scripts/sync_claude_to_gemini.sh  # Syncs skills/agents/permissions

What it does:

  • Symlinks Claude Code skills to ~/.gemini/skills/
  • Converts Claude agents to Gemini skill format
  • Syncs permissions from .claude/settings.json to Gemini policies
  • Creates GEMINI.md pointer 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.

Terminal & Shell

Ghostty (Terminal Emulator)

Ghostty is a fast, GPU-accelerated terminal written in Zig. Config is symlinked to the platform-specific location:

./deploy.sh --ghostty  # Part of defaults

Key settings in config/ghostty.conf:

  • Cmd+C triggers shell-based copy (integrates with tmux)
  • Shift+Enter for multiline input
  • Sensible font and color defaults

Config location: macOS ~/Library/Application Support/com.mitchellh.ghostty/config, Linux ~/.config/ghostty/config

Theme Aliases

Launch new Ghostty windows with different color themes - useful for visually distinguishing contexts:

AliasThemeCharacter
g1Catppuccin MochaWarm purple/pink
g2TokyoNightCool blue
g3Gruvbox DarkRetro orange/brown
g4NordArctic icy blue
g5DraculaPurple accents
g6Rose PineMuted rose tones
g1                        # Launch Ghostty with Catppuccin Mocha
gtheme "Tomorrow Night"   # Launch with any theme
ghostty +list-themes      # See all available themes

Each alias opens a single fresh window (no tab restoration) with the specified theme.

SSH Color Switching

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 fallback

Patterns support wildcards (prod* matches prod1, prod-web, etc.). The default key applies to any host without a specific match.

Powerlevel10k Prompt

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).

SegmentDescription
Remote hostMachine name + emoji (SSH sessions only)
DirectoryCurrent path with git root highlighting
Git statusBranch, dirty indicator, stash count
Right sideExit code, command duration, Python venv, cloud contexts

SSH-Aware Machine Identification

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:

  1. Looks up your public IP against ~/.ssh/config HostName entries
  2. Uses the matching Host alias as the display name
  3. Falls back to abbreviated hostname if no match
  4. 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_NAME env var overrides everything
  • MACHINE_EMOJI env var overrides the auto-assigned emoji

Claude Code Statusline

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-name script 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.

Ignore Pattern Management

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 patterns

The TUI shows patterns grouped by category with tri-state toggles:

  • [ ] skip — pattern not applied
  • [ G ] gitignore — added to .gitignore only
  • [G+S] gitignore + searchable — added to .gitignore AND 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 Statusline

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).

SSH Key Management

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 via SSH_KEY_PATH env 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:

  1. Shell starts → detects no key → prompts "Generate a new ed25519 SSH key now? [y/N]"
  2. If yes → generates key → shows command to copy public key
  3. Automatically adds to agent on this and future shell sessions

Custom key path:

export SSH_KEY_PATH=~/.ssh/id_rsa  # Use RSA key instead

Configuration: config/ssh_setup.sh

Dev Tools

htop (Process Monitor)

Dynamic htop configuration that adapts CPU meters to your core count:

./deploy.sh --htop  # Part of defaults

The config in config/htop/htoprc uses a dynamic layout that works across machines with different CPU counts—no manual adjustment needed.

pdb++ (Python Debugger)

High-contrast color scheme for pdb++, the enhanced Python debugger:

./deploy.sh --pdb  # Part of defaults

Global 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 colors

Per-project override (advanced): Create .pdbrc.py in project root. It takes precedence over the global config. See pdb++ docs for details.

Automation

Automatic Cleanup (macOS)

Scheduled cleanup of old files from ~/Downloads and ~/Screenshots:

./deploy.sh --cleanup  # Part of macOS defaults

How 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 weekly

See scripts/cleanup/README.md for full documentation.

Claude Code Session Cleanup (both platforms)

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-code command (aliases: ccl, cci, ccf)
# Check status
clear-claude-code --list

# Uninstall
./scripts/cleanup/setup_claude_cleanup.sh --uninstall

AI Tools Auto-Update (both platforms)

Daily automatic updates for Claude Code, Gemini CLI, and Codex CLI at 06:00:

./deploy.sh --ai-update  # Part of defaults

Runs via launchd (macOS) or cron (Linux). Uninstall with scripts/cleanup/setup_ai_update.sh --uninstall.

Package Auto-Update (both platforms)

Weekly package upgrade + cleanup on Sundays at 05:00:

./deploy.sh --brew-update  # Part of defaults

Supports Homebrew (macOS), apt, dnf, and pacman (Linux). Includes cleanup of stale caches.

Text Replacements (macOS)

Bidirectional sync between macOS text replacements and Alfred snippets. Runs daily at 09:00:

./deploy.sh --text-replacements  # macOS only, part of defaults

macOS uses raw shortcuts; Alfred applies a collection prefix at runtime (e.g., fm.hi). Requires Full Disk Access for your terminal app.

Secrets & Security

Encrypted Secrets (SOPS + age)

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:

FileLocationPurposeGit status
DOTFILES_SECRETS_DIR~/.config/dotfiles-secrets by defaultPrivate repo/path for the encrypted dotfiles secrets storePrivate
.sops.yaml$DOTFILES_SECRETS_DIR/.sops.yamlSOPS config for the private secrets repoPrivate
secrets.env.enc$DOTFILES_SECRETS_DIR/secrets.env.encEncrypted API keys (values encrypted, key names visible)Private
keys.txt~/.config/sops/age/keys.txtage 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 + .envrc

New machine setup:

  1. Install sops + age (./install.sh handles this)
  2. Clone or create your private secrets repo at $DOTFILES_SECRETS_DIR (default ~/.config/dotfiles-secrets)
  3. Paste age private key from Bitwarden: secrets-init (or manually to ~/.config/sops/age/keys.txt)
  4. 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

Gist Sync Automation (both platforms)

Automatically sync config with GitHub gist daily at 08:00:

./deploy.sh --secrets  # Part of defaults

How 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 --uninstall

Note: Secret gists are unlisted, not encrypted. Only non-secret config (SSH config, authorized_keys, git identity) should be synced via gist.

Global Git Hooks

Pre-commit hooks for secret detection across all repositories:

./deploy.sh --git-hooks  # Part of defaults

Scans staged files for API keys, tokens, and credentials before each commit.

Supply Chain Defense

Multi-layer defense against npm/PyPI supply chain attacks (axios 2026, litellm 2026, shai-hulud 2025). Deployed automatically with ./deploy.sh.

What it does:

LayerDefenseWhat it blocks
7-day quarantinemin-release-age on all package managersFreshly-published malicious versions (caught within days)
Script blockingignore-scripts=true in npm/pnpmPostinstall scripts that exfiltrate secrets or install RATs
Credential isolationAPI keys scoped per-project via direnvCompromised package in project A can't read project B's keys
Lockfile scanningPre-commit hook checks changed lockfilesKnown-bad packages entering your lockfile
Weekly auditScans all repos for known-bad IOCsPackages you already have that were later found compromised
Claude Code hookWarns before any npm install / pip installAI 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   # uv

Credential 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.py

Manual audit:

dep-audit                    # Scan all repos for known-bad packages now
# Runs automatically every Sunday at 10 AM

Config files deployed:

FileDeployed toPurpose
config/npmrc~/.npmrcignore-scripts=true + min-release-age=7
config/bunfig.toml~/.bunfig.tomlminimumReleaseAge=604800 (seconds)
config/pnpmrc~/Library/Preferences/pnpm/rcminimum-release-age=10080 (minutes)
config/uv.toml~/.config/uv/uv.tomlexclude-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

Getting to know these dotfiles

  • 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 cd1 to your most used git repo so you can just type cd1 to 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)

Cloud Setup (RunPod, Hetzner, etc.)

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 bash

Then SSH as yulong@<ip> (not root). See scripts/cloud/README.md for details.

What it does:

  • Creates non-root user in persistent storage (/workspace/yulong on RunPod)
  • Installs uv, dotfiles, Claude Code
  • Copies SSH keys for direct access
Skills Info
Original Name:api-experimentsAuthor:yulonglin