:round_pushpin: My dotfiles for macOS using Neovim, Zsh, and Ghostty + Tmux.
$ npx -y skills add joshukraine/dotfiles --agent claude-code
![dotfiles screenshot][screenshot]
[!NOTE] This project previously supported [Fish shell][fish] alongside Zsh. Fish support was removed in [PR #135][pr-135] (
f158de9). If you were using the Fish configuration, you can reference that PR to see what changed or recover code for your own setup.
Make sure macOS is up to date and you have installed the required software.
Clone this repo.
git clone https://github.com/joshukraine/dotfiles.git ~/dotfiles
Read the setup script and check available options.
less ~/dotfiles/setup.sh
~/dotfiles/setup.sh --help
Preview what the setup script will do (dry-run mode).
~/dotfiles/setup.sh --dry-run
Run the setup script.
~/dotfiles/setup.sh
The dotfiles assume you are running macOS with (at minimum) the following software pre-installed:
All of the above and more are installed with my fork of [Laptop][joshuas-laptop].
This is what I would do if I bought a new Mac computer today. The steps below assume you have already completed the basics:
▹ [github.com/joshukraine/laptop][joshuas-laptop]
Download the mac script:
curl --remote-name https://raw.githubusercontent.com/joshukraine/laptop/main/mac
Download .local.laptop for additional customizations:
curl --remote-name https://raw.githubusercontent.com/joshukraine/dotfiles/master/laptop/.laptop.local
Review both scripts before proceeding:
less mac
less .laptop.local
Execute the mac script:
sh mac 2>&1 | tee ~/laptop.log
I've made the following changes to my fork of Laptop:
It is worth noting that the Laptop script (mac) is idempotent and can be safely run multiple times to ensure a consistent baseline configuration.
The dotfiles setup.sh script uses [GNU Stow][gnu-stow] to symlink all the config files to your $HOME directory. If you already have an identically-named file/directory in $HOME (e.g. ~/.zshrc leftover from installing Laptop), this will cause a conflict, and Stow will (rightly) abort with an error.
The setup script will try to detect and backup these files ahead of Stow, but it's still a good idea to check your $HOME directory as well as $HOME/.config and $HOME/.local/bin.
On a fresh machine, setup.sh also pre-creates ~/.claude and ~/.claude/skills as real directories so Stow links the Claude config files individually instead of folding a whole directory into one symlink (which would route Claude Code's runtime state, or its synced account skills, into the repo). If you forked before this behavior existed and see Claude runtime files appearing in git status, see Troubleshooting: ~/.claude folding and Troubleshooting: ~/.claude/skills folding.
Clone
git clone https://github.com/joshukraine/dotfiles.git ~/dotfiles
Read and preview
less ~/dotfiles/setup.sh
~/dotfiles/setup.sh --help
~/dotfiles/setup.sh --dry-run # Preview changes without applying them
Setup
~/dotfiles/setup.sh
If you do encounter Stow conflicts, resolve these and run setup again. The script is idempotent, so you can run it multiple times safely.
[Zap][zap] describes itself as a "minimal zsh plugin manager that does what you expect."
▹ [zapzsh.com][zap]
[!IMPORTANT] After copying/pasting the install command for Zap, be sure to add the
--keepflag to prevent Zap from replacing you existing.zshrcfile.
Review the included Brewfile and make desired adjustments.
less ~/Brewfile
Install the bundle.
brew bundle install
nvim) and run [:checkhealth][checkhealth]. Resolve errors and warnings. Plugins should install automatically on first launch.*.local files such as ~/.gitconfig.local, ~/.laptop.local.<prefix> + I (https://github.com/tmux-plugins/tpm)Zsh is now the default shell on macOS. However, it's helpful to add an entry enabling the Homebrew version of Zsh (/opt/homebrew/bin/zsh on Apple Silicon, /usr/local/bin/zsh on Intel) instead of the default (/bin/zsh) version.
Ensure that you have Zsh from Homebrew. (which zsh) If not:
brew install zsh
Add Zsh (Homebrew version) to /etc/shells:
# Apple Silicon Macs:
echo /opt/homebrew/bin/zsh | sudo tee -a /etc/shells
# Intel Macs:
echo /usr/local/bin/zsh | sudo tee -a /etc/shells
# Or use this universal command:
echo $(which zsh) | sudo tee -a /etc/shells
Set it as your default shell:
chsh -s $(which zsh)
Install [Zap][zap].
Restart your terminal.
One of the best ideas I picked up from using Fish shell is abbreviations over aliases. [zsh-abbr][zsh-abbr] brings this functionality to Zsh.
Abbreviations are managed directly in zsh/.config/zsh-abbr/abbreviations.zsh. You can edit this file directly, or use the abbr add/abbr remove commands in your shell.
The configuration includes intelligent git functions that automatically detect your main branch:
gpum - Push current branch to origin with upstream trackinggrbm - Rebase on main/mastergcom - Checkout main/mastergbrm - Remove branches merged into main/masterThese functions work with both main and master branch names automatically.
bubo reports what in the environment has drifted, across every surface setup.sh installs: Homebrew, Zap and its plugins, tmux/tpm plugins, asdf plugins and tool versions, Neovim/lazy, and Mason. It prints the command to fix anything that is behind.
bubo
tmux plugins (tpm) 2 stale, 2 current
tpm 99469c4 -> e261deb
vim-tmux-navigator c45243d -> e41c431
fix: ~/.config/tmux/plugins/tpm/bin/update_plugins all
mason 4 stale, 33 current
registry data: 4h old
tree-sitter-cli v0.26.10 -> v0.26.11
fix: nvim +Mason (then press U)
It installs and upgrades nothing. Index and registry refreshes are permitted and unavoidable — brew update refreshes the tap index, and lazy fetches — but nothing changes version. Fixing is always a separate, deliberate act. bubc upgrades Homebrew; the other fix commands are printed next to whatever needs them.
Everything runs in parallel, because the cost is almost entirely network round trips: the clone probes alone take 4.2s sequentially against 0.6s in parallel. The full report costs about 3s, against 2.5s for the brew update && brew outdated that bubo used to be.
Two properties are worth knowing, because both were learned from real bugs:
UNKNOWN, never "current". A report that under-reports staleness is worse than no report, because it turns "I don't know" into "you're fine". Same reason a symbolic asdf pin like stable reports UNKNOWN: it resolves at runtime, so comparing it against a version number is not a comparison.HEAD never moved. It called both tpm and vim-tmux-navigator current while each sat a commit behind.That second bug is not hypothetical: a stale Zap plugin clone kept a fixed-upstream bug alive locally for 10 months, and nothing surfaced it.
[Claude Code][claude-code] is Anthropic's CLI tool for AI-assisted development. This repo includes a full configuration under the claude/ directory, stowed to ~/.claude/.
[!TIP] Claude Code pairs well with [Neovim][neovim] via the [sidekick.nvim][sidekick-nvim] plugin, which is how I use it most of the time during development. My preferred layout runs Claude as a sibling tmux pane (60% editor / 40% Claude) rather than nested inside the editor — see [Claude Code + Neovim][claude-code-neovim] for the setup, the reasoning, and how sidekick sends editor context across panes.
Custom [skills][agent-skills] provide structured workflows for the full development lifecycle:
| Skill | Purpose |
|---|---|
/autopilot | Carry one well-scoped issue through the full dev loop autonomously, to a review-ready PR (or merge for small reversible changes) |
/autopilot-batch | Fan out a queue of issues to parallel worktree subagents, each running /autopilot, with an Opus gating review per PR |
/autopilot-triage | Vet open issues for autonomous resolution and queue the qualifying ones for /autopilot-batch |
/bootstrap-prd | Scaffold PRD-driven development infrastructure into a new project |
/checkpoint | Quick status update — what's done, in progress, and blocked |
/create-pr | Create a PR with auto-linked issues and formatted description |
/debrief | Comprehensive walkthrough of recent work with architecture rationale |
/drift-check | Pre-PR advisory check for deviations from the project spec (read-only) |
/dustoff | Read-only re-entry assessment for a dormant project, with a prioritized plan |
/md2pdf | Convert a Markdown file to PDF with GitHub-style formatting |
/merge-pr | Merge a PR with status checks, squash merge, and branch cleanup |
/plan-phase | Draft implementation plan and create GitHub issues (no code written) |
/prd-view | Render a PRD Markdown file as a rich HTML reading view in the browser |
/qa-handoff | Prepare a hands-on QA testing guide (Rails apps or static/Hugo sites) for a completed PRD phase |
/qa-triage | Triage a QA-labeled report, classify it, and draft the technical issue(s) |
/qa-triage-batch | Fan out /qa-triage across the open QA reports, cluster shared root causes across them, and create the tech issues behind one gate |
/readme-refresh | Audit and update a project README, or bootstrap a new one |
/resolve-issue | Structured workflow for resolving a GitHub issue end-to-end |
/ruby-gc | Audit asdf-installed Ruby versions against project pins and remove unreferenced ones (dry-run by default) |
/sidecar | Research-only mode for a secondary session sharing a working dir — no tracked-file edits or git mutations |
/todoist-cli | Manage Todoist tasks, projects, and labels via the td CLI |
joshukraine-dotfiles is a Claude Code plugin with 24 hand-picked skills, indexed on Flowy in the Development category. Install it with the command on its page. It includes autopilot-batch, autopilot-triage, autopilot. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it