Personal cross-platform system configuration for macOS and Linux/WSL. Manages shell environment (zsh), editor (nvim, vim), terminal (alacritty), window management (tmux, karabiner, rectangle, vscode), background daemons (watchcommit, opencode-skills-sync), and personal coding-agent instruction overlays.
Following the separation of the cross-harness agent toolkit into agent-toolkit:
- Shared agent toolkit (
agent-toolkit): The upstream repository at~/Workspace/agent-toolkit(or your clone location) owns shared multi-harness agent tooling: slash commands, skills, hooks, MCP server configurations, and cross-harness parity for Claude Code, GitHub Copilot, opencode, Google Antigravity, Pi, and Codex CLI. Refer toagent-toolkit/README.mdfor shared agent tooling documentation. - Personal dotfiles (
dotfiles): This repository owns your personal operating system configuration plus your personal agent overlay:claude/personal-overlay.md: Personal workflow rules (backlog management viadev_status.py, worktree-first policy, proactive capture, verification standards).claude/global-instructions.md: Composed dynamically by combining upstream core instructions withpersonal-overlay.mdviagen_core_instructions.py.- Personal daemons:
watchcommit(automatic commit and push) andopencode-skills-sync.
Claude Code and opencode config (~/.claude/settings.json, ~/.config/opencode/opencode.jsonc) are seeded exclusively by agent-toolkit's installer — this repo does not own or seed either.
IMPORTANT: Installation on dual-repo machines
agent-toolkit's installer symlinks bare upstream core instructions (CORE_INSTRUCTIONS.md) to~/.claude/CLAUDE.mdand equivalent harness paths. Ifagent-toolkit/install.pyruns after this repo'sinstall.sh, it will overwrite and silently detach your personal overlay.On any machine that has both repositories checked out, always install via the wrapper script:
./scripts/install-with-agent-toolkit.sh --harness=claude,copilot,opencode,agy,pi,codexThe wrapper guarantees the correct installation order: it runs
agent-toolkit's installer first, then immediately runsdotfiles' installer second to reassert the composed personal overlay.
Work machine, no git checkout? The wrapper above assumes dotfiles should keep winning those five destinations — correct for a personal machine, wrong for work hardware, which shouldn't carry the personal overlay at all. If dotfiles there is a downloaded ZIP rather than a git checkout, see docs/work-machine-cutover.md for the inverse procedure.
For single-repo setups or personal machines without a separate agent-toolkit checkout:
git clone <repo-url> ~/dotfiles
cd ~/dotfiles
chmod +x install.sh
./install.sh --harness=claude # personal machine, Claude Code overlay
./install.sh --profile=work --harness=copilot # work machine, Copilot overlay
./install.sh --harness=claude,opencode # multiple harnesses
./install.sh --dry-run --harness=claude # preview only, nothing written./install.sh is a ~20-line POSIX bootstrap: it finds Python 3.12+ on PATH and hands off to install.py, which is the actual installer. You can also run python3 install.py --harness=... directly; flags and behavior are identical.
The symlink table lives in links.toml at the repo root. Each entry can be gated on harness, platform, wsl, and profile_exclude. Copy-once seed files (Pi's settings.json and WSL VS Code settings) are handled directly by install.py. Claude Code and opencode config are seeded by agent-toolkit's installer instead — see Dual-repo machines.
--harness is required on an install run: choose any combination of claude, copilot, opencode, agy, pi, codex (comma-separated). In dotfiles, this controls which harness personal overlays and Pi/VS Code settings seeds are wired up.
--profile controls machine-level concerns (personal by default). --profile=work excludes watchcommit and rejects opencode. See Work profile below.
Add --dry-run to preview any run (including --rollback) without writing or removing anything.
| Source | Destination | Notes |
|---|---|---|
vim/.vimrc |
~/.vimrc |
All platforms |
nvim/ |
~/.config/nvim |
Neovim 0.11+ configuration |
zsh/.zshrc |
~/.zshrc |
Core shell configuration |
zsh/.zprofile |
~/.zprofile |
macOS only (Homebrew shellenv) |
zsh/.common_shell_aliases |
~/.common_shell_aliases |
Cross-platform shell aliases |
shell/.poshtheme.omp.json |
~/.poshtheme.omp.json |
oh-my-posh prompt theme |
tmux/.tmux.conf |
~/.tmux.conf |
Terminal multiplexer config |
alacritty/alacritty.toml |
~/.config/alacritty/alacritty.toml |
Terminal emulator config |
herdr/config.toml |
~/.config/herdr/config.toml |
Herdr agent supervisor config |
karabiner/karabiner.json |
~/.config/karabiner/karabiner.json |
macOS keyboard modifications |
vscode/settings.json |
VS Code user settings | Per-OS path (macOS, Linux, WSL) |
vscode/keybindings.json |
VS Code keybindings | Per-OS path (macOS, Linux, WSL) |
claude/global-instructions.md |
~/.claude/CLAUDE.md, ~/.copilot/copilot-instructions.md, ~/.gemini/GEMINI.md, ~/.pi/agent/AGENTS.md, ~/.codex/AGENTS.md |
Composed personal instructions overlay. Codex caps the combined instruction chain at project_doc_max_bytes (32 KiB default) and this composed file is already ~31 KB of it, leaving very little headroom for a repo's own AGENTS.md chain — raise project_doc_max_bytes in ~/.codex/config.toml if a heavy-AGENTS.md repo truncates. |
claude/output-styles/PlainEngineer.md |
~/.claude/output-styles/PlainEngineer.md |
Custom Claude Code output style |
scripts/watchcommit.py |
~/.local/bin/watchcommit |
Background auto-commit daemon (--profile=personal) |
scripts/wc-guard |
~/.local/bin/wc-guard |
Watchcommit pause/resume wrapper |
systemd/watchcommit.service |
~/.config/systemd/user/watchcommit.service |
Linux systemd user service |
launchd/com.user.watchcommit.plist |
~/Library/LaunchAgents/com.user.watchcommit.plist |
macOS launchd agent |
scripts/opencode_skills_sync.py |
~/.local/bin/opencode-skills-sync |
Skills synchronization daemon (Linux personal) |
systemd/opencode-skills-sync.service |
~/.config/systemd/user/opencode-skills-sync.service |
Linux systemd user service |
pi/settings.json: Seeded once to~/.pi/agent/settings.json.- VS Code
settings.jsonandkeybindings.json: Under WSL, copied to the Windows-side AppData roaming directory via thecodeCLI on PATH.
Claude Code (~/.claude/settings.json) and opencode (~/.config/opencode/opencode.jsonc) config are seeded by agent-toolkit's installer, not this repo's — see Dual-repo machines.
Use --adopt --harness=... to pull drifted copy-once settings back into the repository.
- Installs CLI packages:
tmux,zoxide,eza,bat,ripgrep,lsd,ncdu,tldr,oh-my-posh,neovim,fd,uv,ruff. - Installs NVM and Node/npm if
claudeorcopilotis selected in--harness. - Symlinks every applicable entry in
links.toml(existing non-symlinks are backed up to*.bak). - Seeds copy-once configuration files (Pi's
settings.json, WSL VS Code). - Bootstraps Neovim plugins (
lazy.nvimsync) ifnvimis >=0.11.
- Installs Homebrew (Apple Silicon and Intel).
- Installs casks: Karabiner-Elements, Rectangle, Ghostty, VS Code, AltTab, JetBrainsMono Nerd Font.
- Symlinks macOS configs (
.zprofile, Karabiner, launchd plist). - Imports Rectangle window management shortcuts.
- Maps Caps Lock → Escape via macOS modifier keys.
- Loads the
watchcommitlaunchd agent (personal profile).
- Installs distro packages via
apt(Ubuntu/Debian) ordnf(Fedora). - Creates
~/.local/bin/batshim where packaged asbatcat. - Installs JetBrainsMono Nerd Font (pinned version) to
~/.local/share/fonts/JetBrainsMonoNerdFont. - Enables and starts
watchcommit.serviceunder systemd--user, enabling user lingering vialoginctl.
--profile=work controls machine-level concerns:
- watchcommit is excluded entirely: No binary, no service. Watchcommit auto-pushes to personal remotes and has no place on work hardware.
- opencode is excluded entirely:
--profile=work --harness=opencodeis rejected at argument parsing. - Profile marker: Written to
~/.local/state/dotfiles/profile. Subsequent runs with--profile=personalwill refuse unless--forceis provided.
The installer never aborts on a recoverable failure. Unmet dependencies or skipped steps are highlighted in yellow in the summary; exit code is 1 if anything was skipped.
File mutations are recorded in ~/.local/state/dotfiles/history.jsonl. --rollback reverses every mutation recorded there:
./install.sh --rollbackTo perform a complete clean slate undo, removing backups and sweeping Neovim and service states:
./install.sh --rollback --wipe--check-links compares live filesystem symlinks against links.toml. It is read-only and safe to run at any time:
./install.sh --check-links
./install.sh --check-links --harness=claudeReported categories:
- broken-source: Symlink exists but repo source was removed (dangling).
- wrong-target: Symlink points to something other than
links.tomlsource. - not-a-symlink: Real file or directory sits where a symlink belongs.
- orphaned: Live symlink recorded by a previous install that is no longer in
links.toml. (Normal install runs remove orphans automatically;--check-linksonly reports them). - unmanaged: Files in directories marked exclusive via
[[managed_dir]]not produced by any link.
--depart removes or restores everything installed on Ubuntu/WSL (apt) or Fedora (dnf), leaving no local trace that install.sh was run:
./install.sh --depart # interactive confirmation
./install.sh --depart --dry-run # preview only
./install.sh --depart --yes # non-interactiveOperates strictly from a baseline snapshot captured at install time (~/.local/state/dotfiles/departure.jsonl). See Nuclear reset (WSL) if full image re-creation is required.
To completely and irreversibly wipe a WSL distribution and start from a stock image:
# From Windows PowerShell:
wsl --list --verbose
wsl --unregister <DistroName> # irreversibly deletes distro filesystem
wsl --install -d <DistroName> # recreate from stock image- Karabiner-Elements (macOS): Configuration in
karabiner/karabiner.json. Maps Caps Lock to Control when held, Escape when tapped. - AltTab (macOS): Windows-style Alt+Tab application switching.
- Rectangle (macOS): Window snapping shortcuts:
Ctrl+Option+Enter: MaximizeCtrl+Option+Left/Right: Left/Right halfCtrl+Option+C: Center
Background daemon that automatically commits and pushes dotfiles changes within ~90 seconds:
- Managed on Linux via
systemd --user(watchcommit.service) and macOS via launchd (com.user.watchcommit.plist). - Automatically detects active coding agents and pauses itself to avoid racing session edits.
- Use
wc-guard <command>orwc-pause/wc-resumeto manually pause synchronization during git history edits or testing.
Synchronizes local skills in ~/.config/opencode/skills to a local commit-only git branch to prevent accidental loss of interactive skill edits.
- intel Mac:
install.pyand.zprofiledetect/usr/local/bin/brewautomatically. - Secrets:
~/.secretsis gitignored — created manually per machine for environment tokens. - Testing:
- Fast tests:
uv run pytest test/ claude/scripts/(includesconftest.pysandboxing to prevent real filesystem/subprocess mutations). - Linting:
uv run ruff check .anduv run ruff format --check .. - Lifecycle tests:
test/run.shdrives containerized install scenarios against Ubuntu and Fedora Docker images.
- Fast tests: