00
dotfiles
One command sets up a machine the way I work: the shell, 39 tools, and the same rules for three AI coding agents. Scroll to see how it fits together.
- 247files
- 39tools
- 17steps
- 17skills
- 8agents
- 19guard checks
01
Plain git. No manager.
The repo is the single source of truth. The 23 files in home/ mirror $HOME, and each one is a symlink back, so I edit either side and the change is live at once. No manager, no template engine, no apply step. Fewer parts, fewer ways to drift.
readlink ~/.zshrc | sed "s|$HOME|~|"~/dotfiles/home/.zshrc
02
One line, any machine.
I paste one line in a terminal and walk away. It asks a few questions, then runs 17 steps: prerequisites, the package manager, 39 tools, Rust, Node, the agents, the git hooks. A step that fails warns and steps aside. The run goes on.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/pavstev/dotfiles/main/bootstrap.sh)"✓ mise installed✓ rg installed (mise)✓ fd installed (mise)
03
Safe to run twice.
Every step skips what is already there. An older file moves to a .bak copy before its link goes in. When something feels off I run dots doctor, which prints one PASS or FAIL line for each of its 8 checks. A check changes nothing.
dots doctorPASS linksPASS settings-builtPASS bins-freshPASS guard-selftest
04
Tested on three systems.
macOS, five Linux distros and Windows in Git Bash are all first class. A change must keep each one working, so one command runs the check on all three at once. macOS runs here, Linux in fresh containers, Windows in a throwaway VM. The Rust side has 769 tests.
bash scripts/os-check.sh allPASS macosPASS linuxPASS windowsRESULT: pass. Logs: .os-check/
05
Public, but private.
The repo is public, so it holds nothing personal: no names, no emails, no home paths, no secrets. My own rules live in one ignored file. Git hooks block private words before a commit and before a push. The guard will not read the file that holds my secrets.
sed -n 6,9p .gitignore.zshrc.locallocal.zsh.gitconfig.localBrewfile.local
06
One source, three AIs.
Claude Code, Cursor and Opencode read the same rules, 17 skills and 8 agents from one folder. Claude gets symlinks. Cursor and Opencode get a generated copy with a marker that says it is ours. A file without the marker is yours and is never touched.
rg -o "generated-by: dotfiles" .cursor/rules.cursor/rules/claude-global.mdc:generated-by: dotfiles.cursor/rules/claude-output-style.mdc:generated-by: dotfiles
H0
The harness
Every tool call my AI agents make passes through a Rust guard with 19 checks. Time and tokens are the cost that counts, so the rules live in code, not in a prompt.
- 19guard checks
- 17skills
- 8agents
- 7presets
H1
One tool call.
A hook runs on every call, so it starts with sh and fails open. It hands the call to the guard, 19 checks. The router sets the model and the effort, and the pace meter says ok, ahead or critical. A denied call comes back with the rule and the fix.
sh ~/.claude/hooks/rust-bin --hook guard pretoolG14: bare sed -i on macOS eats the script as the backup suffix. Give a suffix: `sed -i '' 's/a/b/' f` (or `-i.bak`). Or use the Edit tool.exit 2
H2
The token ledger.
My time is the cost that counts, and tokens are the second cost. So the guard says no before a helper spends anything. Each row is one rule and the reason it exists. Together they make the weekly budget last all the work days.
token ledgerpreset off: no tokenshook sh, not bash: about 6 ms less per callrouter opus: plans, final review. haiku: lookups. sonnet: the rest. xhigh, max: neverG8 G7 helpers 3, 1, 0 at once. general-purpose: refused, about 45K heavierstatus copper at 300K. hand off to a file
H3
The roster, pinned down.
17 skills, 8 agents, 7 presets, 6 hook events, 17 Rust programs and 2 output styles. Each agent is pinned to a model and an effort, and gets only the tools its job needs. Every hand-back starts with a status word and has a character cap. A pre-commit check keeps the skill descriptions honest.
rg "^(name|model|effort|tools|disallowedTools):" claude/agents/worker.mdname: workermodel: sonneteffort: hightools: Read, Edit, Write, Bash, Grep, GlobdisallowedTools: Agent, NotebookEdit
end
Take it all back.
One line installs it. One command takes it back: dots uninstall. It removes the links and the generated files, and puts the old files back. Your own tools and settings stay.
Install
sh -c "$(curl -fsSL https://raw.githubusercontent.com/pavstev/dotfiles/main/bootstrap.sh)"Take it back
dots uninstallBack to work