A clean, organized dotfiles repository using symlinks for easy management and deployment.
- Overview
- Structure
- Installation
- Usage
dotsUsage Examples- How It Works
- Black Atom Theme Integration
- Submodules
- Development
- Dependencies
- Platform Support
- Useful Links
This dotfiles system uses a unified YAML configuration for symlink management:
- Configuration files live in this repository organized by platform
- Symlinks are defined in a single
symlinks.ymlfile with OS-specific sections - Running
dots linkupdates everything automatically (removes broken symlinks + creates new ones) - Supports wildcard patterns for flexible file management (e.g.,
"common/.local/bin/*": "~/.local/bin") - Eliminates duplication with shared common entries across platforms
dots/
├── README.md # This file
├── CLAUDE.md # Claude Code instructions
├── symlinks.yml # Symlinks configuration (YAML)
├── scripts/ # Management scripts
│ ├── detect-os.sh # OS detection utility
│ └── symlinks.sh # Symlink creation and management
├── common/ # Cross-platform configurations
│ ├── .config/ # Config files (.zshrc, .gitconfig, etc.)
│ ├── bin/ # Custom scripts
│ └── .zshrc, etc. # Root dotfiles
├── macos/ # macOS-specific configurations
│ ├── .config/karabiner/ # Karabiner configuration
│ ├── Library/ # Application Support files
│ └── Brewfile # Homebrew dependencies
├── linux/ # Linux-specific configurations
└── arch/ # Arch-specific configurations
Before cloning, set up SSH access to GitHub via 1Password:
macOS:
# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install 1Password
brew install --cask 1passwordArch Linux (EndeavourOS):
# yay is pre-installed on EndeavourOS
yay -S 1passwordThen configure 1Password SSH:
- Open 1Password and sign in
- Add your SSH key (or create one: + New Item → SSH Key)
- Enable the SSH agent: Settings → Developer → SSH Agent
- Verify:
ssh -T git@github.comshould show "Hi username!"
git clone git@github.com:nikbrunner/dots.git ~/repos/nikbrunner/dots
cd ~/repos/nikbrunner/dots
./install.shThis will:
- Install all required dependencies (git, zsh, tmux, neovim, fzf, ripgrep, etc.)
- Configure system settings (default shell, Git signing)
- Create all symlinks
- Set up the
dotscommand
Manual Installation (skip dependencies):
./install.sh --no-deps
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrcTroubleshooting:
# For detailed diagnostics during installation
./install.sh --debug --dry-run
# For testing symlink operations
dots link --debug --dry-runThe dots command provides a unified interface for managing your dotfiles:
| Command | Description | Options |
|---|---|---|
dots install |
Complete machine setup with dependencies, symlinks and submodules | --dry-run, --no-deps |
dots link |
Update all symlinks (removes broken + creates new) | --dry-run, --no-backup, --verbose |
dots sync |
Git pull + submodule updates | - |
dots status |
Show git and symlink status | - |
dots open |
Open dots directory with $EDITOR |
- |
dots test |
Run comprehensive system tests | - |
dots format |
Format repository files with prettier and shfmt | --check |
dots commit |
Open LazyGit for interactive committing | - |
dots push |
Push commits to remote | --force |
dots log |
Show recent commits | - |
dots sub-update |
Update all submodules | - |
dots sub-add |
Add new submodule | <url> <path> |
dots sub-commit |
Commit submodule hash updates | - |
dots sub-status |
Show status of all submodules | - |
dots theme-link |
Create relative symlinks for Black Atom themes | --dry-run |
dots test |
Run comprehensive system tests (repository structure, OS detection, symlinks, etc) | - |
A minimal but powerful repository manager for organizing all your git repositories under ~/repos/username/repo-name/:
| Command | Description | Options |
|---|---|---|
repos find |
Search and open files across all repositories | - |
repos open |
Open a repository in tmux | - |
repos status |
Show git status for all repositories | - |
repos add <url> |
Clone a repository to organized location | - |
repos config |
Edit repos configuration (ENSURE_CLONED list) | - |
repos setup |
Clone all repositories from ENSURE_CLONED list | - |
Unified repository operations with optional AI assistance:
| Command | Description | Options |
|---|---|---|
repo commit |
Open lazygit for interactive committing | - |
repo commit -s |
Generate commit message with AI | -y (auto-confirm), -p (push), -f (force push) |
repo branch "name" |
Create branch with exact name | - |
repo branch -s "desc" |
Generate branch name with AI | -y (auto-create) |
Git Aliases:
git sc→repo commit -s(smart commit)git sb→repo branch -s(smart branch)
Examples:
repo commit -s -yp- AI commit with auto-confirm and pushrepo branch -s -y "BCD-123 fix login"- AI branch name with auto-create
- Add the file to the appropriate directory:
- Cross-platform:
common/(mirrors home directory structure) - OS-specific:
macos/orarch/(mirrors home directory structure)
- Cross-platform:
- Update configuration: Edit
symlinks.ymlto add the symlink entry in the appropriate OS section - Update symlinks:
dots link - Commit changes:
dots commit(opens LazyGit)
- Delete the file from the repository
- Update configuration: Remove the entry from
symlinks.yml - Update symlinks:
dots link(broken symlinks are automatically removed) - Commit changes:
dots commit
- Rename the file in the repository:
# Example: Rename a script mv common/.local/bin/old-name common/.local/bin/new-name - Update symlinks:
dots link- Old symlink (
~/.local/bin/old-name) is automatically removed - New symlink (
~/.local/bin/new-name) is created
- Old symlink (
- Commit changes:
dots commit
- Edit files directly in your home directory (they're symlinked!)
- Check changes:
dots status - Commit changes:
dots commit
# On other machines
dots sync # Pull latest changes and update submodules
dots link # Update symlinks if needed# Run comprehensive system tests (good before making changes)
dots test
# Preview what symlinks would be created (detailed output)
dots link --dry-runThe dots test command validates the entire system (repository structure, OS detection, symlink creation, etc.) and reports pass/fail status. Use dots link --dry-run when you want detailed output showing exactly what symlink operations would be performed.
- Various helper scripts in
~/.local/bin/for development workflows - Platform-specific utilities and configurations
When you run dots link:
- Loads configuration: Reads the
symlinks.ymlfile for your platform - Cleans up: Removes any broken symlinks from previous configurations
- Processes entries: Creates symlinks as defined in the configuration:
- Directory symlinks for entire directories
- File symlinks for individual files
- Wildcard expansion for patterns like
"common/.local/bin/*": "~/.local/bin"
- Backs up conflicts: If a real file exists where a symlink should go, it's backed up with a timestamp (unless
--no-backupis used)
Manual Configuration Benefits:
- Explicit control over what gets symlinked
- Mix directory and file-level symlinks as needed
- Wildcard patterns for selective file linking
- 74% fewer configuration entries compared to auto-discovery (42 vs 353)
Options:
--dry-run: Preview what would happen without making changes--no-backup: Overwrite existing files instead of backing them up--verbose: Show detailed output for each symlink operation
Place OS-specific files in macos/, linux/, or arch/ following the home directory structure. The system uses the symlinks.yml configuration file with OS-specific sections to define which files get symlinked.
Important: The YAML section names must match the OS detection output:
| Detected OS | YAML Section | Description |
|---|---|---|
macos |
macos: |
macOS systems |
arch |
arch: |
Arch Linux systems |
linux |
common: only |
Other Linux distributions (no dedicated section) |
common |
common: |
Always processed on all platforms |
The system always processes the common section first, then adds the platform-specific section if it exists. OS detection is handled by scripts/detect-os.sh.
The terminal configuration supports switching between WezTerm and tmux as multiplexers while maintaining consistent keybindings. This allows you to use the same muscle memory regardless of which tool handles session/window/pane management.
Switching multiplexers:
To use tmux as multiplexer:
- In
~/.config/wezterm/keymaps.lua: Comment out the multiplexer bindings (lines 48-51) - In
~/.config/tmux/keymaps.conf: Uncomment the source line (line 27)
To use WezTerm as multiplexer (default):
- In
~/.config/wezterm/keymaps.lua: Ensure multiplexer bindings are uncommented - In
~/.config/tmux/keymaps.conf: Ensure the source line is commented out
How it works:
- WezTerm loads multiplexer keybindings when uncommented, providing full session/window/pane management
- tmux loads multiplexer keybindings when uncommented, handling session/window/pane management
- Both configurations provide the same keybinding experience for navigation and management
- Simple comment/uncomment approach works reliably across all platforms and desktop environments
This dotfiles system integrates with Black Atom Industries theme adapters. The theme files in this repository are symlinks pointing to the Black Atom adapter repos, not copies of the actual theme files.
Architecture:
~/.config/ghostty/themes/black-atom-*.conf
↓ (symlink via dots link)
~/repos/nikbrunner/dots/common/.config/ghostty/themes/black-atom-*.conf
↓ (relative symlink via dots theme-link)
~/repos/black-atom-industries/ghostty/themes/**/*.conf
This two-layer approach means:
- Your home directory links to dots (managed by
dots link) - Dots links to Black Atom repos (managed by
dots theme-link)
Why relative symlinks?
The symlinks inside dots use relative paths (e.g., ../../../../../../black-atom-industries/ghostty/...) so they work on any machine as long as both repos are cloned to the same relative locations.
Commands:
# Preview what theme symlinks would be created
dots theme-link --dry-run
# Create/recreate all theme symlinks with correct relative paths
dots theme-linkWhen to run dots theme-link:
- After cloning this repo on a new machine
- If theme symlinks become absolute (git will show them as modified)
- After adding new themes to Black Atom adapter repos
Supported adapters:
| Adapter | Source | Dots Location |
|---|---|---|
| Ghostty | ~/repos/black-atom-industries/ghostty/themes/ |
common/.config/ghostty/themes/ |
| WezTerm | ~/repos/black-atom-industries/wezterm/themes/ |
common/.config/wezterm/colors/ |
| Zed | ~/repos/black-atom-industries/zed/themes/ |
common/.config/zed/themes/ |
Current submodules:
common/.config/nvim- Neovim configuration (nikbrunner/nbr.nvim)common/.config/wezterm- Wezterm configuration (nikbrunner/wezterm)
Common commands:
dots sub-add <url> <path>- Add new submoduledots sub-update- Update all submodulesdots sub-status- Show submodule statusdots sub-commit- Commit submodule hash updates
📖 Detailed Documentation: See docs/SUBMODULES.md for comprehensive submodule management guide including troubleshooting, best practices, and removal planning.
- ✅ macOS (primary development)
- ✅ Linux (EndeavourOS/Arch)
- ❌ Windows (not supported)