From 6644f00586ed4770d4db0ff91140e55a1c3700b9 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sat, 4 Jul 2026 17:27:12 +0400 Subject: [PATCH 01/45] feat(homebrew): swap 1password/superwhisper for opencode, wezterm, tmux, neovim, opensuperwhisper Removed 1Password and SuperWhisper from the setup; added opencode, wezterm, tmux, neovim, and opensuperwhisper (open-source SuperWhisper replacement) across Brewfile, setup.sh, verify.sh, docs/inventory.md, and README.md per the repo's add/remove tool checklist. --- .gitignore | 1 + Brewfile | 7 +++++-- README.md | 1 - docs/inventory.md | 7 +++++-- setup.sh | 17 ++++++++++------- verify.sh | 7 ++++--- 6 files changed, 25 insertions(+), 15 deletions(-) mode change 100644 => 100755 setup.sh diff --git a/.gitignore b/.gitignore index e43b0f9..30f8929 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +/.obsidian diff --git a/Brewfile b/Brewfile index 5cb65d4..9c79d12 100644 --- a/Brewfile +++ b/Brewfile @@ -31,6 +31,8 @@ brew "btop" # Modern resource monitor (replaces top/htop) brew "dust" # Visual disk usage (replaces du) brew "tldr" # Simplified man pages with real examples brew "atuin" # SQLite-backed shell history with search +brew "tmux" # Terminal multiplexer +brew "neovim" # Modal text editor # ---- Shell productivity ---- brew "starship" # Cross-shell prompt (actively maintained; replaces powerlevel10k) @@ -59,11 +61,13 @@ cask "gcloud-cli" # gcloud, gsutil, bq (kubectl installed on dem # ---- Terminal emulator & editors ---- cask "ghostty" # Native GPU-accelerated terminal (replaces iTerm2 in 2026) +cask "wezterm" # GPU-accelerated cross-platform terminal emulator cask "visual-studio-code" # Editor cask "cursor" # AI-native code editor # ---- AI coding tools ---- # Claude Code (CLI) is intentionally excluded — it uses its own auto-updating native installer. +brew "opencode" # AI coding agent, built for the terminal cask "claude" # Anthropic Claude desktop app cask "codex" # OpenAI Codex CLI (coding agent in terminal) cask "codex-app" # OpenAI Codex desktop app (manages coding agents) @@ -75,13 +79,12 @@ cask "chatgpt-atlas" # OpenAI's browser with ChatGPT built in # ---- Productivity & utilities ---- cask "rectangle" # Window management (keyboard-driven tiling) -cask "1password" # Password manager cask "appcleaner" # Clean app uninstalls cask "maccy" # Clipboard history manager (Cmd+Shift+C) cask "linearmouse" # Mouse customization: extra buttons, per-device scroll/accel # Essential for third-party mice with side/extra buttons. # Fixes macOS's patchy handling of back/forward buttons. -cask "superwhisper" # AI voice-to-text dictation (system-wide) +cask "opensuperwhisper" # Open-source AI voice-to-text dictation (system-wide) cask "obsidian" # Markdown-based knowledge base / note-taking # ---- API testing ---- diff --git a/README.md b/README.md index 14a0ea3..e09f222 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,6 @@ Docker Desktop also appends a CLI completions block to `~/.zshrc` on first launc ### 5. Sign Into GUI Apps -- **1Password**: sign into your account so the CLI (`op`) can authenticate later - **Chrome**: sign in, set as default browser if desired - **Cursor / VS Code**: sign in for settings sync - **Maccy**: no account, but enable "Launch at Login" in its preferences diff --git a/docs/inventory.md b/docs/inventory.md index 3716ff6..e63bb9f 100644 --- a/docs/inventory.md +++ b/docs/inventory.md @@ -25,6 +25,7 @@ All installed via `brew install --cask` unless noted otherwise. | App | Installed via | Category | Notes | |-----|---------------|----------|-------| | Ghostty | `brew install --cask` | Terminal | GPU-accelerated; replaces iTerm2 | +| WezTerm | `brew install --cask` | Terminal | GPU-accelerated, cross-platform terminal emulator | | Visual Studio Code | `brew install --cask` | Editor | | | Cursor | `brew install --cask` | Editor | AI-native code editor; global AI rules symlinked from `cursor-rules/git-commits.mdc` to `~/.cursor/rules/git-commits.mdc` | | Docker Desktop | `brew install --cask` | Containers | Provides `docker` and `docker compose` CLIs — see [Docker cheat sheet](docker-cheatsheet.md) | @@ -33,11 +34,10 @@ All installed via `brew install --cask` unless noted otherwise. | ChatGPT Atlas | `brew install --cask` | Browser | OpenAI's browser with ChatGPT built in; requires arm64 + macOS 14 | | Google Cloud CLI | `brew install --cask` | Cloud | Includes `gcloud`, `gsutil`, `bq`; kubectl installed on demand | | Rectangle | `brew install --cask` | Productivity | Keyboard-driven window tiling | -| 1Password | `brew install --cask` | Productivity | Password manager | | AppCleaner | `brew install --cask` | Productivity | Clean app uninstalls | | Maccy | `brew install --cask` | Productivity | Clipboard history (Cmd+Shift+C) | | LinearMouse | `brew install --cask` | Productivity | Mouse customization: side buttons, scroll, acceleration | -| SuperWhisper | `brew install --cask` | Productivity | AI voice-to-text dictation (system-wide) | +| OpenSuperWhisper | `brew install --cask` | Productivity | Open-source AI voice-to-text dictation (system-wide) | | Obsidian | `brew install --cask` | Productivity | Markdown-based knowledge base / note-taking | | Granola | `brew install --cask` | Productivity | AI-powered notepad for meetings | | Postman | `brew install --cask` | API Testing | REST client | @@ -61,6 +61,8 @@ All installed via `brew install` (formula) unless noted otherwise. | `jq` | JSON processor | | `tree` | Directory tree visualizer | | `wget` | HTTP downloader | +| `tmux` | Terminal multiplexer | +| `nvim` (neovim) | Modal text editor | ### Modern CLI replacements @@ -85,6 +87,7 @@ All installed via `brew install` (formula) unless noted otherwise. |------|---------------|-------------| | `codex` | `brew install --cask codex` | OpenAI Codex CLI — coding agent in terminal (cask binary; depends on `ripgrep`) | | `claude` | `curl \| bash` (claude.ai/install.sh) | Anthropic Claude Code CLI — auto-updating native installer, **not** via Homebrew | +| `opencode` | `brew install` | AI coding agent, built for the terminal | ### Shell productivity diff --git a/setup.sh b/setup.sh old mode 100644 new mode 100755 index fb478cf..3f3715e --- a/setup.sh +++ b/setup.sh @@ -138,7 +138,7 @@ if want "wget — HTTP downloader"; then formula wget; fi # ── Modern CLI Tools ────────────────────────────────────────────────────────── category "Modern CLI Tools" \ - "rg fd bat eza zoxide fzf delta lazygit btop dust tldr atuin" + "rg fd bat eza zoxide fzf delta lazygit btop dust tldr atuin tmux neovim" if want "ripgrep (rg) — fast grep, respects .gitignore"; then formula ripgrep; fi if want "fd — intuitive find replacement"; then formula fd; fi @@ -152,6 +152,8 @@ if want "btop — modern resource monitor"; then formula btop; if want "dust — tree-based disk usage"; then formula dust; fi if want "tldr — simplified man pages"; then formula tldr; fi if want "atuin — SQLite-backed shell history"; then formula atuin; fi +if want "tmux — terminal multiplexer"; then formula tmux; fi +if want "neovim — modal text editor"; then formula neovim; fi # ── Shell Productivity ──────────────────────────────────────────────────────── category "Shell Productivity" "starship antidote" @@ -191,19 +193,21 @@ category "Cloud Tooling" "Google Cloud CLI — gcloud, gsutil, bq" if want "Google Cloud CLI (gcloud, gsutil, bq)"; then cask_pkg gcloud-cli; fi # ── Editors & Terminal ──────────────────────────────────────────────────────── -category "Editors & Terminal" "Ghostty VS Code Cursor" +category "Editors & Terminal" "Ghostty WezTerm VS Code Cursor" if want "Ghostty — GPU-accelerated terminal"; then cask_pkg ghostty; fi +if want "WezTerm — GPU-accelerated terminal"; then cask_pkg wezterm; fi if want "Visual Studio Code"; then cask_pkg visual-studio-code; fi if want "Cursor — AI-native code editor"; then cask_pkg cursor; fi # ── AI Coding Tools ─────────────────────────────────────────────────────────── -category "AI Coding Tools" "Claude Code Claude desktop Codex CLI Codex desktop app" +category "AI Coding Tools" "Claude Code Claude desktop Codex CLI Codex desktop app opencode" if want "Claude Code — AI CLI (native installer)"; then DID_CLAUDE=true; fi if want "Claude — Anthropic desktop app"; then cask_pkg claude; fi if want "Codex CLI — OpenAI coding agent in terminal"; then cask_pkg codex; fi if want "Codex desktop app — manages coding agents"; then cask_pkg codex-app; fi +if want "opencode — AI coding agent in terminal"; then formula opencode; fi # ── Browser ─────────────────────────────────────────────────────────────────── category "Browser" "Google Chrome Firefox ChatGPT Atlas" @@ -214,14 +218,13 @@ if want "ChatGPT Atlas"; then cask_pkg chatgpt-atlas; fi # ── Productivity Apps ───────────────────────────────────────────────────────── category "Productivity Apps" \ - "Rectangle 1Password AppCleaner Maccy LinearMouse SuperWhisper Granola Postman Whimsical" + "Rectangle AppCleaner Maccy LinearMouse OpenSuperWhisper Granola Postman Whimsical" if want "Rectangle — keyboard-driven window tiling"; then cask_pkg rectangle; fi -if want "1Password — password manager"; then cask_pkg 1password; fi if want "AppCleaner — clean app uninstalls"; then cask_pkg appcleaner; fi if want "Maccy — clipboard history (Cmd+Shift+C)"; then cask_pkg maccy; fi if want "LinearMouse — mouse customization"; then cask_pkg linearmouse; fi -if want "SuperWhisper — AI voice-to-text dictation"; then cask_pkg superwhisper; fi +if want "OpenSuperWhisper — open-source AI voice-to-text dictation"; then cask_pkg opensuperwhisper; fi if want "Obsidian — markdown knowledge base"; then cask_pkg obsidian; fi if want "Granola — AI-powered notepad for meetings"; then cask_pkg granola; fi if want "Postman — REST client & API testing"; then cask_pkg postman; fi @@ -378,7 +381,7 @@ echo echo " 5. Launch Docker Desktop once to complete its install:" echo " open -a Docker" echo -echo " 6. Sign into GUI apps (1Password, Chrome, Cursor, VS Code)" +echo " 6. Sign into GUI apps (Chrome, Cursor, VS Code)" echo echo " 7. Authenticate Claude Code:" echo " claude" diff --git a/verify.sh b/verify.sh index 290c895..311f6cd 100644 --- a/verify.sh +++ b/verify.sh @@ -159,13 +159,14 @@ check_contains "$HOME/.zsh_plugins.txt" "zsh-syntax-highlighting" ".zsh_plugins. # ── 4. Modern CLI Tools ─────────────────────────────────────────────────────── info "4. Modern CLI Tools" -for tool in rg fd bat eza zoxide fzf delta lazygit btop dust tldr atuin; do +for tool in rg fd bat eza zoxide fzf delta lazygit btop dust tldr atuin tmux nvim; do check_command "$tool" "$tool" done # ── 4b. AI Coding CLIs ──────────────────────────────────────────────────────── info "4b. AI Coding CLIs" check_command codex "codex (OpenAI Codex CLI)" +check_command opencode "opencode (AI coding agent)" # ── 5. Git & GitHub CLI ─────────────────────────────────────────────────────── info "5. Git & GitHub" @@ -241,18 +242,18 @@ check_contains "$DOTFILES_DIR/gitconfig" "pager = delta" "dotfiles/gitconfig: de info "10. GUI Applications" APPS=( "Ghostty" + "WezTerm" "Visual Studio Code" "Cursor" "Docker" "Google Chrome" "Firefox" "ChatGPT Atlas" - "1Password" "Rectangle" "AppCleaner" "Maccy" "LinearMouse" - "SuperWhisper" + "OpenSuperWhisper" "Obsidian" "Granola" "Postman" From 8ba882fee2bbcf88714a94324da1af954dbff11b Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sat, 4 Jul 2026 17:55:36 +0400 Subject: [PATCH 02/45] refactor(configs): move dotfiles into configs/ directory, add wezterm Consolidates ghostty, git, linearmouse, and starship configs under configs/ for consistency, and adds a wezterm config alongside the existing terminal emulator options. --- README.md | 26 ++++++++------- ghostty-config => configs/ghostty-config | 0 gitconfig => configs/gitconfig | 0 linearmouse.json => configs/linearmouse.json | 0 starship.toml => configs/starship.toml | 0 configs/wezterm.lua | 33 ++++++++++++++++++++ docs/ghostty-cheatsheet.md | 4 +-- docs/inventory.md | 2 +- docs/modern-cli-cheatsheet.md | 8 ++--- setup.sh | 13 +++++--- verify.sh | 7 +++-- 11 files changed, 67 insertions(+), 26 deletions(-) rename ghostty-config => configs/ghostty-config (100%) rename gitconfig => configs/gitconfig (100%) rename linearmouse.json => configs/linearmouse.json (100%) rename starship.toml => configs/starship.toml (100%) create mode 100644 configs/wezterm.lua diff --git a/README.md b/README.md index e09f222..4a24df7 100644 --- a/README.md +++ b/README.md @@ -211,9 +211,9 @@ System Settings > Desktop & Dock > Scroll to "Default web browser / Default term The defaults are chosen carefully, but if you want to customize: -- **Ghostty theme:** edit `ghostty-config`, change the `theme = ...` line. Preview options with `ghostty +list-themes`. -- **Font:** the Brewfile installs both `font-jetbrains-mono-nerd-font` (default) and `font-fira-code-nerd-font`. Change `font-family` in `ghostty-config`. -- **Starship prompt:** edit `starship.toml`. See [starship.rs/presets](https://starship.rs/presets) for ready-made layouts. +- **Ghostty theme:** edit `configs/ghostty-config`, change the `theme = ...` line. Preview options with `ghostty +list-themes`. +- **Font:** the Brewfile installs both `font-jetbrains-mono-nerd-font` (default) and `font-fira-code-nerd-font`. Change `font-family` in `configs/ghostty-config`. +- **Starship prompt:** edit `configs/starship.toml`. See [starship.rs/presets](https://starship.rs/presets) for ready-made layouts. - **Shell aliases:** edit `.zshrc` and run `reload`. All configs live in this repo and are symlinked, so changes are preserved in git. @@ -249,9 +249,10 @@ This repo is meant to be forked and personalized. The files worth editing: | `.tool-versions` | Language runtime versions. Edit and run `asdf install`. | | `.zshrc` | Aliases, env vars, tool integration. Run `reload` after editing. | | `.zsh_plugins.txt` | Zsh plugins loaded by Antidote. | -| `starship.toml` | Prompt appearance. | -| `ghostty-config` | Terminal appearance and keybindings. Reload with `Cmd+Shift+,`. | -| `linearmouse.json` | Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. | +| `configs/starship.toml` | Prompt appearance. | +| `configs/ghostty-config` | Terminal appearance and keybindings. Reload with `Cmd+Shift+,`. | +| `configs/wezterm.lua` | WezTerm terminal appearance. | +| `configs/linearmouse.json` | Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. | | `setup.sh` | Bootstrap steps. Only touch if you add new tools needing custom setup. | After any changes, commit them to your dotfiles repo. Other machines pick up changes with `git pull && ./setup.sh`. @@ -332,7 +333,7 @@ git push ### Icons show as squares in Starship / eza -Your terminal isn't using a Nerd Font. Check `ghostty-config`: +Your terminal isn't using a Nerd Font. Check `configs/ghostty-config`: ``` font-family = JetBrainsMono Nerd Font @@ -346,7 +347,7 @@ Accessibility permission not granted. Go to System Settings > Privacy & Security ### SSH session looks broken (vim / less render incorrectly) -The remote host doesn't have Ghostty's terminfo. Your `ghostty-config` already sets `term = xterm-256color` to avoid this, but if you removed that line, re-add it. +The remote host doesn't have Ghostty's terminfo. Your `configs/ghostty-config` already sets `term = xterm-256color` to avoid this, but if you removed that line, re-add it. ### asdf says "No version is set for command X" @@ -424,9 +425,12 @@ dotfiles/ ├── .tool-versions # asdf runtime versions (Node, Python, Go) ├── .zshrc # shell config (aliases, tool integration) ├── .zsh_plugins.txt # Antidote plugin list -├── starship.toml # Starship prompt config -├── ghostty-config # Ghostty terminal config -├── linearmouse.json # Mouse customization (side buttons, acceleration) +├── configs/ +│ ├── starship.toml # Starship prompt config +│ ├── ghostty-config # Ghostty terminal config +│ ├── wezterm.lua # WezTerm terminal config +│ ├── linearmouse.json # Mouse customization (side buttons, acceleration) +│ └── gitconfig # Delta pager + git config, included via ~/.gitconfig └── docs/ ├── inventory.md # full list of installed apps, tools, and runtimes ├── homebrew-cheatsheet.md diff --git a/ghostty-config b/configs/ghostty-config similarity index 100% rename from ghostty-config rename to configs/ghostty-config diff --git a/gitconfig b/configs/gitconfig similarity index 100% rename from gitconfig rename to configs/gitconfig diff --git a/linearmouse.json b/configs/linearmouse.json similarity index 100% rename from linearmouse.json rename to configs/linearmouse.json diff --git a/starship.toml b/configs/starship.toml similarity index 100% rename from starship.toml rename to configs/starship.toml diff --git a/configs/wezterm.lua b/configs/wezterm.lua new file mode 100644 index 0000000..cc2a442 --- /dev/null +++ b/configs/wezterm.lua @@ -0,0 +1,33 @@ +local wezterm = require("wezterm") + +local config = wezterm.config_builder() + +local is_windows = os.getenv("OS") and os.getenv("OS"):lower():find("windows") +local is_macos = wezterm.target_triple:lower():find("darwin") ~= nil + +config.color_scheme = "rose-pine-moon" +config.max_fps = 120 +config.font = wezterm.font("Hack Nerd Font", { weight = "DemiBold" }) +config.window_decorations = "INTEGRATED_BUTTONS|RESIZE" +config.window_frame = { + font = wezterm.font("Hack Nerd Font", { weight = "Bold" }), +} +config.inactive_pane_hsb = { + saturation = 0.0, + brightness = 0.5, +} + +if is_windows then + config.win32_system_backdrop = "Acrylic" + config.window_background_opacity = 0.7 + config.window_frame.font_size = 10.0 +end + +if is_macos then + config.window_background_opacity = 0.8 + config.macos_window_background_blur = 50 + config.font_size = 15.0 + config.window_frame.font_size = 13.0 +end + +return config diff --git a/docs/ghostty-cheatsheet.md b/docs/ghostty-cheatsheet.md index cb85162..2ff933b 100644 --- a/docs/ghostty-cheatsheet.md +++ b/docs/ghostty-cheatsheet.md @@ -49,7 +49,7 @@ These ship with Ghostty and don't require config. All use the `Cmd` modifier to | `Cmd+Shift+E` | Equalize splits | | `Cmd+Shift+F` | Zooms into one panel (press again to restore) | -Note: Split *creation* keybindings aren't default; they're in your `ghostty-config`. Navigation between existing splits is fully default on macOS. (`Alt` in shortcuts above = the key labeled Option `⌥`.) +Note: Split *creation* keybindings aren't default; they're in your `configs/ghostty-config`. Navigation between existing splits is fully default on macOS. (`Alt` in shortcuts above = the key labeled Option `⌥`.) ### Clipboard @@ -391,7 +391,7 @@ Do this once per remote you care about. ### Commit your config to dotfiles -`~/.config/ghostty/config` is plain text. Version it. In this repo it's at `ghostty-config` in the root and symlinked into place by `setup.sh`. +`~/.config/ghostty/config` is plain text. Version it. In this repo it's at `configs/ghostty-config` and symlinked into place by `setup.sh`. ### Keep the config minimal diff --git a/docs/inventory.md b/docs/inventory.md index e63bb9f..e2b1a52 100644 --- a/docs/inventory.md +++ b/docs/inventory.md @@ -74,7 +74,7 @@ All installed via `brew install` (formula) unless noted otherwise. | `eza` | `ls` | Modern listing with icons, git status, tree view | | `zoxide` (`z`) | `cd` | Learns habits; jump to dirs by partial name | | `fzf` | — | Fuzzy finder for history, files, branches, processes | -| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; config in `gitconfig`, wired via `[include]` in `~/.gitconfig` | +| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; config in `configs/gitconfig`, wired via `[include]` in `~/.gitconfig` | | `lazygit` (`lg`) | — | Full terminal UI for git | | `btop` | `top` / `htop` | Modern resource monitor with graphs | | `dust` | `du` | Tree-based disk usage visualizer | diff --git a/docs/modern-cli-cheatsheet.md b/docs/modern-cli-cheatsheet.md index 9bb32e8..e10450e 100644 --- a/docs/modern-cli-cheatsheet.md +++ b/docs/modern-cli-cheatsheet.md @@ -452,15 +452,15 @@ Delta is a pager for git diffs. Once configured, every `git diff`, `git log -p`, ### Setup -Automated by `setup.sh` — no manual steps needed. The config lives in `dotfiles/gitconfig` and is wired into `~/.gitconfig` via `[include]`, leaving your personal identity settings untouched. +Automated by `setup.sh` — no manual steps needed. The config lives in `dotfiles/configs/gitconfig` and is wired into `~/.gitconfig` via `[include]`, leaving your personal identity settings untouched. To apply manually on an existing machine: ```bash -git config --global --add include.path ~/dotfiles/gitconfig +git config --global --add include.path ~/dotfiles/configs/gitconfig ``` -Config applied (`dotfiles/gitconfig`): +Config applied (`dotfiles/configs/gitconfig`): ```ini [core] @@ -706,4 +706,4 @@ Most tools in this cheat sheet respect these locations: | zoxide | None; controlled by env vars; database at `~/.local/share/zoxide/db.zo` | | fd | None; use shell aliases if you want different defaults | -All configs in this repo are either in the root (`.zshrc`, `starship.toml`) or commented into `.zshrc` itself for these tools. +All configs in this repo are either in `configs/` (`starship.toml`, `ghostty-config`, etc.), in the root (`.zshrc`), or commented into `.zshrc` itself for these tools. diff --git a/setup.sh b/setup.sh index 3f3715e..294f93c 100755 --- a/setup.sh +++ b/setup.sh @@ -250,8 +250,11 @@ ln -sf "$DOTFILES_DIR/.zsh_plugins.txt" "$HOME/.zsh_plugins.txt" ln -sf "$DOTFILES_DIR/.tool-versions" "$HOME/.tool-versions" mkdir -p "$HOME/.config/ghostty" -ln -sf "$DOTFILES_DIR/ghostty-config" "$HOME/.config/ghostty/config" -ln -sf "$DOTFILES_DIR/starship.toml" "$HOME/.config/starship.toml" +ln -sf "$DOTFILES_DIR/configs/ghostty-config" "$HOME/.config/ghostty/config" +ln -sf "$DOTFILES_DIR/configs/starship.toml" "$HOME/.config/starship.toml" + +mkdir -p "$HOME/.config/wezterm" +ln -sf "$DOTFILES_DIR/configs/wezterm.lua" "$HOME/.config/wezterm/wezterm.lua" # LinearMouse: back up any pre-existing real file before replacing with symlink. mkdir -p "$HOME/.config/linearmouse" @@ -261,7 +264,7 @@ if [[ -f "$LINEARMOUSE_TARGET" && ! -L "$LINEARMOUSE_TARGET" ]]; then info "Existing LinearMouse config found; backing up to $(basename "$BACKUP")" mv "$LINEARMOUSE_TARGET" "$BACKUP" fi -ln -sf "$DOTFILES_DIR/linearmouse.json" "$LINEARMOUSE_TARGET" +ln -sf "$DOTFILES_DIR/configs/linearmouse.json" "$LINEARMOUSE_TARGET" mkdir -p "$HOME/.claude" ln -sf "$DOTFILES_DIR/claude/CLAUDE.md" "$HOME/.claude/CLAUDE.md" @@ -276,8 +279,8 @@ ln -sf "$DOTFILES_DIR/cursor-rules/git-commits.mdc" "$HOME/.cursor/rules/git-com # Wire delta + merge config via git include (keeps personal user info separate). # Guard against duplicates on re-runs — git config --add would append every time. -if ! git config --global --get-all include.path | grep -qF "$DOTFILES_DIR/gitconfig"; then - git config --global --add include.path "$DOTFILES_DIR/gitconfig" +if ! git config --global --get-all include.path | grep -qF "$DOTFILES_DIR/configs/gitconfig"; then + git config --global --add include.path "$DOTFILES_DIR/configs/gitconfig" fi done_ "Dotfiles linked" diff --git a/verify.sh b/verify.sh index 311f6cd..b944610 100644 --- a/verify.sh +++ b/verify.sh @@ -205,6 +205,7 @@ check_symlink "$HOME/.tool-versions" "~/.tool-versions" check_symlink "$HOME/.config/ghostty/config" "~/.config/ghostty/config" check_symlink "$HOME/.config/starship.toml" "~/.config/starship.toml" check_symlink "$HOME/.config/linearmouse/linearmouse.json" "~/.config/linearmouse/linearmouse.json" +check_symlink "$HOME/.config/wezterm/wezterm.lua" "~/.config/wezterm/wezterm.lua" check_symlink "$HOME/.claude/CLAUDE.md" "~/.claude/CLAUDE.md" check_symlink "$HOME/.claude/settings.json" "~/.claude/settings.json" check_symlink "$HOME/.claude/statusline-command.sh" "~/.claude/statusline-command.sh" @@ -234,9 +235,9 @@ fi # LinearMouse — must be valid JSON check_json "$HOME/.config/linearmouse/linearmouse.json" "linearmouse.json" -# Delta git config — included from dotfiles/gitconfig -check_contains "$HOME/.gitconfig" "include" "~/.gitconfig: includes dotfiles/gitconfig" -check_contains "$DOTFILES_DIR/gitconfig" "pager = delta" "dotfiles/gitconfig: delta pager configured" +# Delta git config — included from configs/gitconfig +check_contains "$HOME/.gitconfig" "include" "~/.gitconfig: includes configs/gitconfig" +check_contains "$DOTFILES_DIR/configs/gitconfig" "pager = delta" "configs/gitconfig: delta pager configured" # ── 10. GUI Applications ────────────────────────────────────────────────────── info "10. GUI Applications" From 299ce7e86c09366c3c937a7405ee577d4dd66176 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sat, 4 Jul 2026 18:20:32 +0400 Subject: [PATCH 03/45] feat(wezterm): align font and split keybindings with ghostty Matches JetBrainsMono Nerd Font at size 14 and mirrors Ghostty's custom split creation/navigation/zoom keybinds for consistent muscle memory across both terminals. --- configs/wezterm.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/configs/wezterm.lua b/configs/wezterm.lua index cc2a442..61bd2cc 100644 --- a/configs/wezterm.lua +++ b/configs/wezterm.lua @@ -1,4 +1,5 @@ local wezterm = require("wezterm") +local act = wezterm.action local config = wezterm.config_builder() @@ -7,16 +8,32 @@ local is_macos = wezterm.target_triple:lower():find("darwin") ~= nil config.color_scheme = "rose-pine-moon" config.max_fps = 120 -config.font = wezterm.font("Hack Nerd Font", { weight = "DemiBold" }) +-- Matches configs/ghostty-config font choice for a consistent look across terminals. +config.font = wezterm.font("JetBrainsMono Nerd Font", { weight = "DemiBold" }) +config.font_size = 14.0 config.window_decorations = "INTEGRATED_BUTTONS|RESIZE" config.window_frame = { - font = wezterm.font("Hack Nerd Font", { weight = "Bold" }), + font = wezterm.font("JetBrainsMono Nerd Font", { weight = "Bold" }), } config.inactive_pane_hsb = { saturation = 0.0, brightness = 0.5, } +-- ---- Split navigation ---- +-- Mirrors configs/ghostty-config's custom split keybinds for muscle-memory parity. +config.keys = { + { key = "d", mods = "CMD", action = act.SplitPane({ direction = "Right" }) }, + { key = "d", mods = "CMD|SHIFT", action = act.SplitPane({ direction = "Down" }) }, + { key = "LeftArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Left") }, + { key = "RightArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Right") }, + { key = "UpArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Up") }, + { key = "DownArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Down") }, + { key = "f", mods = "CMD|SHIFT", action = act.TogglePaneZoomState }, +} +-- Note: Ghostty's Cmd+Shift+E "equalize splits" has no WezTerm equivalent; +-- WezTerm has no built-in quake-style quick terminal either (would need Hammerspoon). + if is_windows then config.win32_system_backdrop = "Acrylic" config.window_background_opacity = 0.7 @@ -26,7 +43,6 @@ end if is_macos then config.window_background_opacity = 0.8 config.macos_window_background_blur = 50 - config.font_size = 15.0 config.window_frame.font_size = 13.0 end From c114fa3be3aa6dfa5f1abe02271a33744f15068c Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 02:02:43 +0400 Subject: [PATCH 04/45] refactor(nix): migrate macOS setup to nix-darwin and home-manager Replace the imperative Brewfile/setup.sh bootstrap with a declarative flake, move live configs under home/, and update docs plus verify for the new rebuild workflow. Co-authored-by: Cursor --- .gitignore | 9 + Brewfile | 103 +---- CLAUDE.md | 104 +++-- README.md | 265 +++++++----- bootstrap.sh | 203 +++++++++ claude/settings.json | 9 - claude/statusline-command.sh | 43 -- codex-instructions.md | 41 -- configs/gitconfig | 2 +- configs/starship.toml | 50 --- configs/wezterm.lua | 49 --- configuration.nix | 108 +++++ cursor-rules/git-commits.mdc | 46 -- docs/cheatsheets/README.md | 41 ++ docs/{ => cheatsheets}/asdf-cheatsheet.md | 10 + .../claude-code-cheatsheet.md | 13 +- .../cursor-cli-cheatsheet.md | 8 + docs/{ => cheatsheets}/docker-cheatsheet.md | 8 + docs/{ => cheatsheets}/ghostty-cheatsheet.md | 13 +- docs/{ => cheatsheets}/git-cheatsheet.md | 9 + docs/{ => cheatsheets}/homebrew-cheatsheet.md | 10 + docs/{ => cheatsheets}/lazygit-cheatsheet.md | 8 + .../modern-cli-cheatsheet.md | 18 +- docs/cheatsheets/nix-cheatsheet.md | 161 +++++++ docs/cheatsheets/vim-cheat-sheet.md | 333 +++++++++++++++ docs/inventory.md | 147 ++++--- flake.lock | 107 +++++ flake.nix | 40 ++ home.nix | 226 ++++++++++ home/.claude/settings.json | 7 + .../.config/ghostty/config | 9 +- home/.config/herdr/config.toml | 13 + .../.config/linearmouse}/linearmouse.json | 0 home/.config/nvim/init.lua | 3 + home/.config/nvim/lazy-lock.json | 11 + home/.config/nvim/lua/keys.lua | 6 + home/.config/nvim/lua/plugin.lua | 7 + home/.config/nvim/lua/plugins/colorscheme.lua | 27 ++ home/.config/nvim/lua/plugins/git.lua | 12 + home/.config/nvim/lua/plugins/navigation.lua | 23 + home/.config/nvim/lua/plugins/ui.lua | 7 + home/.config/nvim/lua/vim_config.lua | 11 + home/.config/wezterm/wezterm.lua | 13 + claude/CLAUDE.md => home/AGENTS.md | 0 rebuild.sh | 19 + setup.sh | 398 ------------------ update.sh | 40 +- verify.sh | 207 +++++---- 48 files changed, 1911 insertions(+), 1086 deletions(-) create mode 100755 bootstrap.sh delete mode 100644 claude/settings.json delete mode 100644 claude/statusline-command.sh delete mode 100644 codex-instructions.md delete mode 100644 configs/starship.toml delete mode 100644 configs/wezterm.lua create mode 100644 configuration.nix delete mode 100644 cursor-rules/git-commits.mdc create mode 100644 docs/cheatsheets/README.md rename docs/{ => cheatsheets}/asdf-cheatsheet.md (98%) rename docs/{ => cheatsheets}/claude-code-cheatsheet.md (97%) rename docs/{ => cheatsheets}/cursor-cli-cheatsheet.md (99%) rename docs/{ => cheatsheets}/docker-cheatsheet.md (99%) rename docs/{ => cheatsheets}/ghostty-cheatsheet.md (96%) rename docs/{ => cheatsheets}/git-cheatsheet.md (99%) rename docs/{ => cheatsheets}/homebrew-cheatsheet.md (98%) rename docs/{ => cheatsheets}/lazygit-cheatsheet.md (99%) rename docs/{ => cheatsheets}/modern-cli-cheatsheet.md (94%) create mode 100644 docs/cheatsheets/nix-cheatsheet.md create mode 100644 docs/cheatsheets/vim-cheat-sheet.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 home.nix create mode 100644 home/.claude/settings.json rename configs/ghostty-config => home/.config/ghostty/config (86%) create mode 100644 home/.config/herdr/config.toml rename {configs => home/.config/linearmouse}/linearmouse.json (100%) create mode 100644 home/.config/nvim/init.lua create mode 100644 home/.config/nvim/lazy-lock.json create mode 100644 home/.config/nvim/lua/keys.lua create mode 100644 home/.config/nvim/lua/plugin.lua create mode 100644 home/.config/nvim/lua/plugins/colorscheme.lua create mode 100644 home/.config/nvim/lua/plugins/git.lua create mode 100644 home/.config/nvim/lua/plugins/navigation.lua create mode 100644 home/.config/nvim/lua/plugins/ui.lua create mode 100644 home/.config/nvim/lua/vim_config.lua create mode 100644 home/.config/wezterm/wezterm.lua rename claude/CLAUDE.md => home/AGENTS.md (100%) create mode 100755 rebuild.sh delete mode 100755 setup.sh mode change 100644 => 100755 verify.sh diff --git a/.gitignore b/.gitignore index 30f8929..722414a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,11 @@ .DS_Store /.obsidian + +# Nix build artifacts (created by `nix build` without -o; never commit) +result +result-* + +# herdr runtime artifacts (regenerated at runtime; never commit) +home/.config/herdr/*.log +home/.config/herdr/session.json +home/.config/herdr/*.sock diff --git a/Brewfile b/Brewfile index 9c79d12..e8dc9c1 100644 --- a/Brewfile +++ b/Brewfile @@ -1,98 +1,9 @@ -# Brewfile: macOS Developer Setup +# Brewfile: RETIRED — kept only as a pointer for anyone who lands here out +# of habit or an old bookmark. # -# Usage: -# brew bundle # install everything -# brew bundle check # dry-run check -# brew bundle cleanup # remove anything not listed here -# brew bundle list --all # show what would be installed +# Package management moved to Nix: +# - GUI apps (casks) and the handful of formulae still worth Homebrew +# (asdf + build deps, tapped tools) -> configuration.nix's `homebrew` block +# - CLI tools available in nixpkgs -> home.nix's home.packages # -# Language runtimes are NOT in this file by design; they're managed by asdf -# via a committed .tool-versions file. See .tool-versions alongside this -# Brewfile. Claude Code is also excluded by design: it uses its own -# auto-updating native installer. - -# ---- Version control & core utilities ---- -brew "git" # Version control -brew "gh" # GitHub CLI -brew "jq" # JSON processor -brew "tree" # Directory tree visualizer -brew "wget" # HTTP downloader - -# ---- Modern CLI replacements (Rust-based, drop-in upgrades) ---- -brew "ripgrep" # Fast recursive grep (rg); respects .gitignore -brew "fd" # Modern find: intuitive syntax, parallel execution -brew "bat" # cat with syntax highlighting and git integration -brew "eza" # Modern ls: icons, git status, tree view -brew "zoxide" # Smarter cd: jumps to directories by partial name -brew "fzf" # Fuzzy finder: powers Ctrl+R, file pickers, etc. -brew "git-delta" # Syntax-highlighted git diffs -brew "lazygit" # Terminal UI for git -brew "btop" # Modern resource monitor (replaces top/htop) -brew "dust" # Visual disk usage (replaces du) -brew "tldr" # Simplified man pages with real examples -brew "atuin" # SQLite-backed shell history with search -brew "tmux" # Terminal multiplexer -brew "neovim" # Modal text editor - -# ---- Shell productivity ---- -brew "starship" # Cross-shell prompt (actively maintained; replaces powerlevel10k) -brew "antidote" # Zsh plugin manager (fast, static-generated loader) - -# ---- Language version manager ---- -# Runtimes for Node.js, Python, Go are declared in .tool-versions. -# After `brew bundle`, run `asdf install` to fetch them. -brew "asdf" - -# ---- Language-adjacent build tools ---- -# Needed by some asdf plugins and native modules during compilation. -brew "coreutils" # GNU core utilities (required by asdf on macOS) -brew "openssl@3" # TLS library (Python, Node native modules) -brew "readline" # Line-editing library (Python build) -brew "xz" # Compression library (Python build) - -# ---- Containers ---- -# Docker Desktop provides both the Docker daemon and docker/docker-compose CLIs. -# All local databases (PostgreSQL, Redis, etc.) run as containers per your setup. -cask "docker-desktop" - -# ---- Cloud tooling ---- -cask "gcloud-cli" # gcloud, gsutil, bq (kubectl installed on demand) - # Formerly the "google-cloud-sdk" formula; renamed + moved to cask in 2025. - -# ---- Terminal emulator & editors ---- -cask "ghostty" # Native GPU-accelerated terminal (replaces iTerm2 in 2026) -cask "wezterm" # GPU-accelerated cross-platform terminal emulator -cask "visual-studio-code" # Editor -cask "cursor" # AI-native code editor - -# ---- AI coding tools ---- -# Claude Code (CLI) is intentionally excluded — it uses its own auto-updating native installer. -brew "opencode" # AI coding agent, built for the terminal -cask "claude" # Anthropic Claude desktop app -cask "codex" # OpenAI Codex CLI (coding agent in terminal) -cask "codex-app" # OpenAI Codex desktop app (manages coding agents) - -# ---- Browsers ---- -cask "google-chrome" -cask "firefox" -cask "chatgpt-atlas" # OpenAI's browser with ChatGPT built in - -# ---- Productivity & utilities ---- -cask "rectangle" # Window management (keyboard-driven tiling) -cask "appcleaner" # Clean app uninstalls -cask "maccy" # Clipboard history manager (Cmd+Shift+C) -cask "linearmouse" # Mouse customization: extra buttons, per-device scroll/accel - # Essential for third-party mice with side/extra buttons. - # Fixes macOS's patchy handling of back/forward buttons. -cask "opensuperwhisper" # Open-source AI voice-to-text dictation (system-wide) -cask "obsidian" # Markdown-based knowledge base / note-taking - -# ---- API testing ---- -cask "granola" # AI-powered notepad for meetings -cask "postman" # REST client & API testing -cask "whimsical" # Collaboration and diagramming tool - -# ---- Developer fonts ---- -# Nerd Fonts include glyphs required by Starship, eza icons, and modern TUI tools. -cask "font-jetbrains-mono-nerd-font" # Recommended: primary coding font -cask "font-fira-code-nerd-font" # Alternative with strong ligatures +# See docs/nix-cheatsheet.md for the day-to-day workflow (./rebuild.sh etc). diff --git a/CLAUDE.md b/CLAUDE.md index 49bde3c..b85b827 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,9 +9,9 @@ Before answering any question about this Mac dev setup, shell tools, CLI tools, | Question type | Search here first | |---|---| | What is installed / how | `docs/inventory.md` | -| How-to / CLI reference | `docs/` cheat sheets (see index below) | -| Bootstrap / wiring | `setup.sh`, `verify.sh`, `Brewfile`, `.tool-versions` | -| Live config / "what is set?" | Root dotfiles: `.zshrc`, `starship.toml`, `ghostty-config` | +| How-to / CLI reference | `docs/cheatsheets/` (see index below) | +| Bootstrap / wiring | `flake.nix`, `configuration.nix`, `home.nix`, `bootstrap.sh`, `rebuild.sh`, `verify.sh` | +| Live config / "what is set?" | `home/.config/{wezterm,ghostty,nvim,herdr,linearmouse}/`, `home.nix`'s `programs.starship`/`programs.zsh` blocks | | Setup walkthrough / daily commands | `README.md` | ### docs/ index @@ -21,14 +21,17 @@ See [`README.md` — Cheat Sheets & References](README.md#cheat-sheets--referenc | Topic | File | |-------|------| | Installed software (what, how installed) | `docs/inventory.md` | -| Homebrew / Brewfile | `docs/homebrew-cheatsheet.md` | -| Language runtimes | `docs/asdf-cheatsheet.md` | -| Terminal keybindings / config | `docs/ghostty-cheatsheet.md` | -| Git workflows | `docs/git-cheatsheet.md` | -| Git TUI | `docs/lazygit-cheatsheet.md` | -| Modern CLI tools | `docs/modern-cli-cheatsheet.md` | -| Claude Code CLI | `docs/claude-code-cheatsheet.md` | -| Cursor IDE | `docs/cursor-cli-cheatsheet.md` | +| Nix / nix-darwin / home-manager workflow | `docs/cheatsheets/nix-cheatsheet.md` | +| Homebrew (via nix-homebrew) | `docs/cheatsheets/homebrew-cheatsheet.md` | +| Language runtimes | `docs/cheatsheets/asdf-cheatsheet.md` | +| Terminal keybindings / config | `docs/cheatsheets/ghostty-cheatsheet.md` | +| Git workflows | `docs/cheatsheets/git-cheatsheet.md` | +| Git TUI | `docs/cheatsheets/lazygit-cheatsheet.md` | +| Modern CLI tools | `docs/cheatsheets/modern-cli-cheatsheet.md` | +| Claude Code CLI | `docs/cheatsheets/claude-code-cheatsheet.md` | +| Cursor IDE | `docs/cheatsheets/cursor-cli-cheatsheet.md` | +| Docker | `docs/cheatsheets/docker-cheatsheet.md` | +| Vim/Neovim keybindings | `docs/cheatsheets/vim-cheat-sheet.md` | ### How to search @@ -47,11 +50,13 @@ Never duplicate these. Every other place must link, not repeat. | Data | Authoritative file | What goes here | |------|--------------------|----------------| -| Installed packages (formulae + casks) | `Brewfile` | Every `brew install` and `brew install --cask` | -| Language runtime versions | `.tool-versions` | Version numbers only — never written in docs | -| Zsh plugin list | `.zsh_plugins.txt` | Plugin names only — never written in docs | +| GUI apps (casks), asdf itself + build deps, tapped tools (terraform, stripe) | `configuration.nix`'s `homebrew` block | Every `homebrew.brews` / `homebrew.casks` entry. Rule of thumb: `.app` bundles and things not sensibly packaged in nixpkgs stay here. | +| CLI tools available in nixpkgs | `home.nix`'s `home.packages` | Everything installable straight from nixpkgs (ripgrep, fd, bat, lazygit, etc.) | +| Zsh plugins, aliases, keybinds, Starship prompt | `home.nix`'s `programs.zsh` / `programs.starship` blocks | Antidote is retired — native home-manager zsh plugin toggles replace it. There is no more standalone plugin-list file. | +| Dotfile symlink targets | `home.nix`'s `home.file` / `mkOutOfStoreSymlink` entries | Every path under `~` that should point back into this repo | +| Language runtime versions | `.tool-versions` | Version numbers only — never written in docs. Runtimes stay asdf-managed by design (not Nix-managed) — see README's Design Decisions. | | Human-readable inventory | `docs/inventory.md` | What's installed, how, one row per item | -| Detailed command references | `docs/*-cheatsheet.md` | Full usage, flags, examples, gotchas | +| Detailed command references | `docs/cheatsheets/*-cheatsheet.md` | Full usage, flags, examples, gotchas | | Orientation + links | `README.md` | Overview only — links to cheatsheets, no duplicated content | --- @@ -63,8 +68,8 @@ Never duplicate these. Every other place must link, not repeat. - If a table, code block, or list already exists in one doc, all other docs must link to it — never copy it. - `README.md` is orientation only: it describes categories and links out. It does not contain tool lists, keybinding tables, command blocks, or version numbers. - Version numbers appear only in `.tool-versions`. Docs name the language (e.g. "Node.js") but never the version. -- Plugin names appear only in `.zsh_plugins.txt`. Docs say "see `.zsh_plugins.txt`" rather than listing plugins. -- When adding content from the web: place it in the single most relevant `docs/*-cheatsheet.md`. Do not add it to README as well. +- Package lists appear only in `configuration.nix` (Homebrew) and `home.nix` (Nix packages). Docs say "see `configuration.nix`" / "see `home.nix`" rather than listing packages. +- When adding content from the web: place it in the single most relevant `docs/cheatsheets/*-cheatsheet.md`. Do not add it to README as well. --- @@ -72,40 +77,46 @@ Never duplicate these. Every other place must link, not repeat. Work through every applicable item. Skipping any item leaves the repo inconsistent. -### Adding a Homebrew formula +### Adding a Homebrew cask (GUI app) + +- [ ] Add to `configuration.nix`'s `homebrew.casks` +- [ ] Add a row to the GUI Applications table in `docs/inventory.md` (include "Installed via: `brew install --cask`" or "configuration.nix's homebrew.casks") +- [ ] Add to the `APPS` array in `verify.sh` +- [ ] If the app writes back to a config file, add a `home.file` entry in `home.nix` using `config.lib.file.mkOutOfStoreSymlink` (NOT plain `home.file`, which symlinks from the read-only Nix store) plus a `home.activation` script that backs up any pre-existing real file before the symlink lands — see the `linearmouse.json` pattern in `home.nix` (`home.activation.backupLinearMouseConfig`) +- [ ] Run `./rebuild.sh` + +### Adding a Homebrew formula (only for things that must stay Homebrew — e.g. asdf, build-support libs, tapped tools) -- [ ] Add to `Brewfile` under the correct section comment +- [ ] Add to `configuration.nix`'s `homebrew.brews` (add a `homebrew.taps` entry too if it's from a third-party tap) - [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: `brew install`") -- [ ] Add to the matching category block in `setup.sh` with a `want "..."` + `formula ...` line -- [ ] Add to the CLI tools loop in `verify.sh` if it provides a binary +- [ ] Run `./rebuild.sh` -### Adding a Homebrew cask (GUI app) +### Adding a Nix-native CLI package (tools available in nixpkgs — the default choice for new CLI tools) -- [ ] Add to `Brewfile` under the correct section comment -- [ ] Add a row to the GUI Applications table in `docs/inventory.md` (include "Installed via: `brew install --cask`") -- [ ] Add to the matching category block in `setup.sh` with a `want "..."` + `cask_pkg ...` line -- [ ] Add to the `APPS` array in `verify.sh` -- [ ] If the app writes back to a config file, symlink that file into the dotfiles repo so changes persist (see `linearmouse.json` pattern in `setup.sh`) +- [ ] Check it exists in nixpkgs first (`nix search nixpkgs `), not `brew search` +- [ ] Add to `home.nix`'s `home.packages` +- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: Nix (home.packages)") +- [ ] Add to the `check_command` loop in `verify.sh` if it provides a binary +- [ ] Run `./rebuild.sh` ### Adding a language runtime (asdf) - [ ] Add to `.tool-versions` (version number goes here and nowhere else) - [ ] Add a row to the Language Runtimes table in `docs/inventory.md` (language name and `.tool-versions` key only — no version number) -- [ ] Add `asdf plugin add ` to the asdf section in `setup.sh` -- [ ] Add the language to the runtime loop in `verify.sh` +- [ ] Run `./bootstrap.sh` (registers the asdf plugin and runs `asdf install` for anything new in `.tool-versions`) +- [ ] Confirm the language's runtime-version check loop in `verify.sh` covers it -### Adding a Zsh plugin +### Adding a zsh alias / keybind -- [ ] Add to `.zsh_plugins.txt` (plugin entry goes here and nowhere else) -- [ ] Add a row to the Zsh plugins table in `docs/inventory.md` -- [ ] Add a `check_contains` line for the plugin in `verify.sh` +- [ ] Add to `home.nix`'s `programs.zsh.shellAliases` (aliases) or `programs.zsh.initContent` (keybinds, env vars, PATH changes) +- [ ] Run `./rebuild.sh` ### Removing anything -- [ ] Remove from `Brewfile` / `.tool-versions` / `.zsh_plugins.txt` +- [ ] Remove from `configuration.nix` / `home.nix` / `.tool-versions` - [ ] Remove the corresponding row from `docs/inventory.md` -- [ ] Remove from `setup.sh` -- [ ] Remove from `verify.sh` +- [ ] Remove from `verify.sh` (`check_command` loop, `APPS` array, or `check_contains`/`check_resolves_to_repo` calls as applicable) +- [ ] Run `./rebuild.sh` — note `homebrew.onActivation.cleanup = "none"` means removed Homebrew packages are left installed but undeclared, not force-uninstalled; `brew uninstall` manually if you want it gone from disk too --- @@ -113,25 +124,30 @@ Work through every applicable item. Skipping any item leaves the repo inconsiste `README.md` must never contain: - Lists of tool or app names (link to `docs/inventory.md`) -- Keybinding tables (link to `docs/ghostty-cheatsheet.md` or `docs/modern-cli-cheatsheet.md`) +- Keybinding tables (link to `docs/cheatsheets/ghostty-cheatsheet.md` or `docs/cheatsheets/modern-cli-cheatsheet.md`) - Command examples that are already in a cheatsheet (link to the cheatsheet) - Version numbers (link to `.tool-versions`) -- Plugin names (link to `.zsh_plugins.txt`) +- Package lists (link to `configuration.nix` / `home.nix`) When editing README, ask: "does this content already exist in a cheatsheet or inventory?" If yes, replace with a link. --- -## 6. verify.sh must stay in sync +## 6. verify.sh and nix flake check — two layers of contract + +`nix flake check --no-build` (and `nix build .#darwinConfigurations.mac.system --dry-run`) is the **primary** contract now for anything declared in `configuration.nix`/`home.nix` — if a package or symlink is declared wrong, the flake fails to evaluate or build before you ever get to `verify.sh`. `verify.sh` itself now runs `nix flake check` as its first check, then covers what Nix genuinely can't express on its own: -`verify.sh` is the contract that the setup is complete and correct. Keep it in sync: +- Whether things are *actually* wired up on THIS machine right now (not just declared correctly) +- GUI app presence and `/Applications` state (Nix can't verify sign-in state) +- asdf runtime versions matching `.tool-versions` (asdf is deliberately outside Nix's management) +- Git identity (deliberately outside home-manager's `programs.git`) +- LinearMouse JSON content integrity (a write-back file, worth sanity-checking regardless of installer mechanism) +Keep `verify.sh` in sync: - New CLI binary installed → add to the `check_command` loop - New GUI app installed → add to the `APPS` array -- New symlink added in `setup.sh` → add a `check_symlink` call +- New symlink added in `home.nix` → add a `check_resolves_to_repo` call (for edit-in-place files) or `check_nonempty` (for home-manager-generated files like `.zshrc`/`starship.toml`) - New config file added → add `check_contains` or `check_json` assertions for its critical settings -- New Zsh plugin added → add a `check_contains` line for `.zsh_plugins.txt` -- New `.zshrc` initialization added → add a `check_contains` line for `.zshrc` --- @@ -139,4 +155,4 @@ When editing README, ask: "does this content already exist in a cheatsheet or in 1. **Say so explicitly.** 2. **Search the web** using the WebSearch tool. Prefer official docs and release notes over blog posts. -3. **Update the repo** if the answer is stable and reusable — place it in the most relevant `docs/*-cheatsheet.md`. Match existing tone: skimmable, commands first, gotchas where they matter. Do this proactively in Agent sessions. +3. **Update the repo** if the answer is stable and reusable — place it in the most relevant `docs/cheatsheets/*-cheatsheet.md`. Match existing tone: skimmable, commands first, gotchas where they matter. Do this proactively in Agent sessions. diff --git a/README.md b/README.md index 4a24df7..8b38ddc 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # macOS Developer Setup (2026) -A reproducible macOS development environment. Clone this repo onto a fresh Mac, run one script, and you get: +A reproducible macOS development environment, managed with **nix-darwin** and **home-manager**. Clone this repo onto a fresh Mac, run one script, and you get: -- A modern terminal (Ghostty) with an actively-maintained cross-shell prompt (Starship) +- A modern terminal stack (Ghostty + WezTerm) with an actively-maintained cross-shell prompt (Starship) - Language runtimes pinned per-project via asdf - Rust-based CLI replacements for classic Unix tools, all aliased transparently -- Code editors, containers, cloud tools, and developer utilities -- Everything wired together via `.zshrc`, Starship, Antidote, and a bootstrap script +- Code editors, containers, cloud tools, and developer utilities — declared once, applied atomically +- Everything wired together via `flake.nix`, `configuration.nix`, `home.nix`, and a bootstrap script See [`docs/inventory.md`](docs/inventory.md) for the complete list of every installed tool, app, and runtime — including how each one is installed. @@ -20,7 +20,7 @@ See [`docs/inventory.md`](docs/inventory.md) for the complete list of every inst - [CLI Tools & Aliases](#cli-tools--aliases) - [Language Runtimes (via asdf)](#language-runtimes-via-asdf) - [Applications Installed](#applications-installed) -- [Manual Steps (After `./setup.sh`)](#manual-steps-after-setupsh) +- [Manual Steps (After `./bootstrap.sh`)](#manual-steps-after-bootstrapsh) - [1. Grant Accessibility Permissions](#1-grant-accessibility-permissions-one-time-per-app) - [2. Configure Git Identity](#2-configure-git-identity) - [3. Authenticate GitHub CLI](#3-authenticate-github-cli) @@ -36,16 +36,17 @@ See [`docs/inventory.md`](docs/inventory.md) for the complete list of every inst - [Updating Your Setup](#updating-your-setup) - [Update everything](#update-everything) - [Update just Homebrew packages](#update-just-homebrew-packages) - - [Update Claude Code](#update-claude-code) + - [Roll back a bad switch](#roll-back-a-bad-switch) - [Update asdf plugins](#update-asdf-plugins) - [Troubleshooting](#troubleshooting) - - [`./setup.sh` returns "permission denied"](#setupsh-returns-permission-denied) + - [`./bootstrap.sh` returns "permission denied"](#bootstrapsh-returns-permission-denied) + - [`sudo: darwin-rebuild: command not found`](#sudo-darwin-rebuild-command-not-found) - ["command not found" on a tool that should exist](#command-not-found-on-a-tool-that-should-exist) - [Icons show as squares in Starship / eza](#icons-show-as-squares-in-starship--eza) - [Ghostty Quick Terminal doesn't respond](#ghostty-quick-terminal-doesnt-respond) - [SSH session looks broken](#ssh-session-looks-broken-vim--less-render-incorrectly) - [asdf says "No version is set"](#asdf-says-no-version-is-set-for-command-x) - - [`brew bundle` fails with permissions errors](#brew-bundle-fails-with-permissions-errors) + - [`darwin-rebuild switch` fails on a Homebrew step](#darwin-rebuild-switch-fails-on-a-homebrew-step) - [Atuin doesn't import my old history](#atuin-doesnt-import-my-old-history) - [Starting over on a single tool](#starting-over-on-a-single-tool) - [Cheat Sheets & References](#cheat-sheets--references) @@ -66,19 +67,21 @@ xcode-select --install # 2. Clone and bootstrap git clone https://github.com/lakshyads/dotfiles.git ~/dotfiles cd ~/dotfiles -chmod +x setup.sh # first time only; see note below -./setup.sh +chmod +x bootstrap.sh # first time only; see note below +./bootstrap.sh ``` -`setup.sh` launches an interactive wizard — it walks you through each category (core tools, CLI tools, editors, apps, fonts, etc.) and asks whether to install all, customize, or skip. Pass `--full` to skip all prompts and install everything non-interactively: +`bootstrap.sh` is the single entry point. In order, it: installs Xcode CLT if missing, installs Determinate Nix if missing, symlinks the repo to `~/.dotfiles`, runs the first `darwin-rebuild switch` (this installs every package — Homebrew via nix-homebrew, CLI tools via home-manager — and wires up every dotfile symlink), then registers asdf plugins and installs runtimes from `.tool-versions`, and finally prompts for your git identity. + +It's designed to be re-run: some steps (Xcode CLT install, first Nix install) deliberately exit and ask you to re-run once they finish, since they need a fresh shell or a GUI installer to complete first. Pass `--full` to skip the interactive git-identity prompt (just reports current state instead): ```bash -./setup.sh --full +./bootstrap.sh --full ``` -The script is idempotent and safe to re-run at any time. Already-installed packages are detected and skipped. +Every step is idempotent — safe to re-run at any time. Already-applied steps are detected and skipped. -When it finishes, open Ghostty, run `exec zsh`, and you're in the new environment. +When it finishes, open Ghostty or WezTerm, run `exec zsh`, and you're in the new environment. To verify everything installed correctly in a new shell: @@ -86,9 +89,16 @@ To verify everything installed correctly in a new shell: ./verify.sh ``` -This runs non-destructive smoke tests: checks every CLI tool resolves, every symlink is in place, every GUI app installed, language runtimes match `.tool-versions`, fonts are detected, and Git is configured. Exit 0 on success, 1 with a failure summary otherwise. +This runs non-destructive smoke tests: checks every CLI tool resolves, every dotfile symlink is wired correctly, every GUI app is installed, language runtimes match `.tool-versions`, fonts are detected, and Git is configured. It also runs `nix flake check --no-build` as its first check — that's the primary structural contract now (anything declared in `configuration.nix`/`home.nix` that fails to evaluate fails there first). Exit 0 on success, 1 with a failure summary otherwise. + +After the first bootstrap, day-to-day changes to configuration go through: + +```bash +./rebuild.sh # build + apply configuration.nix / home.nix changes +./rebuild.sh --dry-run # build only, validate before switching +``` -> **Why `chmod +x`?** Depending on how you cloned or downloaded the repo, the executable bit on `setup.sh` may not be preserved (macOS Gatekeeper strips it for quarantined files, and some git configs do too). Running `chmod +x setup.sh` once fixes it permanently. If you cloned via plain `git clone` into a trusted directory, it may already be executable and this step is a no-op. +> **Why `chmod +x`?** Depending on how you cloned or downloaded the repo, the executable bit on `bootstrap.sh` may not be preserved (macOS Gatekeeper strips it for quarantined files, and some git configs do too). Running `chmod +x bootstrap.sh` once fixes it permanently. If you cloned via plain `git clone` into a trusted directory, it may already be executable and this step is a no-op.

↑ Back to top

@@ -96,23 +106,24 @@ This runs non-destructive smoke tests: checks every CLI tool resolves, every sym ## What the Setup Gives You -See [`docs/inventory.md`](docs/inventory.md) for a full list of what's provisioned automatically. Everything below is set up for you—no manual configuration required after running `./setup.sh`. +See [`docs/inventory.md`](docs/inventory.md) for a full list of what's provisioned automatically. Everything below is set up for you — no manual configuration required after running `./bootstrap.sh`. ### Terminal & Shell -- **Ghostty** with JetBrains Mono Nerd Font, Catppuccin theme (auto light/dark switching), 25M-line scrollback, and split keybindings +- **Ghostty** and **WezTerm**, both themed `rose-pine-moon` with Hack Nerd Font, 25M-line scrollback, and split keybindings - **Quick Terminal** (Quake-style dropdown) bound to ```Ctrl+` ```; see manual steps for required permission -- **Zsh** with Antidote — see [Zsh plugins](docs/inventory.md#zsh-plugins) +- **Zsh** with native home-manager plugin support (autosuggestions, syntax-highlighting) — see [`home.nix`](home.nix)'s `programs.zsh` block - **Starship** prompt showing directory, git branch + status, active language version, and command duration - **Atuin** replacing `Ctrl+R` with a full-screen SQLite-backed history search +- **herdr**, a terminal-based agent multiplexer (tmux-style `Ctrl+B` prefix), for running multiple coding agent sessions side by side ### CLI Tools & Aliases -Classic commands (`ls`, `cat`, `top`, `du`, `git`) are aliased to their modern replacements. Shell key bindings for history search and fuzzy file/directory picking are wired up. See [`docs/modern-cli-cheatsheet.md`](docs/modern-cli-cheatsheet.md) for the full alias map, key bindings, and usage reference. +Classic commands (`ls`, `cat`, `top`, `du`, `git`) are aliased to their modern replacements. Shell key bindings for history search and fuzzy file/directory picking are wired up. See [`docs/cheatsheets/modern-cli-cheatsheet.md`](docs/cheatsheets/modern-cli-cheatsheet.md) for the full alias map, key bindings, and usage reference. ### Language Runtimes (via asdf) -Versions are pinned in `.tool-versions` — that is the only place versions are defined. See [`docs/inventory.md`](docs/inventory.md#language-runtimes) for the list of managed languages and [`docs/asdf-cheatsheet.md`](docs/asdf-cheatsheet.md) for version management commands. +Versions are pinned in `.tool-versions` — that is the only place versions are defined. Runtimes stay asdf-managed by design (not Nix-managed): asdf's per-project `.tool-versions` override, walking up the directory tree, is a workflow Nix doesn't replicate without a heavier devshell/direnv setup. See [`docs/inventory.md`](docs/inventory.md#language-runtimes) for the list of managed languages and [`docs/cheatsheets/asdf-cheatsheet.md`](docs/cheatsheets/asdf-cheatsheet.md) for version management commands. ### Applications Installed @@ -122,7 +133,7 @@ See **[`docs/inventory.md`](docs/inventory.md)** for the full list of GUI apps, --- -## Manual Steps (After `./setup.sh`) +## Manual Steps (After `./bootstrap.sh`) These genuinely require human action, either because they need you to sign in, grant macOS permissions, or make personal choices. Work through them in order: @@ -139,6 +150,8 @@ Some apps need Accessibility permission to function. macOS will prompt on first ### 2. Configure Git Identity +`./bootstrap.sh` already prompted you for this interactively. To change it later: + ```bash git config --global user.name "Your Name" git config --global user.email "you@example.com" @@ -149,7 +162,7 @@ git config --global pull.rebase true git config --global rebase.autoStash true ``` -Full recommended git config and a workflow reference are in [`docs/git-cheatsheet.md`](docs/git-cheatsheet.md). +This is deliberately kept out of home-manager's `programs.git` — that would make `~/.gitconfig` an immutable Nix-store symlink. Full recommended git config and a workflow reference are in [`docs/cheatsheets/git-cheatsheet.md`](docs/cheatsheets/git-cheatsheet.md). ### 3. Authenticate GitHub CLI @@ -161,7 +174,7 @@ gh auth login # follow prompts, choose SSH or HTTPS First launch triggers a macOS prompt to install a privileged helper. Click through it. After that, `docker` and `docker compose` work from any terminal. -Docker Desktop also appends a CLI completions block to `~/.zshrc` on first launch. This is already committed to the dotfiles `.zshrc`; don't let it get added a second time if you re-run setup on a machine where Docker Desktop has already launched. +Docker Desktop also appends a CLI completions block to `~/.zshrc` on first launch — but `~/.zshrc` is now home-manager generated (not a plain file), so that append doesn't persist across a `./rebuild.sh`. The Docker completions fpath addition is already handled declaratively in `home.nix`'s `programs.zsh.initContent`, so this is a no-op in practice. ### 5. Sign Into GUI Apps @@ -188,7 +201,7 @@ claude # first run opens browser for OAuth login claude doctor # verifies installation + auth ``` -Requires a paid Anthropic account (Pro, Max, Team, Enterprise, or Console with API credits). +Requires a paid Anthropic account (Pro, Max, Team, Enterprise, or Console with API credits). Claude Code is installed via the `claude-code` Homebrew cask (declared in `configuration.nix`), not a native installer — Homebrew's `onActivation.autoUpdate` keeps it current on every `./rebuild.sh`. ### 8. Optional: Enable Atuin History Sync @@ -211,12 +224,12 @@ System Settings > Desktop & Dock > Scroll to "Default web browser / Default term The defaults are chosen carefully, but if you want to customize: -- **Ghostty theme:** edit `configs/ghostty-config`, change the `theme = ...` line. Preview options with `ghostty +list-themes`. -- **Font:** the Brewfile installs both `font-jetbrains-mono-nerd-font` (default) and `font-fira-code-nerd-font`. Change `font-family` in `configs/ghostty-config`. -- **Starship prompt:** edit `configs/starship.toml`. See [starship.rs/presets](https://starship.rs/presets) for ready-made layouts. -- **Shell aliases:** edit `.zshrc` and run `reload`. +- **Ghostty theme:** edit `home/.config/ghostty/config`, change the `theme = ...` line. Preview options with `ghostty +list-themes`. +- **Font:** Hack Nerd Font is the primary system font (Ghostty + WezTerm). JetBrains Mono and Fira Code are also installed (via `home.nix`'s `home.packages`) as alternatives — to switch, change `font-family` in `home/.config/ghostty/config` and `config.font`/`config.window_frame.font` in `home/.config/wezterm/wezterm.lua` to `JetBrainsMono Nerd Font` or `FiraCode Nerd Font`, then run `./rebuild.sh`. +- **Starship prompt:** edit `home.nix`'s `programs.starship.settings`, then run `./rebuild.sh`. +- **Shell aliases:** edit `home.nix`'s `programs.zsh.shellAliases`, then run `./rebuild.sh`. -All configs live in this repo and are symlinked, so changes are preserved in git. +`home/.config/{wezterm,ghostty,nvim,herdr}/` files are edit-in-place — home-manager symlinks them directly into place (`mkOutOfStoreSymlink`), so editing them takes effect immediately, no rebuild needed. Everything else (packages, shell aliases, Starship settings) requires `./rebuild.sh` to apply.

↑ Back to top

@@ -226,12 +239,13 @@ All configs live in this repo and are symlinked, so changes are preserved in git | What you need | Where to look | |---|---| -| Terminal keybindings (tabs, splits, Quick Terminal) | [`docs/ghostty-cheatsheet.md`](docs/ghostty-cheatsheet.md) | -| Shell history search, fuzzy file/dir picker, autosuggestions | [`docs/modern-cli-cheatsheet.md`](docs/modern-cli-cheatsheet.md) | -| Directory jumping (`z`) | [`docs/modern-cli-cheatsheet.md`](docs/modern-cli-cheatsheet.md#jumping-to-directories-zoxide) | -| `rg`, `fd`, `bat`, `eza`, `dust`, `btop` usage | [`docs/modern-cli-cheatsheet.md`](docs/modern-cli-cheatsheet.md) | -| Git TUI (`lg`) | [`docs/lazygit-cheatsheet.md`](docs/lazygit-cheatsheet.md) | -| Language runtime commands (`asdf current`, `asdf install`) | [`docs/asdf-cheatsheet.md`](docs/asdf-cheatsheet.md) | +| Terminal keybindings (tabs, splits, Quick Terminal) | [`docs/cheatsheets/ghostty-cheatsheet.md`](docs/cheatsheets/ghostty-cheatsheet.md) | +| Shell history search, fuzzy file/dir picker, autosuggestions | [`docs/cheatsheets/modern-cli-cheatsheet.md`](docs/cheatsheets/modern-cli-cheatsheet.md) | +| Directory jumping (`z`) | [`docs/cheatsheets/modern-cli-cheatsheet.md`](docs/cheatsheets/modern-cli-cheatsheet.md#jumping-to-directories-zoxide) | +| `rg`, `fd`, `bat`, `eza`, `dust`, `btop` usage | [`docs/cheatsheets/modern-cli-cheatsheet.md`](docs/cheatsheets/modern-cli-cheatsheet.md) | +| Git TUI (`lg`) | [`docs/cheatsheets/lazygit-cheatsheet.md`](docs/cheatsheets/lazygit-cheatsheet.md) | +| Language runtime commands (`asdf current`, `asdf install`) | [`docs/cheatsheets/asdf-cheatsheet.md`](docs/cheatsheets/asdf-cheatsheet.md) | +| Nix / nix-darwin / home-manager commands (`darwin-rebuild`, rollback, GC) | [`docs/cheatsheets/nix-cheatsheet.md`](docs/cheatsheets/nix-cheatsheet.md) | > **On Mac, `Alt` = `Option` (⌥).** fzf and readline docs use "Alt" historically. @@ -245,17 +259,18 @@ This repo is meant to be forked and personalized. The files worth editing: | File | What it controls | |---|---| -| `Brewfile` | What gets installed via Homebrew. Add/remove lines, run `brew bundle`. | -| `.tool-versions` | Language runtime versions. Edit and run `asdf install`. | -| `.zshrc` | Aliases, env vars, tool integration. Run `reload` after editing. | -| `.zsh_plugins.txt` | Zsh plugins loaded by Antidote. | -| `configs/starship.toml` | Prompt appearance. | -| `configs/ghostty-config` | Terminal appearance and keybindings. Reload with `Cmd+Shift+,`. | -| `configs/wezterm.lua` | WezTerm terminal appearance. | -| `configs/linearmouse.json` | Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. | -| `setup.sh` | Bootstrap steps. Only touch if you add new tools needing custom setup. | - -After any changes, commit them to your dotfiles repo. Other machines pick up changes with `git pull && ./setup.sh`. +| `configuration.nix` | System defaults, and Homebrew packages: GUI apps (casks), asdf + its build deps, and the two tapped tools (`terraform`, `stripe`) not worth moving to Nix. Run `./rebuild.sh` after editing. | +| `home.nix` | CLI tools available in nixpkgs (`home.packages`), zsh aliases/keybinds/plugins (`programs.zsh`), Starship prompt (`programs.starship`), and every dotfile symlink (`home.file`). Run `./rebuild.sh` after editing. | +| `.tool-versions` | Language runtime versions. Edit and run `./bootstrap.sh` (registers asdf plugins + runs `asdf install`). | +| `home/.config/wezterm/wezterm.lua` | WezTerm terminal appearance. Edit-in-place, no rebuild needed. | +| `home/.config/ghostty/config` | Ghostty terminal appearance and keybindings. Edit-in-place, no rebuild needed. Reload in-app with `Cmd+Shift+,`. | +| `home/.config/nvim/` | Neovim config (lazy.nvim plugin specs under `lua/plugins/`). Edit-in-place, no rebuild needed. | +| `home/.config/herdr/config.toml` | herdr (terminal agent multiplexer) keybindings. Edit-in-place, no rebuild needed. | +| `home/.config/linearmouse/linearmouse.json` | Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. | +| `home/AGENTS.md` | Shared agent instructions, symlinked into Claude Code, Codex, and opencode. | +| `configs/gitconfig` | Delta pager + git config, included via `~/.gitconfig`'s `include.path` (wired by `bootstrap.sh`, kept outside home-manager so personal identity stays mutable). | + +After any changes, commit them to your dotfiles repo. Other machines pick up changes with `git pull && ./rebuild.sh` (or `./bootstrap.sh` on a machine that hasn't been bootstrapped yet).

↑ Back to top

@@ -266,34 +281,45 @@ After any changes, commit them to your dotfiles repo. Other machines pick up cha ### Update everything ```bash -./update.sh +git pull +./rebuild.sh ``` -`update.sh` runs each updater in sequence — Homebrew (formulae + casks + cleanup), Zsh plugins (antidote), asdf plugins, and Claude Code. A failure in one section does not abort the rest. +`darwin-rebuild switch` (what `./rebuild.sh` runs) re-applies `configuration.nix` and `home.nix` in one atomic step: any new Homebrew packages, any new `home.packages` entries, any dotfile symlink changes. There's no separate "install new Brewfile entries" step anymore — `Brewfile` is a retired stub; `configuration.nix`'s `homebrew` block is what `darwin-rebuild switch` reads, and it applies unconditionally on every switch (`onActivation.autoUpdate = true`). -After pulling dotfiles changes, also run: +For runtime version changes: ```bash -git pull -brew bundle # install any new Brewfile entries -asdf install # install any new .tool-versions runtimes +./bootstrap.sh # re-registers asdf plugins and runs `asdf install` for anything new in .tool-versions ``` -> **Note:** `brew bundle` installs everything in `Brewfile` unconditionally — including anything you chose to skip in an earlier interactive `./setup.sh` run. See [Homebrew cheat sheet — Common Pitfalls](docs/homebrew-cheatsheet.md#common-pitfalls). +To bump the Nix flake's own pins (nixpkgs, nix-darwin, home-manager, nix-homebrew — this is how Nix-packaged CLI tool *versions* move forward, since `home.packages` doesn't pin exact versions itself) and update Homebrew/asdf all in one pass: + +```bash +./update.sh # nix flake update, then a reminder to ./rebuild.sh; also upgrades Homebrew packages and asdf plugins +``` ### Update just Homebrew packages +Homebrew's own package *versions* (not the declared list, which comes from `configuration.nix`) update via: + ```bash brew update && brew upgrade && brew upgrade --cask brew cleanup ``` -### Update Claude Code +This is independent of `./rebuild.sh` — it updates the installed version of whatever `configuration.nix` already declares, it doesn't change what's declared. + +### Roll back a bad switch + +One of the reasons for the Nix migration — a bad `./rebuild.sh` is a one-command undo: ```bash -claude update +sudo /run/current-system/sw/bin/darwin-rebuild --rollback ``` +See [`docs/cheatsheets/nix-cheatsheet.md`](docs/cheatsheets/nix-cheatsheet.md) for generation management and garbage collection. + ### Update asdf plugins ```bash @@ -306,40 +332,48 @@ asdf plugin update --all ## Troubleshooting -### `./setup.sh` returns "permission denied" +### `./bootstrap.sh` returns "permission denied" The executable bit wasn't preserved. One-time fix: ```bash -chmod +x setup.sh -./setup.sh +chmod +x bootstrap.sh +./bootstrap.sh ``` To make the fix permanent so other machines cloning the repo don't hit this, commit the mode change: ```bash -chmod +x setup.sh -git add setup.sh # `git status` should show "mode change 100644 → 100755" -git commit -m "chore: make setup.sh executable" +chmod +x bootstrap.sh +git add bootstrap.sh # `git status` should show "mode change 100644 → 100755" +git commit -m "chore: make bootstrap.sh executable" git push ``` +### `sudo: darwin-rebuild: command not found` + +`sudo` resets `PATH` and doesn't include where nix-darwin installs `darwin-rebuild`. Use the full path, or just use `./rebuild.sh` (already handles this): + +```bash +sudo /run/current-system/sw/bin/darwin-rebuild switch --flake .#mac +``` + ### "command not found" on a tool that should exist -1. Did you run `./setup.sh`? It symlinks `.zshrc`; without that, aliases and PATH aren't set. +1. Did you run `./bootstrap.sh` (or `./rebuild.sh` if already bootstrapped)? It's what wires up `~/.zshrc`, PATH, and every dotfile symlink. 2. Did you restart your shell after install? Run `exec zsh` or open a new tab. -3. Check the tool is actually installed: `brew list | grep `. +3. Check the tool is actually declared: `grep configuration.nix home.nix`. 4. For language tools (node, python, go): run `asdf current` to verify the active version is installed. ### Icons show as squares in Starship / eza -Your terminal isn't using a Nerd Font. Check `configs/ghostty-config`: +Your terminal isn't using a Nerd Font. Check `home/.config/ghostty/config`: ``` -font-family = JetBrainsMono Nerd Font +font-family = Hack Nerd Font ``` -The spaces matter. See [`docs/ghostty-cheatsheet.md`](docs/ghostty-cheatsheet.md#themes--fonts) for details. +The spaces matter. See [`docs/cheatsheets/ghostty-cheatsheet.md`](docs/cheatsheets/ghostty-cheatsheet.md#themes--fonts) for details. ### Ghostty Quick Terminal doesn't respond @@ -347,7 +381,7 @@ Accessibility permission not granted. Go to System Settings > Privacy & Security ### SSH session looks broken (vim / less render incorrectly) -The remote host doesn't have Ghostty's terminfo. Your `configs/ghostty-config` already sets `term = xterm-256color` to avoid this, but if you removed that line, re-add it. +The remote host doesn't have Ghostty's terminfo. Your `home/.config/ghostty/config` already sets `term = xterm-256color` to avoid this, but if you removed that line, re-add it. ### asdf says "No version is set for command X" @@ -357,9 +391,9 @@ The `.tool-versions` file references a version that isn't installed yet: asdf install ``` -See [`docs/asdf-cheatsheet.md`](docs/asdf-cheatsheet.md#troubleshooting) for deeper issues. +See [`docs/cheatsheets/asdf-cheatsheet.md`](docs/cheatsheets/asdf-cheatsheet.md#troubleshooting) for deeper issues. -### `brew bundle` fails with permissions errors +### `darwin-rebuild switch` fails on a Homebrew step Usually means Homebrew itself needs repair: @@ -368,11 +402,11 @@ sudo chown -R $(whoami) /opt/homebrew brew doctor ``` -See [`docs/homebrew-cheatsheet.md`](docs/homebrew-cheatsheet.md#common-pitfalls). +See [`docs/cheatsheets/homebrew-cheatsheet.md`](docs/cheatsheets/homebrew-cheatsheet.md#common-pitfalls). Note: `brew doctor` will report this Homebrew install as "managed by Nix" (Tier 3) — that's expected since nix-homebrew owns it; ignore warnings that only apply to a plain native Homebrew install. ### Atuin doesn't import my old history -See [Shell History (atuin)](docs/modern-cli-cheatsheet.md#shell-history-atuin) in the modern CLI cheat sheet. +See [Shell History (atuin)](docs/cheatsheets/modern-cli-cheatsheet.md#shell-history-atuin) in the modern CLI cheat sheet. ### Starting over on a single tool @@ -394,18 +428,19 @@ reload ## Cheat Sheets & References -Full command references for the tools that get the most daily use. These live in `docs/` so you can open them in-repo rather than fishing through web docs: +Full command references for the tools that get the most daily use. These live in `docs/cheatsheets/` so you can open them in-repo rather than fishing through web docs (this folder doubles as an Obsidian vault — see `home.nix`'s `Documents/workspace/my-matrix/a-utils/cheatsheets` symlink): -- **[Software inventory](docs/inventory.md)**: full list of GUI apps, CLI tools, and language runtimes — update this whenever `Brewfile` or `.tool-versions` changes -- **[Homebrew cheat sheet](docs/homebrew-cheatsheet.md)**: install, daily commands, Brewfile workflows, FAQ, common pitfalls -- **[asdf cheat sheet](docs/asdf-cheatsheet.md)**: plugin management, version commands, `.tool-versions` format, CI integration, troubleshooting -- **[Ghostty cheat sheet](docs/ghostty-cheatsheet.md)**: default keybindings, config syntax, action reference, SSH terminfo fixes, themes and fonts -- **[Git cheat sheet](docs/git-cheatsheet.md)**: daily workflow commands, branching, rebasing, undoing mistakes, stash, tags, `.gitignore` essentials, troubleshooting -- **[Lazygit cheat sheet](docs/lazygit-cheatsheet.md)**: panel navigation, default keybindings, line-staging, interactive rebase workflows, custom commands -- **[Modern CLI tools cheat sheet](docs/modern-cli-cheatsheet.md)**: ripgrep, fd, bat, eza, zoxide, fzf, atuin, delta, dust, btop, tldr. Usage per tool plus composition examples -- **[Docker cheat sheet](docs/docker-cheatsheet.md)**: images, containers, volumes, networks, Docker Compose, Dockerfile basics, disk cleanup, troubleshooting -- **[Claude Code cheat sheet](docs/claude-code-cheatsheet.md)**: CLI flags, slash commands, keyboard shortcuts, permission modes, CLAUDE.md, hooks, MCP, subagents, models and cost -- **[Cursor CLI cheat sheet](docs/cursor-cli-cheatsheet.md)**: agent modes (Agent/Plan/Ask), slash commands, cloud handoff, MCP integration, rules and skills, subagents +- **[Software inventory](docs/inventory.md)**: full list of GUI apps, CLI tools, and language runtimes — update this whenever `configuration.nix`, `home.nix`, or `.tool-versions` changes +- **[Nix cheat sheet](docs/cheatsheets/nix-cheatsheet.md)**: `darwin-rebuild` workflow, rollback, generations, garbage collection, the `homebrew.onActivation.cleanup` gotcha +- **[Homebrew cheat sheet](docs/cheatsheets/homebrew-cheatsheet.md)**: install, daily commands, nix-homebrew notes, FAQ, common pitfalls +- **[asdf cheat sheet](docs/cheatsheets/asdf-cheatsheet.md)**: plugin management, version commands, `.tool-versions` format, CI integration, troubleshooting +- **[Ghostty cheat sheet](docs/cheatsheets/ghostty-cheatsheet.md)**: default keybindings, config syntax, action reference, SSH terminfo fixes, themes and fonts +- **[Git cheat sheet](docs/cheatsheets/git-cheatsheet.md)**: daily workflow commands, branching, rebasing, undoing mistakes, stash, tags, `.gitignore` essentials, troubleshooting +- **[Lazygit cheat sheet](docs/cheatsheets/lazygit-cheatsheet.md)**: panel navigation, default keybindings, line-staging, interactive rebase workflows, custom commands +- **[Modern CLI tools cheat sheet](docs/cheatsheets/modern-cli-cheatsheet.md)**: ripgrep, fd, bat, eza, zoxide, fzf, atuin, delta, dust, btop, tldr. Usage per tool plus composition examples +- **[Docker cheat sheet](docs/cheatsheets/docker-cheatsheet.md)**: images, containers, volumes, networks, Docker Compose, Dockerfile basics, disk cleanup, troubleshooting +- **[Claude Code cheat sheet](docs/cheatsheets/claude-code-cheatsheet.md)**: CLI flags, slash commands, keyboard shortcuts, permission modes, CLAUDE.md, hooks, MCP, subagents, models and cost +- **[Cursor CLI cheat sheet](docs/cheatsheets/cursor-cli-cheatsheet.md)**: agent modes (Agent/Plan/Ask), slash commands, cloud handoff, MCP integration, rules and skills, subagents Each is written as a skimmable reference, not a tutorial. Use them when you need to look something up. @@ -418,29 +453,43 @@ Each is written as a skimmable reference, not a tutorial. Use them when you need ``` dotfiles/ ├── README.md # this file (orientation + daily reference) -├── setup.sh # one-command bootstrap (idempotent) +├── CLAUDE.md # governance rules for Claude Code working in this repo +├── bootstrap.sh # single entry point: fresh-machine bootstrap (idempotent) +├── rebuild.sh # daily use: re-apply configuration.nix/home.nix after editing ├── update.sh # update all package managers and tools -├── verify.sh # end-to-end smoke test (run after setup.sh) -├── Brewfile # Homebrew packages (formulae + casks) -├── .tool-versions # asdf runtime versions (Node, Python, Go) -├── .zshrc # shell config (aliases, tool integration) -├── .zsh_plugins.txt # Antidote plugin list +├── verify.sh # end-to-end smoke test (run after bootstrap.sh) +├── flake.nix # Nix flake entry point (inputs, darwinConfigurations) +├── flake.lock # pinned input versions +├── configuration.nix # system defaults + Homebrew (casks, asdf, tapped tools) +├── home.nix # home-manager: CLI packages, zsh/starship, dotfile symlinks +├── Brewfile # RETIRED — pointer stub, superseded by configuration.nix +├── .tool-versions # asdf runtime versions (Node, Python, Go, Java) ├── configs/ -│ ├── starship.toml # Starship prompt config -│ ├── ghostty-config # Ghostty terminal config -│ ├── wezterm.lua # WezTerm terminal config -│ ├── linearmouse.json # Mouse customization (side buttons, acceleration) -│ └── gitconfig # Delta pager + git config, included via ~/.gitconfig +│ └── gitconfig # Delta pager + git config, included via ~/.gitconfig +├── home/ # edit-in-place source for home-manager's mkOutOfStoreSymlink files +│ ├── AGENTS.md # shared agent instructions (Claude, Codex, opencode) +│ ├── .claude/ +│ │ └── settings.json # Claude Code settings (theme, statusline) +│ └── .config/ +│ ├── wezterm/wezterm.lua +│ ├── ghostty/config +│ ├── nvim/ # lazy.nvim config: init.lua, vim_config.lua, keys.lua, plugin.lua, plugins/ +│ ├── herdr/config.toml +│ └── linearmouse/linearmouse.json └── docs/ ├── inventory.md # full list of installed apps, tools, and runtimes - ├── homebrew-cheatsheet.md - ├── asdf-cheatsheet.md - ├── ghostty-cheatsheet.md - ├── git-cheatsheet.md - ├── lazygit-cheatsheet.md - ├── modern-cli-cheatsheet.md - ├── claude-code-cheatsheet.md - └── cursor-cli-cheatsheet.md + └── cheatsheets/ + ├── README.md # cheatsheet index (also an Obsidian vault folder) + ├── nix-cheatsheet.md + ├── homebrew-cheatsheet.md + ├── asdf-cheatsheet.md + ├── ghostty-cheatsheet.md + ├── git-cheatsheet.md + ├── lazygit-cheatsheet.md + ├── modern-cli-cheatsheet.md + ├── docker-cheatsheet.md + ├── claude-code-cheatsheet.md + └── cursor-cli-cheatsheet.md ```

↑ Back to top

@@ -451,12 +500,14 @@ dotfiles/ A few choices that drive the rest of the setup. Full context is in the cheat sheets, but the gist: -- **Ghostty over iTerm2**: GPU-accelerated native macOS rendering, ~3× faster than iTerm2, zero-config +- **nix-darwin + home-manager over plain Homebrew + shell scripts**: declarative, atomically-applied config with one-command rollback (`darwin-rebuild --rollback`) beats an imperative install script with no undo. See [`docs/cheatsheets/nix-cheatsheet.md`](docs/cheatsheets/nix-cheatsheet.md). +- **Homebrew kept, not replaced**: GUI `.app` bundles and a few tools not worth repackaging (asdf, terraform, stripe) stay Homebrew-managed via nix-homebrew, declared in `configuration.nix`. CLI tools available in nixpkgs moved to `home.nix`'s `home.packages`. +- **Ghostty and WezTerm, both kept**: rather than picking one, both stay maintained in parallel (`rose-pine-moon` theme, Hack Nerd Font, matching split keybindings) since both had real, independent customization worth preserving. +- **Native home-manager zsh plugins over Antidote**: `programs.zsh.autosuggestion`/`syntaxHighlighting` replace the Antidote plugin manager entirely — fewer moving parts, same behavior. - **Starship over Powerlevel10k**: P10k is on life support; Starship is actively maintained and cross-shell -- **Antidote over Oh My Zsh**: faster startup, picks exactly what you need (OMZ is also defensible if you want its catalog) -- **asdf over pyenv/nvm/rbenv**: one tool replaces all of them; single `.tool-versions` file per project +- **asdf over pyenv/nvm/rbenv, and over Nix for runtimes**: one tool replaces all of them; single `.tool-versions` file per project. Kept outside Nix's management deliberately — asdf's per-project override (walking up the directory tree) is a workflow Nix doesn't replicate without a heavier devshell/direnv setup. - **Zsh kept as login shell**: POSIX-compatible (unlike Fish) and already the macOS default -- **Claude Code via native installer, not Homebrew**: the native installer auto-updates; the brew cask does not -- **Docker for all local databases**: Brewfile intentionally omits `postgresql` / `redis` so they don't conflict with container-based local environments +- **Claude Code via Homebrew cask, not the native installer**: `configuration.nix`'s `homebrew.onActivation.autoUpdate` keeps it current on every switch, consistent with how every other package in this repo updates. +- **Docker for all local databases**: `configuration.nix` intentionally omits `postgresql` / `redis` so they don't conflict with container-based local environments

↑ Back to top

diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..e5d824a --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# bootstrap.sh — Single entry point for a fresh Mac. +# +# Usage: +# ./bootstrap.sh # interactive (prompts for git identity) +# ./bootstrap.sh --full # non-interactive (reports git identity, doesn't prompt) +# +# What it does, in order: +# 1. Installs Xcode Command Line Tools, if missing. +# 2. Installs Determinate Nix, if it isn't already installed. +# 3. Symlinks this repo to ~/.dotfiles (home.nix points at config files +# through that path). +# 4. Checks the `user` variable in flake.nix against your actual macOS +# username, and offers to fix it for you if they differ. +# 5. Runs the first `darwin-rebuild switch` — this installs every package +# (Homebrew via nix-homebrew, CLI tools via home.nix) and wires up all +# dotfile symlinks. Everything past this point is stuff Nix genuinely +# can't express: +# 6. Registers asdf plugins and installs runtimes from .tool-versions +# (per-project .tool-versions overrides don't fit Nix's model, so +# runtimes stay asdf-managed by design). +# 7. Prompts for git identity (kept out of home-manager's programs.git so +# ~/.gitconfig stays mutable). +# +# After this, `darwin-rebuild` exists on PATH and you're on the normal +# workflow: edit files, then run ./rebuild.sh. +# +# Idempotent: safe to re-run at any time (steps that are already done are skipped). + +set -euo pipefail + +DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HOST_LABEL="mac" +FULL=false +[[ "${1:-}" == "--full" ]] && FULL=true + +if [[ -t 1 ]]; then + G=$'\033[0;32m' Y=$'\033[0;33m' B=$'\033[0;34m' C=$'\033[0;36m' W=$'\033[1m' R=$'\033[0m' +else + G='' Y='' B='' C='' W='' R='' +fi +info() { printf "${B} → ${R}%s\n" "$*"; } +done_() { printf "${G} ✓ ${R}%s\n" "$*"; } +warn() { printf "${Y} ! ${R}%s\n" "$*" >&2; } +section() { printf "\n${W}${C}━━ %s${R}\n" "$*"; } + +# ── 1. Xcode Command Line Tools ────────────────────────────────────────────── +section "Xcode Command Line Tools" +if ! xcode-select -p >/dev/null 2>&1; then + info "Installing Xcode Command Line Tools (GUI prompt) …" + xcode-select --install + warn "Re-run this script once CLT installation finishes." + exit 0 +fi +done_ "Xcode Command Line Tools" + +# ── 2. Determinate Nix ─────────────────────────────────────────────────────── +section "Nix" +if ! command -v nix &>/dev/null; then + info "Installing Determinate Nix..." + curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm + done_ "Determinate Nix installed" + warn "Open a new terminal (or re-run this script) so the Nix environment is loaded." + exit 0 +else + done_ "Nix already installed" +fi + +# ── 3. Symlink repo to ~/.dotfiles ────────────────────────────────────────── +if [[ ! -e "$HOME/.dotfiles" ]]; then + ln -s "$DOTFILES_DIR" "$HOME/.dotfiles" + done_ "Symlinked $DOTFILES_DIR -> ~/.dotfiles" +elif [[ "$(readlink "$HOME/.dotfiles" 2>/dev/null)" != "$DOTFILES_DIR" ]]; then + warn "~/.dotfiles exists and points somewhere else — leaving it alone." +else + done_ "~/.dotfiles already linked" +fi + +# ── 4. Username check ──────────────────────────────────────────────────────── +ACTUAL_USER="$(whoami)" +DECLARED_USER="$(grep -o 'user = "[^"]*"' "$DOTFILES_DIR/flake.nix" | head -1 | sed 's/user = "\(.*\)"/\1/')" +if [[ "$ACTUAL_USER" != "$DECLARED_USER" ]]; then + warn "flake.nix declares user \"$DECLARED_USER\" but this Mac's user is \"$ACTUAL_USER\"." + read -rp " Update flake.nix to use \"$ACTUAL_USER\"? [y/N] " REPLY + if [[ "$REPLY" =~ ^[Yy]$ ]]; then + sed -i '' "s/user = \"$DECLARED_USER\"/user = \"$ACTUAL_USER\"/" "$DOTFILES_DIR/flake.nix" + done_ "Updated flake.nix" + else + warn "Leaving flake.nix as-is — the switch below may fail." + fi +else + done_ "flake.nix user matches this Mac ($ACTUAL_USER)" +fi + +# ── 5. First switch ────────────────────────────────────────────────────────── +section "darwin-rebuild switch" +info "Running first darwin-rebuild switch (this will prompt for your password)..." +sudo nix run nix-darwin/nix-darwin-26.05#darwin-rebuild -- switch --flake "$DOTFILES_DIR#${HOST_LABEL}" +done_ "nix-darwin bootstrapped — packages installed, dotfiles symlinked." + +# ── 6. Language Runtimes (asdf) ─────────────────────────────────────────────── +# asdf itself just got installed via configuration.nix's homebrew.brews. +# Plugins and the actual runtime installs from .tool-versions aren't Nix's job. +section "Language Runtimes (asdf)" +if command -v asdf >/dev/null 2>&1; then + while read -r lang _version; do + [[ -z "$lang" ]] && continue + asdf plugin add "$lang" 2>/dev/null || true + done_ "asdf plugin: $lang" + done < "$DOTFILES_DIR/.tool-versions" + info "Installing versions from .tool-versions (may take a few minutes) …" + asdf install + done_ "Language runtimes installed" +else + warn "asdf not found on PATH yet — open a new terminal and re-run this script." +fi + +# ── 7. Git Configuration ───────────────────────────────────────────────────── +# Deliberately not managed by home-manager's programs.git: that would make +# ~/.gitconfig an immutable Nix-store symlink, and personal identity below +# needs to stay easily settable per-machine. +section "Git Configuration" + +_branch=main +if ! $FULL; then + _cur_branch=$(git config --global init.defaultBranch 2>/dev/null || echo "main") + printf " Default branch [%s]: " "$_cur_branch" + read -r _branch /dev/null || echo "") +_cur_email=$(git config --global user.email 2>/dev/null || echo "") + +if ! $FULL; then + printf " Name" + [[ -n "$_cur_name" ]] && printf " [%s]" "$_cur_name" + printf ": " + read -r _name Privacy & Security > Accessibility" +echo +echo " 5. Launch Docker Desktop once to complete its install:" +echo " open -a Docker" +echo +echo " 6. Sign into GUI apps (Chrome, Cursor, VS Code)" +echo +echo " 7. Authenticate Claude Code:" +echo " claude" +echo +echo " 8. (Optional) Enable Atuin shell history sync:" +echo " atuin register -u -e " +echo +echo " 9. (Optional) Authenticate cloud CLIs:" +echo " gcloud auth login" +echo +echo " From now on, after editing config: ./rebuild.sh" +echo " See README.md 'Manual Steps' section for full details." diff --git a/claude/settings.json b/claude/settings.json deleted file mode 100644 index 4fed33f..0000000 --- a/claude/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "statusLine": { - "type": "command", - "command": "bash /Users/lakshyadevsingh/.claude/statusline-command.sh" - }, - "effortLevel": "high", - "agentPushNotifEnabled": true, - "skipAutoPermissionPrompt": true -} diff --git a/claude/statusline-command.sh b/claude/statusline-command.sh deleted file mode 100644 index b15eafe..0000000 --- a/claude/statusline-command.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -# ~/.claude/statusline-command.sh -# Mirrors the Starship prompt style: dir · git branch/status · model · context % - -input=$(cat) - -# --- Directory (truncate to 3 levels, similar to Starship truncation_length=3) --- -cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd') -home_cwd="${cwd/#$HOME/~}" -# Keep last 3 path components -dir=$(echo "$home_cwd" | awk -F'/' '{ if (NF > 3) { print "…/" $(NF-2) "/" $(NF-1) "/" $NF } else { print $0 } }') - -# --- Git branch + dirty flag (skip optional locks to avoid races) --- -branch="" -dirty="" -if git_branch=$(GIT_OPTIONAL_LOCKS=0 git -C "$cwd" symbolic-ref --short HEAD 2>/dev/null); then - branch="$git_branch" - if [ -n "$(GIT_OPTIONAL_LOCKS=0 git -C "$cwd" status --porcelain 2>/dev/null)" ]; then - dirty="*" - fi -elif git_tag=$(GIT_OPTIONAL_LOCKS=0 git -C "$cwd" describe --tags --exact-match HEAD 2>/dev/null); then - branch="$git_tag" -fi - -# --- Claude session data --- -model=$(echo "$input" | jq -r '.model.display_name // empty') -remaining=$(echo "$input" | jq -r '.context_window.remaining_percentage // empty') - -# --- Assemble output with ANSI colors (dimmed-friendly) --- -# Cyan for dir, purple for git, green for model, yellow for context -printf "\033[36m%s\033[0m" "$dir" - -if [ -n "$branch" ]; then - printf " \033[35m %s%s\033[0m" "$branch" "$dirty" -fi - -if [ -n "$model" ]; then - printf " \033[32m%s\033[0m" "$model" -fi - -if [ -n "$remaining" ]; then - printf " \033[33mctx:%s%%\033[0m" "$(printf '%.0f' "$remaining")" -fi diff --git a/codex-instructions.md b/codex-instructions.md deleted file mode 100644 index b9eb467..0000000 --- a/codex-instructions.md +++ /dev/null @@ -1,41 +0,0 @@ -# Git Conventions - -## Commit Messages - -Write commit subjects as Conventional Commits with a scope. If the current -branch name contains a Jira ticket ID, prefix the subject with that ID: - -``` -(): -``` - -``` - (): -``` - -- **type**: one of `feat`, `fix`, `refactor`, `chore`, `docs`, `deploy`, `test`, `perf`, `style`, `build`, `ci`. -- **scope**: area touched, lowercase (e.g. `billing`, `auth`). Combine related scopes with `+` (e.g. `auth+security`, `vouchers+billing`). -- **summary**: imperative mood, lowercase start, no trailing period. -- **JIRA-ID**: only include when present in the current branch name (e.g. `ABC-123` from `feature/ABC-123-add-login`). Preserve uppercase. -- Add a blank line then a body for context (what/why), wrapped at ~72 chars. -- Keep each commit focused — split unrelated changes into separate commits. - -Examples: -- `refactor(billing): unify billing_category resolution` -- `ABC-123 refactor(billing): unify billing_category resolution` -- `feat(vouchers): admin list / get endpoints` -- `fix(vouchers+billing): voucher_grant rounding` -- `chore(infra+deploy): rotate voucher secret` - -## Pull Requests - -- **Title**: under 70 characters, concise, imperative mood. -- **Body**: - - ``` - ## Summary - <1-3 bullet points> - - ## Test plan - [Bulleted markdown checklist of TODOs for testing the PR] - ``` diff --git a/configs/gitconfig b/configs/gitconfig index b2b4ec8..b2d3ac3 100644 --- a/configs/gitconfig +++ b/configs/gitconfig @@ -2,7 +2,7 @@ # git config --global include.path ~/dotfiles/gitconfig # # Personal identity (user.name, user.email) is intentionally absent; -# setup.sh sets those directly via git config --global. +# bootstrap.sh sets those directly via git config --global. [core] pager = delta diff --git a/configs/starship.toml b/configs/starship.toml deleted file mode 100644 index d53c382..0000000 --- a/configs/starship.toml +++ /dev/null @@ -1,50 +0,0 @@ -# ~/.config/starship.toml -# Starship prompt config: minimal but informative. -# Shows directory, git state, language versions (when in a project), and command duration. - -format = """ -$directory\ -$git_branch\ -$git_status\ -$nodejs\ -$python\ -$golang\ -$cmd_duration\ -$line_break\ -$character""" - -[character] -success_symbol = "[❯](bold green)" -error_symbol = "[❯](bold red)" - -[directory] -truncation_length = 3 -truncate_to_repo = true -style = "bold cyan" - -[git_branch] -symbol = " " -style = "bold purple" - -[git_status] -style = "bold red" - -[cmd_duration] -min_time = 2_000 -format = "[$duration]($style) " -style = "yellow" - -[python] -symbol = " " -format = '[$symbol$pyenv_prefix($version )(\($virtualenv\) )]($style)' -style = "yellow" - -[nodejs] -symbol = " " -format = '[$symbol($version )]($style)' -style = "green" - -[golang] -symbol = " " -format = '[$symbol($version )]($style)' -style = "cyan" diff --git a/configs/wezterm.lua b/configs/wezterm.lua deleted file mode 100644 index 61bd2cc..0000000 --- a/configs/wezterm.lua +++ /dev/null @@ -1,49 +0,0 @@ -local wezterm = require("wezterm") -local act = wezterm.action - -local config = wezterm.config_builder() - -local is_windows = os.getenv("OS") and os.getenv("OS"):lower():find("windows") -local is_macos = wezterm.target_triple:lower():find("darwin") ~= nil - -config.color_scheme = "rose-pine-moon" -config.max_fps = 120 --- Matches configs/ghostty-config font choice for a consistent look across terminals. -config.font = wezterm.font("JetBrainsMono Nerd Font", { weight = "DemiBold" }) -config.font_size = 14.0 -config.window_decorations = "INTEGRATED_BUTTONS|RESIZE" -config.window_frame = { - font = wezterm.font("JetBrainsMono Nerd Font", { weight = "Bold" }), -} -config.inactive_pane_hsb = { - saturation = 0.0, - brightness = 0.5, -} - --- ---- Split navigation ---- --- Mirrors configs/ghostty-config's custom split keybinds for muscle-memory parity. -config.keys = { - { key = "d", mods = "CMD", action = act.SplitPane({ direction = "Right" }) }, - { key = "d", mods = "CMD|SHIFT", action = act.SplitPane({ direction = "Down" }) }, - { key = "LeftArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Left") }, - { key = "RightArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Right") }, - { key = "UpArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Up") }, - { key = "DownArrow", mods = "CMD|ALT", action = act.ActivatePaneDirection("Down") }, - { key = "f", mods = "CMD|SHIFT", action = act.TogglePaneZoomState }, -} --- Note: Ghostty's Cmd+Shift+E "equalize splits" has no WezTerm equivalent; --- WezTerm has no built-in quake-style quick terminal either (would need Hammerspoon). - -if is_windows then - config.win32_system_backdrop = "Acrylic" - config.window_background_opacity = 0.7 - config.window_frame.font_size = 10.0 -end - -if is_macos then - config.window_background_opacity = 0.8 - config.macos_window_background_blur = 50 - config.window_frame.font_size = 13.0 -end - -return config diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..6dc1003 --- /dev/null +++ b/configuration.nix @@ -0,0 +1,108 @@ +{ user, ... }: + +{ + # Determinate already manages the Nix daemon, so nix-darwin shouldn't. + nix.enable = false; + + nixpkgs.config.allowUnfree = true; + nixpkgs.hostPlatform = "aarch64-darwin"; # use x86_64-darwin for Intel CPU + + system.primaryUser = user; + users.users.${user} = { + home = "/Users/${user}"; + }; + system.stateVersion = 6; + + system.defaults = { + NSGlobalDomain = { + AppleInterfaceStyle = "Dark"; + KeyRepeat = 2; # fast key repeat + InitialKeyRepeat = 15; # short delay before repeat + _HIHideMenuBar = true; # auto-hide the menu bar + AppleShowAllExtensions = true; + }; + dock.autohide = true; + finder.FXPreferredViewStyle = "Nlsv"; # list view by default + finder.CreateDesktop = false; # clean desktop + trackpad.Clicking = true; # tap to click + }; + + + nix-homebrew = { + enable = true; + inherit user; + # This Mac already has a native Homebrew install. autoMigrate lets + # nix-homebrew adopt it: it deletes and recreates Homebrew's own + # management directory (the `brew` tool + git metadata), but leaves + # already-installed formulae/casks (Cellar/Caskroom) untouched. + autoMigrate = true; + }; + + homebrew = { + enable = true; + onActivation.autoUpdate = true; + # Start at "none", not "zap": undeclared casks/formulae are left alone + # rather than force-uninstalled. Only consider "zap" once `brew list` + # has been verified to match this list exactly after a few switches. + onActivation.cleanup = "none"; + + taps = [ + "hashicorp/tap" + "stripe/stripe-cli" + ]; + + # CLI tools that exist in nixpkgs (ripgrep, fd, bat, eza, zoxide, fzf, + # delta, lazygit, btop, dust, tldr, atuin, tmux, neovim, starship, jq, + # tree, wget, git, gh, opencode) are now installed via home.nix's + # home.packages / programs.* instead of here — see home.nix. antidote is + # dropped entirely (native home-manager zsh plugins replace it). Only + # things genuinely still Homebrew's job stay in this list: asdf itself + # (runtimes stay asdf-managed by design) and its build-support libs, plus + # the two tapped tools not worth moving. + brews = [ + "asdf" + "coreutils" + "openssl@3" + "readline" + "xz" + "hashicorp/tap/terraform" + "stripe/stripe-cli/stripe" + "herdr" # agent multiplexer for the terminal (tmux-style, config in home/.config/herdr) + ]; + + casks = [ + # Containers + "docker-desktop" + # Cloud tooling + "gcloud-cli" + # Terminal emulators & editors + "ghostty" + "wezterm" + "visual-studio-code" + "cursor" + # AI coding tools + "claude" + "claude-code" + "codex" + "codex-app" + # Browsers + "google-chrome" + "firefox" + "chatgpt-atlas" + # Productivity & utilities + "rectangle" + "appcleaner" + "maccy" + "linearmouse" + "opensuperwhisper" + "obsidian" + # API testing + "granola" + "postman" + "whimsical" + # Developer fonts + "font-jetbrains-mono-nerd-font" + "font-fira-code-nerd-font" + ]; + }; +} diff --git a/cursor-rules/git-commits.mdc b/cursor-rules/git-commits.mdc deleted file mode 100644 index 23cd0c6..0000000 --- a/cursor-rules/git-commits.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Git commit message and pull request conventions -alwaysApply: true ---- - -# Git Conventions - -## Commit Messages - -Write commit subjects as Conventional Commits with a scope. If the current -branch name contains a Jira ticket ID, prefix the subject with that ID: - -``` -(): -``` - -``` - (): -``` - -- **type**: one of `feat`, `fix`, `refactor`, `chore`, `docs`, `deploy`, `test`, `perf`, `style`, `build`, `ci`. -- **scope**: area touched, lowercase (e.g. `billing`, `auth`). Combine related scopes with `+` (e.g. `auth+security`, `vouchers+billing`). -- **summary**: imperative mood, lowercase start, no trailing period. -- **JIRA-ID**: only include when present in the current branch name (e.g. `ABC-123` from `feature/ABC-123-add-login`). Preserve uppercase. -- Add a blank line then a body for context (what/why), wrapped at ~72 chars. -- Keep each commit focused — split unrelated changes into separate commits. - -Examples: -- `refactor(billing): unify billing_category resolution` -- `ABC-123 refactor(billing): unify billing_category resolution` -- `feat(vouchers): admin list / get endpoints` -- `fix(vouchers+billing): voucher_grant rounding` -- `chore(infra+deploy): rotate voucher secret` - -## Pull Requests - -- **Title**: under 70 characters, concise, imperative mood. -- **Body**: - - ``` - ## Summary - <1-3 bullet points> - - ## Test plan - [Bulleted markdown checklist of TODOs for testing the PR] - ``` diff --git a/docs/cheatsheets/README.md b/docs/cheatsheets/README.md new file mode 100644 index 0000000..6304006 --- /dev/null +++ b/docs/cheatsheets/README.md @@ -0,0 +1,41 @@ +--- +tag: + - type/readme + - topic/cheatsheet +--- + +# Cheat Sheets — Index + +Every cheat sheet here has frontmatter `tag:`/`related:` for Obsidian's tag pane and graph view — this index is the entry point, not the only way in. Backlinks aren't maintained manually anywhere in this vault; Obsidian computes them automatically from any note-to-note link, so linking one direction here is enough. + +--- + +## Package Management + +- [[homebrew-cheatsheet]] — GUI apps (casks), the handful of CLI tools still installed via Homebrew, Brewfile mechanics +- [[nix-cheatsheet]] — nix-darwin/home-manager daily workflow, validation, rollback, the `cleanup = "none"` gotcha +- [[asdf-cheatsheet]] — language runtime versions via `.tool-versions` + +## Terminal & Shell + +- [[ghostty-cheatsheet]] — keybindings, config syntax, Quick Terminal, SSH terminfo fixes +- [[modern-cli-cheatsheet]] — ripgrep/fd/bat/eza/zoxide/fzf/atuin/delta/dust/btop, organized by task + +## Git & Version Control + +- [[git-cheatsheet]] — daily git workflow, branching, rebasing, undoing mistakes +- [[lazygit-cheatsheet]] — the `lg` TUI: panels, keybindings, line-staging + +## Editors + +- [[vim-cheat-sheet]] — modal editing 80/20: motions, operators, the `[count]+operator+motion` formula + +## AI Coding Agents + +- [[claude-code-cheatsheet]] — Claude Code CLI: flags, slash commands, hooks, MCP, subagents +- [[cursor-cli-cheatsheet]] — Cursor CLI: Agent/Plan/Ask modes, Cloud Handoff, MCP, subagents + +## Containers + +- [[docker-cheatsheet]] — Docker Desktop: images, containers, Compose, cleanup + diff --git a/docs/asdf-cheatsheet.md b/docs/cheatsheets/asdf-cheatsheet.md similarity index 98% rename from docs/asdf-cheatsheet.md rename to docs/cheatsheets/asdf-cheatsheet.md index 7f17830..f02a2c6 100644 --- a/docs/asdf-cheatsheet.md +++ b/docs/cheatsheets/asdf-cheatsheet.md @@ -1,3 +1,13 @@ +--- +tag: + - type/cheatsheet + - topic/package-management +related: + - "[[homebrew-cheatsheet]]" + - "[[nix-cheatsheet]]" + - "[[git-cheatsheet]]" +--- + # asdf Cheat Sheet & Best Practices A reference for the day-to-day and the "I set this up months ago and now I need to remember how" moments. Targets asdf `0.16+` (the Go rewrite); older `source asdf.sh` syntax is deprecated and not covered here. diff --git a/docs/claude-code-cheatsheet.md b/docs/cheatsheets/claude-code-cheatsheet.md similarity index 97% rename from docs/claude-code-cheatsheet.md rename to docs/cheatsheets/claude-code-cheatsheet.md index 969bba1..3d1fb82 100644 --- a/docs/claude-code-cheatsheet.md +++ b/docs/cheatsheets/claude-code-cheatsheet.md @@ -1,6 +1,15 @@ +--- +tag: + - type/cheatsheet + - topic/ai-agent +related: + - "[[cursor-cli-cheatsheet]]" + - "[[git-cheatsheet]]" +--- + # Claude Code Cheat Sheet -Reference for Claude Code, Anthropic's agentic CLI. Current as of April 2026, covering through v2.1.116 (the version installed by your `setup.sh`). +Reference for Claude Code, Anthropic's agentic CLI. Current as of April 2026, covering through v2.1.116 (the version installed via the `claude-code` Homebrew cask). Official docs: @@ -27,7 +36,7 @@ Official docs: ## Installation & Authentication -Claude Code was installed by `setup.sh` via the native installer. Basic management: +Claude Code is installed via the `claude-code` Homebrew cask, declared in `configuration.nix`'s `homebrew.casks` and kept up to date by Homebrew's `onActivation.autoUpdate` (not a native installer). Basic management: ```bash claude doctor # verify installation health diff --git a/docs/cursor-cli-cheatsheet.md b/docs/cheatsheets/cursor-cli-cheatsheet.md similarity index 99% rename from docs/cursor-cli-cheatsheet.md rename to docs/cheatsheets/cursor-cli-cheatsheet.md index 4640938..d6a27ac 100644 --- a/docs/cursor-cli-cheatsheet.md +++ b/docs/cheatsheets/cursor-cli-cheatsheet.md @@ -1,3 +1,11 @@ +--- +tag: + - type/cheatsheet + - topic/ai-agent +related: + - "[[claude-code-cheatsheet]]" +--- + # Cursor CLI Cheat Sheet Reference for Cursor CLI (the terminal companion to the Cursor editor). Current as of April 2026, covering Agent Modes, Cloud Handoff, MCP integration, and Subagents (Cursor 2.4+). diff --git a/docs/docker-cheatsheet.md b/docs/cheatsheets/docker-cheatsheet.md similarity index 99% rename from docs/docker-cheatsheet.md rename to docs/cheatsheets/docker-cheatsheet.md index 0b64e9f..ebc4d43 100644 --- a/docs/docker-cheatsheet.md +++ b/docs/cheatsheets/docker-cheatsheet.md @@ -1,3 +1,11 @@ +--- +tag: + - type/cheatsheet + - topic/containers +related: + - "[[homebrew-cheatsheet]]" +--- + # Docker Cheat Sheet A reference for Docker Desktop on macOS. Covers daily container/image commands, Compose workflows, and the troubleshooting you'll actually need. diff --git a/docs/ghostty-cheatsheet.md b/docs/cheatsheets/ghostty-cheatsheet.md similarity index 96% rename from docs/ghostty-cheatsheet.md rename to docs/cheatsheets/ghostty-cheatsheet.md index 2ff933b..fd76ae3 100644 --- a/docs/ghostty-cheatsheet.md +++ b/docs/cheatsheets/ghostty-cheatsheet.md @@ -1,3 +1,12 @@ +--- +tag: + - type/cheatsheet + - topic/terminal +related: + - "[[modern-cli-cheatsheet]]" + - "[[nix-cheatsheet]]" +--- + # Ghostty Cheat Sheet & Best Practices A reference for Ghostty on macOS (2026). Covers default keybindings, config syntax, common actions, and the gotchas that trip people up. @@ -49,7 +58,7 @@ These ship with Ghostty and don't require config. All use the `Cmd` modifier to | `Cmd+Shift+E` | Equalize splits | | `Cmd+Shift+F` | Zooms into one panel (press again to restore) | -Note: Split *creation* keybindings aren't default; they're in your `configs/ghostty-config`. Navigation between existing splits is fully default on macOS. (`Alt` in shortcuts above = the key labeled Option `⌥`.) +Note: Split *creation* keybindings aren't default; they're in your `home/.config/ghostty/config`. Navigation between existing splits is fully default on macOS. (`Alt` in shortcuts above = the key labeled Option `⌥`.) ### Clipboard @@ -391,7 +400,7 @@ Do this once per remote you care about. ### Commit your config to dotfiles -`~/.config/ghostty/config` is plain text. Version it. In this repo it's at `configs/ghostty-config` and symlinked into place by `setup.sh`. +`~/.config/ghostty/config` is plain text. Version it. In this repo it's at `home/.config/ghostty/config`, symlinked into place by `home.nix` (`mkOutOfStoreSymlink`) — edits there take effect immediately, no rebuild needed. ### Keep the config minimal diff --git a/docs/git-cheatsheet.md b/docs/cheatsheets/git-cheatsheet.md similarity index 99% rename from docs/git-cheatsheet.md rename to docs/cheatsheets/git-cheatsheet.md index 63ff393..7785a56 100644 --- a/docs/git-cheatsheet.md +++ b/docs/cheatsheets/git-cheatsheet.md @@ -1,3 +1,12 @@ +--- +tag: + - type/cheatsheet + - topic/git +related: + - "[[lazygit-cheatsheet]]" + - "[[asdf-cheatsheet]]" +--- + # Git Cheat Sheet & Best Practices A reference for git workflows that come up daily. Focused on what you actually use, organized by task, with the gotchas that cause real problems. diff --git a/docs/homebrew-cheatsheet.md b/docs/cheatsheets/homebrew-cheatsheet.md similarity index 98% rename from docs/homebrew-cheatsheet.md rename to docs/cheatsheets/homebrew-cheatsheet.md index 9005a6a..2144213 100644 --- a/docs/homebrew-cheatsheet.md +++ b/docs/cheatsheets/homebrew-cheatsheet.md @@ -1,3 +1,13 @@ +--- +tag: + - type/cheatsheet + - topic/package-management +related: + - "[[nix-cheatsheet]]" + - "[[asdf-cheatsheet]]" + - "[[modern-cli-cheatsheet]]" +--- + # Homebrew Cheat Sheet & Best Practices A reference for Homebrew 5.x on macOS (2026). Covers installation, daily commands, Brewfile workflows, and the troubleshooting you'll actually need. diff --git a/docs/lazygit-cheatsheet.md b/docs/cheatsheets/lazygit-cheatsheet.md similarity index 99% rename from docs/lazygit-cheatsheet.md rename to docs/cheatsheets/lazygit-cheatsheet.md index f980339..6b2c75f 100644 --- a/docs/lazygit-cheatsheet.md +++ b/docs/cheatsheets/lazygit-cheatsheet.md @@ -1,3 +1,11 @@ +--- +tag: + - type/cheatsheet + - topic/git +related: + - "[[git-cheatsheet]]" +--- + # Lazygit Cheat Sheet A reference for lazygit, the keyboard-driven TUI for git. Covers the panels, default keybindings, common workflows, and the features that take lazygit from "nicer git" to "faster than the CLI." diff --git a/docs/modern-cli-cheatsheet.md b/docs/cheatsheets/modern-cli-cheatsheet.md similarity index 94% rename from docs/modern-cli-cheatsheet.md rename to docs/cheatsheets/modern-cli-cheatsheet.md index e10450e..3c33826 100644 --- a/docs/modern-cli-cheatsheet.md +++ b/docs/cheatsheets/modern-cli-cheatsheet.md @@ -1,8 +1,18 @@ +--- +tag: + - type/cheatsheet + - topic/terminal +related: + - "[[homebrew-cheatsheet]]" + - "[[ghostty-cheatsheet]]" + - "[[nix-cheatsheet]]" +--- + # Modern CLI Tools Cheat Sheet A reference for the modern command-line tools in this setup: the Rust-based replacements for classic Unix tools plus a few new additions that don't have classic equivalents. Organized by task rather than by tool, so you can look up "how do I search files" without needing to remember which tool does it. -All tools below are installed via the `Brewfile`. See [`homebrew-cheatsheet.md`](homebrew-cheatsheet.md) if you need to install manually. +Most tools below are now installed via [[nix-cheatsheet|Nix]] (`home.nix`'s `home.packages`) rather than Homebrew directly — see [[homebrew-cheatsheet]] for the package-manager basics either way covers. --- @@ -315,9 +325,9 @@ Every `cd` (and zoxide-aware shell integration) registers a visit. The database ## Fuzzy Finding (fzf) -fzf is the general-purpose fuzzy finder. The Brewfile installs it, and `setup.sh` wires up the shell key bindings. +fzf is the general-purpose fuzzy finder. `home.nix`'s `programs.fzf` (with `enableZshIntegration = true`) installs it and wires up the shell key bindings automatically — no separate setup step needed. -### Default shell bindings (auto-configured by `setup.sh`) +### Default shell bindings (auto-configured by `home.nix`'s `programs.fzf`) | Keys | What it does | |---|---| @@ -452,7 +462,7 @@ Delta is a pager for git diffs. Once configured, every `git diff`, `git log -p`, ### Setup -Automated by `setup.sh` — no manual steps needed. The config lives in `dotfiles/configs/gitconfig` and is wired into `~/.gitconfig` via `[include]`, leaving your personal identity settings untouched. +Automated by `bootstrap.sh` — no manual steps needed. The config lives in `dotfiles/configs/gitconfig` and is wired into `~/.gitconfig` via `[include]`, leaving your personal identity settings untouched. This is deliberately kept outside home-manager's `programs.git` — enabling it would make `~/.gitconfig` an immutable Nix-store symlink, breaking `bootstrap.sh`'s git-identity prompt. To apply manually on an existing machine: diff --git a/docs/cheatsheets/nix-cheatsheet.md b/docs/cheatsheets/nix-cheatsheet.md new file mode 100644 index 0000000..75d81ec --- /dev/null +++ b/docs/cheatsheets/nix-cheatsheet.md @@ -0,0 +1,161 @@ +--- +tag: + - type/cheatsheet + - topic/package-management +related: + - "[[homebrew-cheatsheet]]" + - "[[asdf-cheatsheet]]" + - "[[modern-cli-cheatsheet]]" +--- + +# Nix / nix-darwin / home-manager Cheat Sheet + +A reference for the nix-darwin + home-manager setup this repo uses to manage macOS system defaults, Homebrew (via nix-homebrew), and dotfiles declaratively. Covers the daily workflow, validation before applying, and rollback — the actual payoff of this whole setup. + +Official docs: · + +--- + +## Table of Contents + +- [How This Repo Is Wired](#how-this-repo-is-wired) +- [Daily Workflow](#daily-workflow) +- [Validating Before You Apply](#validating-before-you-apply) +- [Rollback](#rollback) +- [Adding a Package](#adding-a-package) +- [Gotchas](#gotchas) +- [FAQ](#faq) + +--- + +## How This Repo Is Wired + +Three files, three jobs: + +| File | Owns | +|---|---| +| `flake.nix` | Inputs (nixpkgs, nix-darwin, nix-homebrew, home-manager) and the `darwinConfigurations."mac"` output | +| `configuration.nix` | macOS system defaults (`system.defaults`), and Homebrew — GUI apps (casks), asdf + its build deps, and the two tapped CLI tools not worth moving to Nix | +| `home.nix` | User-level: CLI tools available in nixpkgs (`home.packages`), shell/prompt/fzf/zoxide/atuin config (`programs.*`), and every dotfile symlink | + +`bootstrap.sh` is the one-time entry point for a fresh Mac (installs Nix, does the first switch, then registers asdf runtimes and prompts for git identity). `rebuild.sh` is what you reach for day-to-day after editing config. + +--- + +## Daily Workflow + +Edit `configuration.nix` or `home.nix`, then: + +```bash +./rebuild.sh +``` + +Under the hood this runs: + +```bash +sudo /run/current-system/sw/bin/darwin-rebuild switch --flake .#mac +``` + +**Why the full path with `sudo`?** `sudo` resets `PATH` and won't find `darwin-rebuild` on it even though your regular shell can. This is also why `bootstrap.sh`'s first-ever switch uses `sudo nix run nix-darwin/nix-darwin-26.05#darwin-rebuild -- switch --flake .#mac` instead — `darwin-rebuild` doesn't exist as an installed command yet on a brand new machine. + +--- + +## Validating Before You Apply + +Two levels, cheapest first: + +```bash +# Fast structural check — evaluates the flake, no building. Catches syntax +# errors and type mismatches in seconds. +nix flake check --no-build + +# Full dry-run build — actually resolves and would-build every derivation, +# without touching the live system. Slower, but catches real package-name +# typos (e.g. "git-delta" instead of nixpkgs' actual name "delta"). +nix build .#darwinConfigurations.mac.system --dry-run +``` + +`./verify.sh` runs `nix flake check --no-build` as its very first check — a failing flake fails there before anything else is even checked. + +--- + +## Rollback + +This is the actual point of the whole migration: every `darwin-rebuild switch` creates a new, numbered **generation**. Switching is atomic — if a new generation is bad, you go back to the last good one instantly, no manual undo required: + +```bash +# Roll back to the previous generation +sudo darwin-rebuild --rollback + +# List all generations +darwin-rebuild --list-generations + +# Roll back to a specific generation number +sudo darwin-rebuild switch --flake .#mac --rollback # (or re-switch an older flake.lock commit) +``` + +Compare this to the old `setup.sh` + `Brewfile` model: there was no equivalent of "undo the last package change" — you'd have to manually `brew uninstall` and hope you remembered everything that changed. + +--- + +## Adding a Package + +**GUI app, or a tool that isn't in nixpkgs / not worth moving:** add it to `configuration.nix`'s `homebrew.casks` (GUI apps) or `homebrew.brews` (CLI, including anything tapped from a third-party repo like `hashicorp/tap/terraform`). + +**CLI tool that exists in nixpkgs:** add it to `home.nix`'s `home.packages` list. Check it exists first — the nixpkgs name doesn't always match the Homebrew formula name (e.g. Homebrew's `git-delta` is just `delta` in nixpkgs; `nix build --dry-run` will tell you immediately with `error: undefined variable ''` if you guess wrong). + +Either way: + +```bash +./rebuild.sh +``` + +No separate "install" step — the switch both builds and applies. + +--- + +## Gotchas + +### `homebrew.onActivation.cleanup` — read this before touching it + +nix-homebrew can force-uninstall any Homebrew package not declared in `configuration.nix`'s `homebrew.brews`/`homebrew.casks`, via `onActivation.cleanup = "zap"`. **This repo deliberately sets it to `"none"` instead** (see `configuration.nix`) — undeclared packages are left alone rather than force-removed. Given how much is installed here, `"zap"` is a real footgun: a package you `brew install`ed by hand outside the flake, or a typo that silently dropped an entry from `configuration.nix`, would get force-uninstalled on the next switch. Don't flip this to `"zap"` without first running `brew list --formula` / `brew list --cask` and confirming it matches `configuration.nix` exactly. + +### `sudo` and `darwin-rebuild` don't mix without the full path + +Covered above under [Daily Workflow](#daily-workflow) — `rebuild.sh` and `bootstrap.sh` both already handle this correctly, but if you ever run `darwin-rebuild` manually, remember `sudo darwin-rebuild switch ...` alone will fail with `command not found`. + +### `flake.lock` can end up root-owned + +Any command run with `sudo` (like the switch itself) can update `flake.lock` as root, which then blocks your normal user from running `nix flake check`/`nix build` afterwards ("Permission denied"). Fix: + +```bash +sudo chown $(whoami) flake.lock +``` + +### home-manager refuses to overwrite existing symlinks — even ones it should own + +If a path `home.nix` wants to manage already has a real file or a foreign symlink at that location (e.g. left over from a pre-Nix setup), activation may print `Existing file '...' would be clobbered` and abort, even with `backupFileExtension` set in `flake.nix`. This has been observed specifically for home-manager-*generated* files (`.zshrc`, `~/.config/starship.toml`) rather than plain `mkOutOfStoreSymlink` targets. Fix: move the conflicting file aside by hand before re-running the switch: + +```bash +mv ~/.zshrc ~/.zshrc.hm-backup +``` + +--- + +## FAQ + +**Q: How do I know if a package should go in `configuration.nix` or `home.nix`?** + +If it's a `.app` bundle, or a CLI tool that isn't in nixpkgs (or isn't worth moving), it's Homebrew (`configuration.nix`). If it's a CLI tool available in nixpkgs, it's `home.packages` in `home.nix`. See [[homebrew-cheatsheet]] for the general Homebrew-vs-Nix package boundary this repo draws. + +**Q: What does `darwin-rebuild switch --flake .#mac` mean, exactly?** + +`.#mac` refers to the `darwinConfigurations."mac"` output defined in `flake.nix` (`.` = "this directory's flake"). If you ever rename the host label, it has to change in three places — `flake.nix`, `rebuild.sh`, and `bootstrap.sh` — all at once. + +**Q: Why keep asdf instead of managing language runtimes in Nix too?** + +Nix pins global versions much like asdf's fallback, but loses asdf's effortless per-project `.tool-versions` override — a directory-scoped file asdf auto-detects walking up the tree. Nix's per-project story is devshells/flakes/direnv, a heavier workflow. This repo keeps asdf deliberately. + +**Q: `nix build --dry-run` says `error: undefined variable 'some-package'` — now what?** + +The nixpkgs attribute name doesn't match what you typed. Search for the real name before adding it to `home.packages`. diff --git a/docs/cheatsheets/vim-cheat-sheet.md b/docs/cheatsheets/vim-cheat-sheet.md new file mode 100644 index 0000000..14008ec --- /dev/null +++ b/docs/cheatsheets/vim-cheat-sheet.md @@ -0,0 +1,333 @@ +--- +tag: + - type/cheatsheet + - topic/vim + - topic/editor +related: + - "[[modern-cli-cheatsheet]]" +--- +# Vim Cheat Sheet (80/20) + +## Modes + +| Mode | Enter | Purpose | +|------|-------|---------| +| Normal | `Esc` | Navigate & manipulate text | +| Insert | `i`, `a`, `o` | Type text | +| Visual | `v`, `V`, `Ctrl+v` | Select text | +| Command | `:` | Run commands like save/quit | + +--- + +# Movement + +## Character + +| Command | Action | +|----------|--------| +| `h` | Left | +| `j` | Down | +| `k` | Up | +| `l` | Right | + +## Word + +| Command | Action | +|----------|--------| +| `w` | Next word start | +| `b` | Previous word start | +| `e` | End of current/next word | + +Example: + +```text +hello amazing world +^ +``` + +| Keys | Cursor moves to | +|------|-----------------| +| `w` | `amazing` | +| `w` | `world` | +| `b` | `amazing` | +| `e` | End of `amazing` | + +--- + +## Line + +| Command | Action | +|----------|--------| +| `0` | Beginning of line | +| `^` | First non-whitespace character | +| `$` | End of line | + +--- + +## File + +| Command | Action | +|----------|--------| +| `gg` | Top of file | +| `G` | Bottom of file | +| `25G` | Go to line 25 | + +--- + +## Matching Brackets + +| Command | Action | +|----------|--------| +| `%` | Jump between matching `()`, `{}`, `[]` | + +--- + +## Find Characters + +| Command | Action | +|----------|--------| +| `fx` | Jump to next `x` | +| `tx` | Jump before next `x` | +| `Fx` | Search backward for `x` | +| `Tx` | Search backward before `x` | + +Example: + +```text +const myVariable = foo(); +^ +``` + +- `f=` → cursor on `=` +- `t=` → cursor before `=` + +--- + +# Editing + +## Insert + +| Command | Action | +|----------|--------| +| `i` | Insert before cursor | +| `a` | Insert after cursor | +| `o` | Open new line below | + +--- + +## Delete + +| Command | Action | +|----------|--------| +| `x` | Delete character | +| `dd` | Delete current line | + +--- + +## Copy & Paste + +| Command | Action | +|----------|--------| +| `yy` | Copy current line | +| `p` | Paste after cursor | + +--- + +## Undo / Redo + +| Command | Action | +|----------|--------| +| `u` | Undo | +| `Ctrl+r` | Redo | + +--- + +# Operators + +General form: + +``` +operator + motion +``` + +Examples: + +| Command | Meaning | +|----------|---------| +| `dw` | Delete word | +| `diw` | Delete current word | +| `ciw` | Change current word | +| `dap` | Delete paragraph | +| `cip` | Change paragraph | +| `d$` | Delete to end of line | +| `c$` | Change to end of line | + +--- + +## Examples + +### `dw` + +Deletes from the cursor to the end of the word. + +### `diw` + +Deletes the entire word regardless of cursor position. + +Before + +```text +hello amazing world + ^ +``` + +After + +```text +hello world +``` + +--- + +### `ciw` + +Deletes the word and immediately enters Insert mode. + +Before + +```js +const age = 25; +``` + +Cursor anywhere on `age` + +``` +ciw +name + +``` + +Result + +```js +const name = 25; +``` + +--- + +### `d$` + +Deletes from the cursor to the end of the line. + +--- + +### `c$` + +Deletes to the end of the line and enters Insert mode. + +--- + +# Search + +| Command | Action | +|----------|--------| +| `/text` | Search forward | +| `n` | Next match | +| `*` | Next occurrence of current word | +| `#` | Previous occurrence of current word | + +Example: + +``` +/function +``` + +Press `Enter`, then `n` for the next match. + +--- + +# Visual Mode + +## Character Selection + +| Command | Action | +|----------|--------| +| `v` | Select characters | + +--- + +## Line Selection + +| Command | Action | +|----------|--------| +| `V` | Select whole lines | + +--- + +## Block Selection + +| Command | Action | +|----------|--------| +| `Ctrl+v` | Rectangular selection | + +Useful for editing multiple lines simultaneously. + +--- + +# Most Useful Everyday Commands + +| Command | Purpose | +|----------|---------| +| `w` | Next word | +| `b` | Previous word | +| `$` | End of line | +| `^` | First code on line | +| `gg` | Top of file | +| `G` | Bottom of file | +| `%` | Matching bracket | +| `i` | Insert | +| `a` | Append | +| `o` | New line | +| `x` | Delete character | +| `dd` | Delete line | +| `yy` | Copy line | +| `p` | Paste | +| `u` | Undo | +| `Ctrl+r` | Redo | +| `dw` | Delete word | +| `diw` | Delete current word | +| `ciw` | Replace current word | +| `d$` | Delete to end of line | +| `c$` | Replace to end of line | +| `/` | Search | +| `n` | Next search result | +| `*` | Next occurrence of current word | +| `#` | Previous occurrence of current word | +| `v` | Character selection | +| `V` | Line selection | +| `Ctrl+v` | Block selection | + +--- + +# The Vim Formula + +Almost every Vim command follows this pattern: + +``` +[count] + operator + motion +``` + +Examples: + +| Command | Meaning | +|----------|---------| +| `3w` | Move forward 3 words | +| `5j` | Move down 5 lines | +| `2dd` | Delete 2 lines | +| `d3w` | Delete 3 words | +| `c2w` | Change 2 words | +| `y$` | Copy to end of line | + +Once you understand this pattern, you can derive hundreds of Vim commands without memorizing them individually. + +--- diff --git a/docs/inventory.md b/docs/inventory.md index e2b1a52..f5a35d8 100644 --- a/docs/inventory.md +++ b/docs/inventory.md @@ -1,59 +1,61 @@ # Installed Software Inventory > **Source of truth per install method:** -> - Packages: `Brewfile` (formulae + casks) -> - Language versions: `.tool-versions` -> - Non-Homebrew installs: `setup.sh` +> - GUI apps (casks), asdf + build deps, tapped tools: `configuration.nix`'s `homebrew` block +> - CLI tools available in nixpkgs: `home.nix`'s `home.packages` / `programs.*` +> - Language runtime versions: `.tool-versions` +> - Non-Homebrew, non-Nix installs: `bootstrap.sh` > > Update this file whenever any of the above change. --- -## Bootstrap (installed before Homebrew) +## Bootstrap (installed before everything else) | Tool | Installed via | Notes | |------|---------------|-------| -| Xcode Command Line Tools | `xcode-select --install` | Required for `git` and native compilation on macOS | -| Homebrew | `curl \| bash` (official install.sh) | Package manager; bootstrapped first by `setup.sh` | +| Xcode Command Line Tools | `xcode-select --install` | Required for `git` and native compilation on macOS; installed by `bootstrap.sh` | +| Determinate Nix | `curl \| sh` (install.determinate.systems) | Nix package manager; installed by `bootstrap.sh` | +| Homebrew | Adopted by nix-homebrew (`autoMigrate = true` in `configuration.nix`) | Package manager for GUI apps + a few CLI tools; management itself is now declared in `configuration.nix`, not a separate bootstrap step | --- ## GUI Applications -All installed via `brew install --cask` unless noted otherwise. +All declared in `configuration.nix`'s `homebrew.casks` (applied via `darwin-rebuild switch`, i.e. `./bootstrap.sh` or `./rebuild.sh`). | App | Installed via | Category | Notes | |-----|---------------|----------|-------| -| Ghostty | `brew install --cask` | Terminal | GPU-accelerated; replaces iTerm2 | -| WezTerm | `brew install --cask` | Terminal | GPU-accelerated, cross-platform terminal emulator | -| Visual Studio Code | `brew install --cask` | Editor | | -| Cursor | `brew install --cask` | Editor | AI-native code editor; global AI rules symlinked from `cursor-rules/git-commits.mdc` to `~/.cursor/rules/git-commits.mdc` | -| Docker Desktop | `brew install --cask` | Containers | Provides `docker` and `docker compose` CLIs — see [Docker cheat sheet](docker-cheatsheet.md) | -| Google Chrome | `brew install --cask` | Browser | | -| Firefox | `brew install --cask` | Browser | | -| ChatGPT Atlas | `brew install --cask` | Browser | OpenAI's browser with ChatGPT built in; requires arm64 + macOS 14 | -| Google Cloud CLI | `brew install --cask` | Cloud | Includes `gcloud`, `gsutil`, `bq`; kubectl installed on demand | -| Rectangle | `brew install --cask` | Productivity | Keyboard-driven window tiling | -| AppCleaner | `brew install --cask` | Productivity | Clean app uninstalls | -| Maccy | `brew install --cask` | Productivity | Clipboard history (Cmd+Shift+C) | -| LinearMouse | `brew install --cask` | Productivity | Mouse customization: side buttons, scroll, acceleration | -| OpenSuperWhisper | `brew install --cask` | Productivity | Open-source AI voice-to-text dictation (system-wide) | -| Obsidian | `brew install --cask` | Productivity | Markdown-based knowledge base / note-taking | -| Granola | `brew install --cask` | Productivity | AI-powered notepad for meetings | -| Postman | `brew install --cask` | API Testing | REST client | -| Whimsical | `brew install --cask` | Productivity | Collaboration and diagramming tool | -| Claude (desktop) | `brew install --cask claude` | AI | Anthropic Claude desktop app | -| Codex (desktop) | `brew install --cask codex-app` | AI | OpenAI Codex desktop app for managing coding agents; global instructions symlinked from `codex-instructions.md` to `~/.codex/instructions.md` | -| Claude Code | `curl \| bash` (claude.ai/install.sh) | AI / CLI | Native auto-updating installer — **not** via Homebrew; global config (`CLAUDE.md`, `settings.json`, `statusline-command.sh`) symlinked from `claude/` to `~/.claude/` | +| Ghostty | `configuration.nix` (homebrew.casks) | Terminal | GPU-accelerated; replaces iTerm2 | +| WezTerm | `configuration.nix` (homebrew.casks) | Terminal | GPU-accelerated, cross-platform terminal emulator | +| Visual Studio Code | `configuration.nix` (homebrew.casks) | Editor | | +| Cursor | `configuration.nix` (homebrew.casks) | Editor | AI-native code editor; no global rules mechanism exists (Cursor only reads project-level `.cursor/rules/`/`AGENTS.md`) | +| Docker Desktop | `configuration.nix` (homebrew.casks) | Containers | Provides `docker` and `docker compose` CLIs — see [Docker cheat sheet](cheatsheets/docker-cheatsheet.md) | +| Google Chrome | `configuration.nix` (homebrew.casks) | Browser | | +| Firefox | `configuration.nix` (homebrew.casks) | Browser | | +| ChatGPT Atlas | `configuration.nix` (homebrew.casks) | Browser | OpenAI's browser with ChatGPT built in; requires arm64 + macOS 14 | +| Google Cloud CLI | `configuration.nix` (homebrew.casks) | Cloud | Includes `gcloud`, `gsutil`, `bq`; kubectl installed on demand | +| Rectangle | `configuration.nix` (homebrew.casks) | Productivity | Keyboard-driven window tiling | +| AppCleaner | `configuration.nix` (homebrew.casks) | Productivity | Clean app uninstalls | +| Maccy | `configuration.nix` (homebrew.casks) | Productivity | Clipboard history (Cmd+Shift+C) | +| LinearMouse | `configuration.nix` (homebrew.casks) | Productivity | Mouse customization: side buttons, scroll, acceleration. Config write-back handled by `home.nix`'s `mkOutOfStoreSymlink` + `home.activation.backupLinearMouseConfig` | +| OpenSuperWhisper | `configuration.nix` (homebrew.casks) | Productivity | Open-source AI voice-to-text dictation (system-wide) | +| Obsidian | `configuration.nix` (homebrew.casks) | Productivity | Markdown-based knowledge base / note-taking. `docs/cheatsheets/` doubles as a vault folder — see `home.nix`'s `Documents/workspace/my-matrix/a-utils/cheatsheets` symlink | +| Granola | `configuration.nix` (homebrew.casks) | Productivity | AI-powered notepad for meetings | +| Postman | `configuration.nix` (homebrew.casks) | API Testing | REST client | +| Whimsical | `configuration.nix` (homebrew.casks) | Productivity | Collaboration and diagramming tool | +| Claude (desktop) | `configuration.nix` (homebrew.casks: `claude`) | AI | Anthropic Claude desktop app | +| Codex (desktop) | `configuration.nix` (homebrew.casks: `codex-app`) | AI | OpenAI Codex desktop app for managing coding agents; global instructions symlinked from `home/AGENTS.md` to `~/.codex/AGENTS.md` | +| Claude Code | `configuration.nix` (homebrew.casks: `claude-code`) | AI / CLI | Global config (`CLAUDE.md` from `home/AGENTS.md`, `settings.json` with inline statusline command) symlinked via home-manager into `~/.claude/`. Updates via Homebrew's `onActivation.autoUpdate`, not a native installer. | --- ## CLI Tools & Utilities -All installed via `brew install` (formula) unless noted otherwise. - ### Version control & core utilities +Installed via `home.nix`'s `home.packages` (Nix), unless noted otherwise. + | Tool | Description | |------|-------------| | `git` | Version control | @@ -62,83 +64,104 @@ All installed via `brew install` (formula) unless noted otherwise. | `tree` | Directory tree visualizer | | `wget` | HTTP downloader | | `tmux` | Terminal multiplexer | -| `nvim` (neovim) | Modal text editor | +| `nvim` (neovim) | Modal text editor — see `home/.config/nvim/` for the full lazy.nvim config | ### Modern CLI replacements +Installed via `home.nix`'s `home.packages` (Nix), unless noted otherwise. + | Tool | Replaces | Description | |------|----------|-------------| | `rg` (ripgrep) | `grep` | Fast recursive search; respects `.gitignore` | | `fd` | `find` | Intuitive file finder; parallel, regex by default | | `bat` | `cat` | Syntax-highlighted file viewer with line numbers | | `eza` | `ls` | Modern listing with icons, git status, tree view | -| `zoxide` (`z`) | `cd` | Learns habits; jump to dirs by partial name | -| `fzf` | — | Fuzzy finder for history, files, branches, processes | -| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; config in `configs/gitconfig`, wired via `[include]` in `~/.gitconfig` | +| `zoxide` (`z`) | `cd` | Learns habits; jump to dirs by partial name — `home.nix`'s `programs.zoxide` (Nix, home-manager module, handles shell init) | +| `fzf` | — | Fuzzy finder for history, files, branches, processes — `home.nix`'s `programs.fzf` (Nix, home-manager module, handles shell integration) | +| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; config in `configs/gitconfig`, wired via `[include]` in `~/.gitconfig` (nixpkgs package name is `delta`, not `git-delta`) | | `lazygit` (`lg`) | — | Full terminal UI for git | | `btop` | `top` / `htop` | Modern resource monitor with graphs | | `dust` | `du` | Tree-based disk usage visualizer | | `tldr` | `man` (common cases) | Simplified man pages with real examples | -| `atuin` | `~/.zsh_history` | SQLite-backed shell history with search | +| `atuin` | `~/.zsh_history` | SQLite-backed shell history with search — `home.nix`'s `programs.atuin` (Nix, home-manager module) | ### AI coding CLIs | Tool | Installed via | Description | |------|---------------|-------------| -| `codex` | `brew install --cask codex` | OpenAI Codex CLI — coding agent in terminal (cask binary; depends on `ripgrep`) | -| `claude` | `curl \| bash` (claude.ai/install.sh) | Anthropic Claude Code CLI — auto-updating native installer, **not** via Homebrew | -| `opencode` | `brew install` | AI coding agent, built for the terminal | +| `claude` | `configuration.nix` (homebrew.casks: `claude-code`) | Anthropic Claude Code CLI | +| `codex` | `configuration.nix` (homebrew.casks: `codex`) | OpenAI Codex CLI — coding agent in terminal | +| `opencode` | `home.nix` (home.packages) | AI coding agent, built for the terminal | +| `agent` / `cursor-agent` | Installed independently by Cursor (not via this repo) | Cursor's agent CLI, under `~/.local/bin` — aliased to `aa` in `home.nix`'s `programs.zsh.shellAliases` | + +### Terminal multiplexing + +| Tool | Installed via | Description | +|------|---------------|-------------| +| `tmux` | `home.nix` (home.packages) | Classic terminal multiplexer | +| `herdr` | `configuration.nix` (homebrew.brews) | Agent multiplexer for the terminal (tmux-style `Ctrl+B` prefix bindings) — config in `home/.config/herdr/config.toml` | + +### Cloud, infrastructure & payments CLIs + +| Tool | Installed via | Description | +|------|---------------|-------------| +| `terraform` | `configuration.nix` (homebrew.brews, tap: `hashicorp/tap`) | Infrastructure-as-code CLI | +| `stripe` | `configuration.nix` (homebrew.brews, tap: `stripe/stripe-cli`) | Stripe CLI: webhook testing, API calls | ### Shell productivity -| Tool | Description | -|------|-------------| -| `starship` | Cross-shell prompt (replaces Powerlevel10k) | -| `antidote` | Zsh plugin manager (fast, static-generated loader) | +| Tool | Installed via | Description | +|------|---------------|-------------| +| `starship` | `home.nix` (programs.starship) | Cross-shell prompt (replaces Powerlevel10k) | + +Zsh plugin management (autosuggestions, syntax-highlighting, completion) is native to home-manager now — see [Zsh plugins](#zsh-plugins) below. Antidote and `.zsh_plugins.txt` were retired. ### Zsh plugins -Loaded by Antidote. Plugin list is declared in **`.zsh_plugins.txt`** — that is the only place plugins are defined. +**Retired.** Antidote (the plugin manager) and `.zsh_plugins.txt` no longer exist. Their functionality is replaced by home-manager's native `programs.zsh` toggles, declared directly in `home.nix`: -| Plugin | Purpose | +| Former plugin | Now | |--------|---------| -| `zsh-users/zsh-autosuggestions` | Inline history suggestions (right arrow to accept) | -| `zsh-users/zsh-completions` | Extra completions for git, docker, kubectl, etc. | -| `zsh-users/zsh-syntax-highlighting` | Live syntax highlighting as you type (must load last) | +| `zsh-users/zsh-autosuggestions` | `programs.zsh.autosuggestion.enable = true` | +| `zsh-users/zsh-syntax-highlighting` | `programs.zsh.syntaxHighlighting.enable = true` | +| `zsh-users/zsh-completions` | `home.packages`' `zsh-completions` + `programs.zsh.enableCompletion = true`, with the extra completion fpath wired in `programs.zsh.initContent` | ### Language version manager -| Tool | Description | -|------|-------------| -| `asdf` | Manages language runtimes via `.tool-versions` | +| Tool | Installed via | Description | +|------|---------------|-------------| +| `asdf` | `configuration.nix` (homebrew.brews) | Manages language runtimes via `.tool-versions`. Kept Homebrew-managed and outside Nix by design — see README's Design Decisions. | ### Post-install integration | Tool | Installed via | Description | |------|---------------|-------------| -| fzf key bindings | `fzf/install` script (run by `setup.sh`) | Wires `Ctrl+T`, `Ctrl+R`, `Alt+C` into Zsh | +| fzf key bindings | `home.nix`'s `programs.fzf.enableZshIntegration` (Nix, home-manager module) | Wires `Ctrl+T`, `Ctrl+R`, `Alt+C` into Zsh — no separate install script needed anymore | ### Build & compilation support -| Tool | Required by | -|------|-------------| -| `coreutils` | asdf on macOS | -| `openssl@3` | Python and Node native modules | -| `readline` | Python build | -| `xz` | Python build | +| Tool | Installed via | Required by | +|------|---------------|-------------| +| `coreutils` | `configuration.nix` (homebrew.brews) | asdf on macOS | +| `openssl@3` | `configuration.nix` (homebrew.brews) | Python and Node native modules | +| `readline` | `configuration.nix` (homebrew.brews) | Python build | +| `xz` | `configuration.nix` (homebrew.brews) | Python build | ### Developer fonts -| Font | Installed via | Use | -|------|---------------|-----| -| JetBrains Mono Nerd Font | `brew install --cask` | Primary coding font (default in Ghostty) | -| Fira Code Nerd Font | `brew install --cask` | Alternative with strong ligatures | +Installed via `home.nix`'s `home.packages` (Nix nerd-fonts derivations), not Homebrew casks. + +| Font | Use | +|------|-----| +| Hack Nerd Font | Primary system font (Ghostty + WezTerm default) | +| JetBrains Mono Nerd Font | Alternative — see README "Personalize Theme & Font" for how to switch | +| Fira Code Nerd Font | Alternative with strong ligatures | --- ## Language Runtimes -Managed by **asdf**. Versions are declared in **`.tool-versions`** — that is the only source of truth for version numbers. Edit that file and run `asdf install` to add or change a version. +Managed by **asdf**. Versions are declared in **`.tool-versions`** — that is the only source of truth for version numbers. Edit that file and run `./bootstrap.sh` (or `asdf install` directly) to add or change a version. Deliberately kept outside Nix's management — see README's Design Decisions for why. | Language | `.tool-versions` key | |----------|----------------------| diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..2693898 --- /dev/null +++ b/flake.lock @@ -0,0 +1,107 @@ +{ + "nodes": { + "brew-src": { + "flake": false, + "locked": { + "lastModified": 1781226006, + "narHash": "sha256-w4ZTuOnhYiDxjaynrMTASzp802QblBWmo3wpB8wVN4Y=", + "owner": "Homebrew", + "repo": "brew", + "rev": "109191be4988470b51a60a5ef1998520aa24c01b", + "type": "github" + }, + "original": { + "owner": "Homebrew", + "ref": "6.0.1", + "repo": "brew", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1783221248, + "narHash": "sha256-ESQnuNHEDChsB4IxoLRhscVahqkDWkTb+qdIz8euYt4=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "af2beae5f0fae0a4310cc0e6aef2572f56090353", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "release-26.05", + "repo": "home-manager", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781772065, + "narHash": "sha256-xIbRSwDB1GBAUsWsQZUjudGfAGQt3BOpsWaO/ugVa4w=", + "owner": "nix-darwin", + "repo": "nix-darwin", + "rev": "adda04f0bf4819575b1978c2f8d78401b3c2be12", + "type": "github" + }, + "original": { + "owner": "nix-darwin", + "ref": "nix-darwin-26.05", + "repo": "nix-darwin", + "type": "github" + } + }, + "nix-homebrew": { + "inputs": { + "brew-src": "brew-src" + }, + "locked": { + "lastModified": 1781389246, + "narHash": "sha256-ORqLAo/hoJdsZC7UPAuEHev6S0+XIqKEC7vjo5prz1k=", + "owner": "zhaofengli", + "repo": "nix-homebrew", + "rev": "de7953a08ed4bb9245be043e468561c17b89130d", + "type": "github" + }, + "original": { + "owner": "zhaofengli", + "repo": "nix-homebrew", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1783549019, + "narHash": "sha256-0XnckG4ZhBmAsYa9mLuEIFowBG0fDGPLHduQGsbMS4A=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "74cc63f702f7d60a557e152a57b40fb1fd0f72ac", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-26.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nix-homebrew": "nix-homebrew", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..299e9c3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,40 @@ +{ + description = "dotfiles"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-26.05-darwin"; + nix-darwin.url = "github:nix-darwin/nix-darwin/nix-darwin-26.05"; + nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; + + nix-homebrew.url = "github:zhaofengli/nix-homebrew"; + + home-manager.url = "github:nix-community/home-manager/release-26.05"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs@{ self, nix-darwin, nix-homebrew, home-manager, nixpkgs }: + let + # The one username line to change if this isn't your machine. + user = "lakshyadevsingh"; + in + { + darwinConfigurations."mac" = nix-darwin.lib.darwinSystem { + specialArgs = { inherit user; }; + modules = [ + ./configuration.nix + nix-homebrew.darwinModules.nix-homebrew + home-manager.darwinModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.extraSpecialArgs = { inherit user; }; + # Renames any pre-existing plain file/symlink at a path home-manager + # wants to manage (e.g. setup.sh's old ~/.zshrc symlink) instead of + # erroring out on first switch. + home-manager.backupFileExtension = "hm-backup"; + home-manager.users.${user} = import ./home.nix; + } + ]; + }; + }; +} diff --git a/home.nix b/home.nix new file mode 100644 index 0000000..0efad88 --- /dev/null +++ b/home.nix @@ -0,0 +1,226 @@ +{ config, pkgs, user, lib, ... }: + +let + dotfiles = "${config.home.homeDirectory}/.dotfiles"; +in + +{ + home.username = user; + home.homeDirectory = "/Users/${user}"; + home.stateVersion = "26.05"; + + home.packages = with pkgs; [ + # Modern CLI replacements & utilities not covered by a programs.* module + ripgrep # fast search + fd # fast find + bat # + lazygit + eza # + btop + delta + dust + tldr + tmux + neovim + jq + tree + wget + git + gh + opencode + zsh-completions + # Fonts — Hack is the primary system font (Ghostty/WezTerm); JetBrains + # Mono and Fira Code stay installed as alternatives. See README.md + # "Customization" for how to switch. + nerd-fonts.hack + nerd-fonts.jetbrains-mono + nerd-fonts.fira-code + ]; + fonts.fontconfig.enable = true; + + home.sessionVariables.EDITOR = "nvim"; + + programs.zsh = { + enable = true; + autosuggestion.enable = true; # ghost text from history (replaces zsh-autosuggestions) + syntaxHighlighting.enable = true; # commands turn green when valid (replaces zsh-syntax-highlighting) + enableCompletion = true; # replaces zsh-completions plugin + manual compinit + + shellAliases = { + ".." = "cd .."; + "..." = "cd ../.."; + "...." = "cd ../../.."; + + # Modern CLI replacements + ls = "eza --icons --group-directories-first"; + ll = "eza -lah --git --icons"; + lt = "eza --tree --level=2 --icons"; + la = "ls -a"; + lla = "ll -a"; + cat = "bat --paging=never"; + top = "btop"; + du = "dust"; + + # Git shortcuts + g = "git"; + gs = "git status"; + gd = "git diff"; + gds = "git diff --staged"; + gl = "git log --oneline --graph --decorate -20"; + lg = "lazygit"; + add = "git add ."; + push = "git push"; + pull = "git pull"; + m = "git switch main"; + + reload = "exec zsh"; + + # High-agency agent shortcuts — opt-in, bypass permission prompts. + cc = "claude --dangerously-skip-permissions"; + co = "codex --full-auto"; + aa = "agent"; + }; + + initContent = lib.mkMerge [ + (lib.mkBefore '' + # Homebrew PATH (Apple Silicon) — must come first; most tools live under /opt/homebrew. + eval "$(/opt/homebrew/bin/brew shellenv)" + + # asdf shims — must come before any tool that might invoke node/python/go. + export PATH="''${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" + + # ~/.local/bin: user-installed tools not managed by Nix/Homebrew (e.g. + # Cursor's `agent`/`cursor-agent` CLI, pip --user scripts). Appended + # AFTER Homebrew's path on purpose — a stale ~/.local/bin/claude + # symlink from Claude's old native installer lives here too, and it + # must never shadow the current claude-code cask binary. + export PATH="$PATH:$HOME/.local/bin" + + # zsh-completions extra definitions (git, docker, kubectl, etc.) — must load before compinit. + fpath+=(${pkgs.zsh-completions}/share/zsh/site-functions) + '') + '' + # Ctrl+F accepts the current autosuggestion (ghost text from history). + bindkey '^f' autosuggest-accept + + # fzf preview integration. Uses bat for file contents, eza for directory trees. + export FZF_CTRL_T_OPTS="--preview 'bat -n --color=always --line-range :500 {}'" + export FZF_ALT_C_OPTS="--preview 'eza --tree --color=always {} | head -200'" + + # Docker CLI completions (added by Docker Desktop on first launch). + if [[ -d "$HOME/.docker/completions" ]]; then + fpath=($HOME/.docker/completions $fpath) + fi + '' + ]; + }; + + programs.starship = { + enable = true; + enableZshIntegration = true; + settings = { + format = "$directory$git_branch$git_status$nodejs$python$golang$cmd_duration$line_break$character"; + # Character colors match reference; segments (nodejs/python/golang) + # kept from the pre-migration config since they're actively useful here. + character = { + success_symbol = "[❯](purple)"; + error_symbol = "[❯](red)"; + }; + directory = { + truncation_length = 3; + truncate_to_repo = true; + style = "bold cyan"; + }; + git_branch = { + symbol = " "; + style = "bold purple"; + }; + git_status.style = "bold red"; + cmd_duration = { + min_time = 2000; + format = "[$duration]($style) "; + style = "yellow"; + }; + python = { + symbol = " "; + format = "[$symbol$pyenv_prefix($version )(\\($virtualenv\\) )]($style)"; + style = "yellow"; + }; + nodejs = { + symbol = " "; + format = "[$symbol($version )]($style)"; + style = "green"; + }; + golang = { + symbol = " "; + format = "[$symbol($version )]($style)"; + style = "cyan"; + }; + }; + }; + + programs.zoxide = { + enable = true; + enableZshIntegration = true; + }; + + programs.atuin = { + enable = true; + enableZshIntegration = true; + }; + + programs.fzf = { + enable = true; + enableZshIntegration = true; + }; + + # Note: programs.git is deliberately NOT enabled here. It would make + # ~/.gitconfig a Nix-store-managed file, which breaks bootstrap.sh's + # `git config --global user.name/user.email` identity prompt (can't write + # to an immutable symlink). configs/gitconfig + the include.path wiring + # stay exactly as bootstrap.sh already sets them up, until that's revisited. + + # Edit-in-place: the real files stay in this repo, ~/.config just points at them. + home.file.".config/wezterm".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/wezterm"; + home.file.".config/ghostty".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/ghostty"; + home.file.".config/nvim".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/nvim"; + home.file.".config/herdr".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/herdr"; + + home.file.".claude/CLAUDE.md".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; + home.file.".codex/AGENTS.md".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; + home.file.".config/opencode/AGENTS.md".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; + + home.file.".claude/settings.json".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.claude/settings.json"; + + home.file.".tool-versions".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/.tool-versions"; + + # CheatSheets symlink setup: this repo's docs/cheatsheets/ is the source of + # truth; a-utils/cheatsheets just points at it. + home.file."Documents/workspace/my-matrix/a-utils/cheatsheets".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/docs/cheatsheets"; + + # LinearMouse writes back to its config file through the GUI's own + # preferences UI (no export step) — mkOutOfStoreSymlink keeps the real + # file in this repo instead of the read-only Nix store, and the + # activation script below preserves any pre-existing real file the same + # way bootstrap.sh's backup-before-symlink logic did. + home.file.".config/linearmouse/linearmouse.json".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.config/linearmouse/linearmouse.json"; + + home.activation.backupLinearMouseConfig = lib.hm.dag.entryBefore [ "checkLinkTargets" ] '' + TARGET="$HOME/.config/linearmouse/linearmouse.json" + if [[ -f "$TARGET" && ! -L "$TARGET" ]]; then + BACKUP="$TARGET.backup.$(date +%Y%m%d-%H%M%S)" + $DRY_RUN_CMD mv "$TARGET" "$BACKUP" + fi + ''; +} diff --git a/home/.claude/settings.json b/home/.claude/settings.json new file mode 100644 index 0000000..e3ab775 --- /dev/null +++ b/home/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "theme": "dark-ansi", + "statusLine": { + "type": "command", + "command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); used=$(echo \"$input\" | jq -r '.context_window.used_percentage // empty'); if [ -n \"$used\" ]; then printf \"%s | ctx: %.0f%% used\" \"$model\" \"$used\"; else printf \"%s\" \"$model\"; fi" + } +} diff --git a/configs/ghostty-config b/home/.config/ghostty/config similarity index 86% rename from configs/ghostty-config rename to home/.config/ghostty/config index 81df5ac..35daadc 100644 --- a/configs/ghostty-config +++ b/home/.config/ghostty/config @@ -4,13 +4,14 @@ # View all options: ghostty +show-config --default --docs # ---- Typography ---- -font-family = JetBrainsMono Nerd Font +# Hack Nerd Font is the primary system font. See README.md "Customization" +# for how to switch to JetBrains Mono or Fira Code instead. +font-family = Hack Nerd Font font-size = 14 # ---- Theme ---- -# Auto light/dark switching follows macOS system appearance. -# theme = light:Catppuccin Latte,dark:Catppuccin Mocha -theme = light:Catppuccin Mocha,dark:Catppuccin Mocha +# rose-pine-moon everywhere: matches WezTerm and Neovim for a consistent look. +theme = light:Rose Pine Moon,dark:Rose Pine Moon # ---- Window appearance ---- macos-titlebar-style = tabs diff --git a/home/.config/herdr/config.toml b/home/.config/herdr/config.toml new file mode 100644 index 0000000..177e1d5 --- /dev/null +++ b/home/.config/herdr/config.toml @@ -0,0 +1,13 @@ +[keys] +prefix = "ctrl+b" +focus_pane_left = "prefix+h" +focus_pane_down = "prefix+j" +focus_pane_up = "prefix+k" +focus_pane_right = "prefix+l" +split_horizontal = "prefix+double_quote" +split_vertical = "prefix+percent" +new_tab = "prefix+c" +close_tab = "prefix+ampersand" +workspace_picker = "prefix+w" +goto = "prefix+g" +copy_mode = "prefix+y" # herdr's copy-mode entry key; copy-mode's own internal keys (v/space select, y/Enter copy, q/Esc cancel) aren't configurable diff --git a/configs/linearmouse.json b/home/.config/linearmouse/linearmouse.json similarity index 100% rename from configs/linearmouse.json rename to home/.config/linearmouse/linearmouse.json diff --git a/home/.config/nvim/init.lua b/home/.config/nvim/init.lua new file mode 100644 index 0000000..88b3d8b --- /dev/null +++ b/home/.config/nvim/init.lua @@ -0,0 +1,3 @@ +require('vim_config') +require('plugin') +require('keys') diff --git a/home/.config/nvim/lazy-lock.json b/home/.config/nvim/lazy-lock.json new file mode 100644 index 0000000..c7dac44 --- /dev/null +++ b/home/.config/nvim/lazy-lock.json @@ -0,0 +1,11 @@ +{ + "diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" }, + "gitsigns.nvim": { "branch": "main", "commit": "eb60cc7b94c46005237fd34170d76f3a089a90aa" }, + "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, + "neogit": { "branch": "master", "commit": "a0847c4bea5a5f92a36e3f3bf7da99d7d685d3ac" }, + "oil.nvim": { "branch": "master", "commit": "b73018b75affd13fa38e2fc94ef753b465f770d7" }, + "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, + "rose-pine": { "branch": "main", "commit": "ff483051a47e27d84bdef47703538df1ed9f4a47" }, + "snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" }, + "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } +} diff --git a/home/.config/nvim/lua/keys.lua b/home/.config/nvim/lua/keys.lua new file mode 100644 index 0000000..32fc646 --- /dev/null +++ b/home/.config/nvim/lua/keys.lua @@ -0,0 +1,6 @@ +-- save by pressing Escape +vim.keymap.set('n', '', ':w', { desc = 'Save' }) +-- select all +vim.keymap.set('n', '', 'ggVG', { desc = 'Select All' }) +-- pasting over a selection no longer clobbers your clipboard +vim.cmd([[ xnoremap p 'pgv"'.v:register.'y' ]]) diff --git a/home/.config/nvim/lua/plugin.lua b/home/.config/nvim/lua/plugin.lua new file mode 100644 index 0000000..fde2312 --- /dev/null +++ b/home/.config/nvim/lua/plugin.lua @@ -0,0 +1,7 @@ +local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' +if not vim.uv.fs_stat(lazypath) then + vim.fn.system({ 'git', 'clone', '--filter=blob:none', + 'https://github.com/folke/lazy.nvim.git', '--branch=stable', lazypath }) +end +vim.opt.rtp:prepend(lazypath) +require('lazy').setup('plugins') -- load every file in lua/plugins/ diff --git a/home/.config/nvim/lua/plugins/colorscheme.lua b/home/.config/nvim/lua/plugins/colorscheme.lua new file mode 100644 index 0000000..d8c3d6b --- /dev/null +++ b/home/.config/nvim/lua/plugins/colorscheme.lua @@ -0,0 +1,27 @@ +return { + { + 'rose-pine/neovim', + lazy = false, + priority = 1000, + name = 'rose-pine', + config = function() + require('rose-pine').setup({ + dark_variant = 'moon', + dim_inactive_windows = false, + extend_background_behind_borders = false, + styles = { + italic = false, + transparency = vim.uv.os_uname().sysname == 'Darwin' + or string.find(vim.uv.os_uname().sysname, 'Windows') ~= nil + or string.find(vim.uv.os_uname().release, 'WSL') ~= nil, + }, + }) + + vim.cmd('colorscheme rose-pine') + + -- Make the dimmed directory path in the Snacks picker readable + local palette = require('rose-pine.palette') + vim.api.nvim_set_hl(0, 'SnacksPickerDir', { fg = palette.subtle }) + end, + }, +} diff --git a/home/.config/nvim/lua/plugins/git.lua b/home/.config/nvim/lua/plugins/git.lua new file mode 100644 index 0000000..6044261 --- /dev/null +++ b/home/.config/nvim/lua/plugins/git.lua @@ -0,0 +1,12 @@ +return { + { + 'NeogitOrg/neogit', + dependencies = { 'nvim-lua/plenary.nvim', 'sindrets/diffview.nvim' }, + keys = { { 'g', function() require('neogit').open() end, desc = 'Neogit' } }, + }, + { + 'lewis6991/gitsigns.nvim', + event = 'BufWinEnter', + opts = { current_line_blame = true }, -- who last touched this line + }, +} diff --git a/home/.config/nvim/lua/plugins/navigation.lua b/home/.config/nvim/lua/plugins/navigation.lua new file mode 100644 index 0000000..9336452 --- /dev/null +++ b/home/.config/nvim/lua/plugins/navigation.lua @@ -0,0 +1,23 @@ +return { + { + 'stevearc/oil.nvim', + opts = { view_options = { show_hidden = true } }, + keys = { { 'e', 'Oil', desc = 'File Browser' } }, + }, + { + 'folke/snacks.nvim', + priority = 1000, + lazy = false, + opts = { + picker = { enabled = true }, + notifier = { enabled = true }, + input = { enabled = true }, + }, + keys = { + { 'f', function() Snacks.picker.files() end, desc = 'Find Files' }, + { 's', function() Snacks.picker.grep() end, desc = 'Search Text' }, + { 'b', function() Snacks.picker.buffers() end, desc = 'Buffers' }, + { 'gd', function() Snacks.picker.lsp_definitions() end, desc = 'Goto Definition' }, + }, + }, +} diff --git a/home/.config/nvim/lua/plugins/ui.lua b/home/.config/nvim/lua/plugins/ui.lua new file mode 100644 index 0000000..8e172cd --- /dev/null +++ b/home/.config/nvim/lua/plugins/ui.lua @@ -0,0 +1,7 @@ +return { + { + 'folke/which-key.nvim', + lazy = false, + config = true, -- popup that shows what my leader keys do + }, +} diff --git a/home/.config/nvim/lua/vim_config.lua b/home/.config/nvim/lua/vim_config.lua new file mode 100644 index 0000000..dc787e2 --- /dev/null +++ b/home/.config/nvim/lua/vim_config.lua @@ -0,0 +1,11 @@ +local o = vim.opt +vim.g.mapleader = ' ' -- space is the leader key +o.expandtab = true -- spaces, not tabs +o.shiftwidth = 2 -- 2 spaces per indent level +o.number = true -- absolute number on the cursor line, relative elsewhere +o.relativenumber = true -- relative line numbers for fast jumps +o.ignorecase = true -- search is case-insensitive by default +o.smartcase = true -- case-sensitive only if i type a capital +o.clipboard = 'unnamedplus' -- share the system clipboard +o.scrolloff = 16 -- keep cursor away from the screen edge +o.undofile = true -- persistent undo across sessions diff --git a/home/.config/wezterm/wezterm.lua b/home/.config/wezterm/wezterm.lua new file mode 100644 index 0000000..8bb9ce8 --- /dev/null +++ b/home/.config/wezterm/wezterm.lua @@ -0,0 +1,13 @@ +local wezterm = require("wezterm") + +local config = wezterm.config_builder() + +config.color_scheme = "rose-pine-moon" +config.font = wezterm.font("Hack Nerd Font") +config.font_size = 15.0 +config.window_background_opacity = 0.8 +config.macos_window_background_blur = 50 +config.hide_tab_bar_if_only_one_tab = true +config.window_decorations = "RESIZE" + +return config diff --git a/claude/CLAUDE.md b/home/AGENTS.md similarity index 100% rename from claude/CLAUDE.md rename to home/AGENTS.md diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..542f15c --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# rebuild.sh — Daily-use: re-apply the nix-darwin config after editing it. +# +# Usage: +# ./rebuild.sh # build + apply +# ./rebuild.sh --dry-run # build only, don't apply (validate before switching) + +set -euo pipefail + +DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HOST_LABEL="mac" #mac + +if [[ "${1:-}" == "--dry-run" ]]; then + nix build "$DOTFILES_DIR#darwinConfigurations.${HOST_LABEL}.system" --dry-run +else + # sudo resets PATH and won't find darwin-rebuild on it, so use the full + # path — same reason bootstrap.sh's first switch needs it. + sudo /run/current-system/sw/bin/darwin-rebuild switch --flake "$DOTFILES_DIR#${HOST_LABEL}" +fi diff --git a/setup.sh b/setup.sh deleted file mode 100755 index 294f93c..0000000 --- a/setup.sh +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env bash -# setup.sh — Interactive bootstrap for a fresh macOS developer environment. -# -# Usage: -# ./setup.sh # interactive wizard -# ./setup.sh --full # non-interactive, install everything (CI / re-runs) -# -# Idempotent: safe to re-run at any time. - -set -euo pipefail - -DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -FULL=false -[[ "${1:-}" == "--full" ]] && FULL=true - -# State flags set during selection; drive post-install steps. -DID_FZF=false -DID_ASDF=false -DID_CLAUDE=false -ASDF_LANGS=() - -CAT_MODE=all # current category mode: all | custom | skip - -# ── Colors (disabled when not a TTY) ───────────────────────────────────────── -if [[ -t 1 ]]; then - G=$'\033[0;32m' Y=$'\033[0;33m' B=$'\033[0;34m' - C=$'\033[0;36m' W=$'\033[1m' R=$'\033[0m' -else - G='' Y='' B='' C='' W='' R='' -fi - -info() { printf "${B} → ${R}%s\n" "$*"; } -done_() { printf "${G} ✓ ${R}%s\n" "$*"; } -warn() { printf "${Y} ! ${R}%s\n" "$*" >&2; } -section() { printf "\n${W}${C}━━ %s${R}\n" "$*"; } - -# ── Idempotent install wrappers ─────────────────────────────────────────────── -formula() { - if brew list --formula "$1" &>/dev/null 2>&1; then - done_ "$1 (already installed)" - else - info "Installing $1 …"; brew install "$1"; done_ "$1" - fi -} - -cask_pkg() { - if brew list --cask "$1" &>/dev/null 2>&1; then - done_ "$1 (already installed)" - else - info "Installing $1 …"; brew install --cask "$1"; done_ "$1" - fi -} - -# ── Wizard helpers ──────────────────────────────────────────────────────────── - -# category [] -# Sets $CAT_MODE: all | custom | skip -# In --full mode always sets all. -category() { - local name="$1" preview="${2:-}" - section "$name" - [[ -n "$preview" ]] && printf " ${preview}\n" - if $FULL; then CAT_MODE=all; return; fi - local ans - printf "\n Install? ${W}[A]ll${R} [c]ustomize [s]kip (default: A) " - read -r ans -# Use only inside an `if` — returns 0=install, 1=skip. -# all → always 0; skip → always 1; custom → prompts. -want() { - [[ "$CAT_MODE" == skip ]] && return 1 - [[ "$CAT_MODE" == all ]] && return 0 - local ans - printf " %-50s [Y/n] " "$1" - read -r ans /dev/null 2>&1; then - info "Installing Xcode Command Line Tools (GUI prompt) …" - xcode-select --install - warn "Re-run this script once CLT installation finishes." - exit 0 -fi -done_ "Xcode Command Line Tools" - -if ! command -v brew >/dev/null 2>&1; then - info "Installing Homebrew …" - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - eval "$(/opt/homebrew/bin/brew shellenv)" -fi -# shellcheck disable=SC2016 -if ! grep -q 'brew shellenv' "$HOME/.zprofile" 2>/dev/null; then - echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zprofile" -fi -done_ "Homebrew" - -# ── Version Control & Core Utilities ───────────────────────────────────────── -category "Version Control & Core Utilities" "git gh jq tree wget" - -if want "git — version control"; then formula git; fi -if want "gh — GitHub CLI"; then formula gh; fi -if want "jq — JSON processor"; then formula jq; fi -if want "tree — directory tree"; then formula tree; fi -if want "wget — HTTP downloader"; then formula wget; fi - -# ── Modern CLI Tools ────────────────────────────────────────────────────────── -category "Modern CLI Tools" \ - "rg fd bat eza zoxide fzf delta lazygit btop dust tldr atuin tmux neovim" - -if want "ripgrep (rg) — fast grep, respects .gitignore"; then formula ripgrep; fi -if want "fd — intuitive find replacement"; then formula fd; fi -if want "bat — cat with syntax highlighting"; then formula bat; fi -if want "eza — modern ls with icons and git status"; then formula eza; fi -if want "zoxide (z) — smarter cd"; then formula zoxide; fi -if want "fzf — fuzzy finder (history, files, dirs)"; then formula fzf; DID_FZF=true; fi -if want "git-delta — syntax-highlighted git diffs"; then formula git-delta; fi -if want "lazygit (lg) — full TUI for git"; then formula lazygit; fi -if want "btop — modern resource monitor"; then formula btop; fi -if want "dust — tree-based disk usage"; then formula dust; fi -if want "tldr — simplified man pages"; then formula tldr; fi -if want "atuin — SQLite-backed shell history"; then formula atuin; fi -if want "tmux — terminal multiplexer"; then formula tmux; fi -if want "neovim — modal text editor"; then formula neovim; fi - -# ── Shell Productivity ──────────────────────────────────────────────────────── -category "Shell Productivity" "starship antidote" - -if want "starship — cross-shell prompt (replaces Powerlevel10k)"; then formula starship; fi -if want "antidote — Zsh plugin manager"; then formula antidote; fi - -# ── Language Runtimes ───────────────────────────────────────────────────────── -category "Language Runtimes (via asdf)" \ - "Node.js Python Go Java (versions from .tool-versions)" - -if [[ "$CAT_MODE" != skip ]]; then - DID_ASDF=true - # Build deps are prerequisites for asdf plugins — always included with runtimes. - info "Installing asdf and build prerequisites …" - formula asdf - formula coreutils - formula openssl@3 - formula readline - formula xz - done_ "asdf + build prerequisites" - - if want "Node.js"; then ASDF_LANGS+=(nodejs); fi - if want "Python"; then ASDF_LANGS+=(python); fi - if want "Go"; then ASDF_LANGS+=(golang); fi - if want "Java"; then ASDF_LANGS+=(java); fi -fi - -# ── Containers ──────────────────────────────────────────────────────────────── -category "Containers" "Docker Desktop — provides docker + docker compose" - -if want "Docker Desktop"; then cask_pkg docker-desktop; fi - -# ── Cloud Tooling ───────────────────────────────────────────────────────────── -category "Cloud Tooling" "Google Cloud CLI — gcloud, gsutil, bq" - -if want "Google Cloud CLI (gcloud, gsutil, bq)"; then cask_pkg gcloud-cli; fi - -# ── Editors & Terminal ──────────────────────────────────────────────────────── -category "Editors & Terminal" "Ghostty WezTerm VS Code Cursor" - -if want "Ghostty — GPU-accelerated terminal"; then cask_pkg ghostty; fi -if want "WezTerm — GPU-accelerated terminal"; then cask_pkg wezterm; fi -if want "Visual Studio Code"; then cask_pkg visual-studio-code; fi -if want "Cursor — AI-native code editor"; then cask_pkg cursor; fi - -# ── AI Coding Tools ─────────────────────────────────────────────────────────── -category "AI Coding Tools" "Claude Code Claude desktop Codex CLI Codex desktop app opencode" - -if want "Claude Code — AI CLI (native installer)"; then DID_CLAUDE=true; fi -if want "Claude — Anthropic desktop app"; then cask_pkg claude; fi -if want "Codex CLI — OpenAI coding agent in terminal"; then cask_pkg codex; fi -if want "Codex desktop app — manages coding agents"; then cask_pkg codex-app; fi -if want "opencode — AI coding agent in terminal"; then formula opencode; fi - -# ── Browser ─────────────────────────────────────────────────────────────────── -category "Browser" "Google Chrome Firefox ChatGPT Atlas" - -if want "Google Chrome"; then cask_pkg google-chrome; fi -if want "Firefox"; then cask_pkg firefox; fi -if want "ChatGPT Atlas"; then cask_pkg chatgpt-atlas; fi - -# ── Productivity Apps ───────────────────────────────────────────────────────── -category "Productivity Apps" \ - "Rectangle AppCleaner Maccy LinearMouse OpenSuperWhisper Granola Postman Whimsical" - -if want "Rectangle — keyboard-driven window tiling"; then cask_pkg rectangle; fi -if want "AppCleaner — clean app uninstalls"; then cask_pkg appcleaner; fi -if want "Maccy — clipboard history (Cmd+Shift+C)"; then cask_pkg maccy; fi -if want "LinearMouse — mouse customization"; then cask_pkg linearmouse; fi -if want "OpenSuperWhisper — open-source AI voice-to-text dictation"; then cask_pkg opensuperwhisper; fi -if want "Obsidian — markdown knowledge base"; then cask_pkg obsidian; fi -if want "Granola — AI-powered notepad for meetings"; then cask_pkg granola; fi -if want "Postman — REST client & API testing"; then cask_pkg postman; fi -if want "Whimsical — collaboration & diagramming"; then cask_pkg whimsical; fi - -# ── Developer Fonts ─────────────────────────────────────────────────────────── -category "Developer Fonts" \ - "Nerd Fonts — required by Starship, eza icons, and TUI tools" - -if want "JetBrains Mono Nerd Font (primary, default in Ghostty)"; then - cask_pkg font-jetbrains-mono-nerd-font -fi -if want "Fira Code Nerd Font (alternative with ligatures)"; then - cask_pkg font-fira-code-nerd-font -fi - -# ── Dotfile symlinks (always) ───────────────────────────────────────────────── -section "Dotfile Symlinks" -info "Linking dotfiles into \$HOME …" - -ln -sf "$DOTFILES_DIR/.zshrc" "$HOME/.zshrc" -ln -sf "$DOTFILES_DIR/.zsh_plugins.txt" "$HOME/.zsh_plugins.txt" -ln -sf "$DOTFILES_DIR/.tool-versions" "$HOME/.tool-versions" - -mkdir -p "$HOME/.config/ghostty" -ln -sf "$DOTFILES_DIR/configs/ghostty-config" "$HOME/.config/ghostty/config" -ln -sf "$DOTFILES_DIR/configs/starship.toml" "$HOME/.config/starship.toml" - -mkdir -p "$HOME/.config/wezterm" -ln -sf "$DOTFILES_DIR/configs/wezterm.lua" "$HOME/.config/wezterm/wezterm.lua" - -# LinearMouse: back up any pre-existing real file before replacing with symlink. -mkdir -p "$HOME/.config/linearmouse" -LINEARMOUSE_TARGET="$HOME/.config/linearmouse/linearmouse.json" -if [[ -f "$LINEARMOUSE_TARGET" && ! -L "$LINEARMOUSE_TARGET" ]]; then - BACKUP="$LINEARMOUSE_TARGET.backup.$(date +%Y%m%d-%H%M%S)" - info "Existing LinearMouse config found; backing up to $(basename "$BACKUP")" - mv "$LINEARMOUSE_TARGET" "$BACKUP" -fi -ln -sf "$DOTFILES_DIR/configs/linearmouse.json" "$LINEARMOUSE_TARGET" - -mkdir -p "$HOME/.claude" -ln -sf "$DOTFILES_DIR/claude/CLAUDE.md" "$HOME/.claude/CLAUDE.md" -ln -sf "$DOTFILES_DIR/claude/settings.json" "$HOME/.claude/settings.json" -ln -sf "$DOTFILES_DIR/claude/statusline-command.sh" "$HOME/.claude/statusline-command.sh" - -mkdir -p "$HOME/.codex" -ln -sf "$DOTFILES_DIR/codex-instructions.md" "$HOME/.codex/instructions.md" - -mkdir -p "$HOME/.cursor/rules" -ln -sf "$DOTFILES_DIR/cursor-rules/git-commits.mdc" "$HOME/.cursor/rules/git-commits.mdc" - -# Wire delta + merge config via git include (keeps personal user info separate). -# Guard against duplicates on re-runs — git config --add would append every time. -if ! git config --global --get-all include.path | grep -qF "$DOTFILES_DIR/configs/gitconfig"; then - git config --global --add include.path "$DOTFILES_DIR/configs/gitconfig" -fi - -done_ "Dotfiles linked" - -# ── Git Configuration ───────────────────────────────────────────────────────── -section "Git Configuration" - -# Default branch is always set — not personal, main is the universal default. -_branch=main -if ! $FULL; then - _cur_branch=$(git config --global init.defaultBranch 2>/dev/null || echo "main") - printf " Default branch [%s]: " "$_cur_branch" - read -r _branch /dev/null || echo "") -_cur_email=$(git config --global user.email 2>/dev/null || echo "") - -if ! $FULL; then - printf " Name" - [[ -n "$_cur_name" ]] && printf " [%s]" "$_cur_name" - printf ": " - read -r _name /dev/null || true - done_ "asdf plugin: $lang" - done - info "Installing versions from .tool-versions (may take a few minutes) …" - asdf install - done_ "Language runtimes installed" -fi - -# ── Claude Code ─────────────────────────────────────────────────────────────── -if $DID_CLAUDE; then - section "Claude Code" - if ! command -v claude >/dev/null 2>&1; then - info "Installing Claude Code via native installer …" - curl -fsSL https://claude.ai/install.sh | bash - fi - done_ "Claude Code ready" -fi - -# ── Shell check ─────────────────────────────────────────────────────────────── -if [[ "$SHELL" != *"zsh"* ]]; then - warn "Your login shell is $SHELL. Run: chsh -s $(which zsh)" -fi - -# ── Done ────────────────────────────────────────────────────────────────────── -printf "\n${W}${G}" -printf "╔══════════════════════════════════════════════╗\n" -printf "║ Setup complete! ✓ ║\n" -printf "╚══════════════════════════════════════════════╝\n" -printf "${R}\n" - -info "Next steps (in order):" -echo -echo " 1. Verify everything installed correctly:" -echo " ./verify.sh" -echo -echo " 2. Reload your shell so new PATH and aliases take effect:" -echo " exec zsh" -echo -echo " 3. Authenticate GitHub CLI:" -echo " gh auth login" -echo -echo " 4. Grant Accessibility permission to Maccy, Rectangle, and LinearMouse:" -echo " System Settings > Privacy & Security > Accessibility" -echo -echo " 5. Launch Docker Desktop once to complete its install:" -echo " open -a Docker" -echo -echo " 6. Sign into GUI apps (Chrome, Cursor, VS Code)" -echo -echo " 7. Authenticate Claude Code:" -echo " claude" -echo -echo " 8. (Optional) Enable Atuin shell history sync:" -echo " atuin register -u -e " -echo -echo " 9. (Optional) Authenticate cloud CLIs:" -echo " gcloud auth login" -echo -echo " See README.md 'Manual Steps' section for full details." diff --git a/update.sh b/update.sh index acaaac2..ed41aff 100755 --- a/update.sh +++ b/update.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# update.sh — Update all package managers and tools. +# update.sh — Update everything this repo manages. # # Usage: # ./update.sh @@ -8,6 +8,8 @@ set -uo pipefail +DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # ── Colors ──────────────────────────────────────────────────────────────────── if [[ -t 1 ]]; then G=$'\033[0;32m' Y=$'\033[0;33m' B=$'\033[0;34m' @@ -32,22 +34,33 @@ run() { fi } +# ── Nix flake inputs ────────────────────────────────────────────────────────── +# Bumps the pins in flake.lock (nixpkgs, nix-darwin, home-manager, +# nix-homebrew) to their latest revisions on the tracked branches. This does +# NOT apply anything by itself — run ./rebuild.sh afterward. +section "Nix flake inputs" +if command -v nix >/dev/null 2>&1; then + run "Update flake.lock" nix flake update --flake "$DOTFILES_DIR" + warn "flake.lock updated — run ./rebuild.sh to apply, then commit the lock file change." +else + warn "nix not found — skipping (run ./bootstrap.sh first)" +fi + # ── Homebrew ────────────────────────────────────────────────────────────────── +# What's INSTALLED is declared by configuration.nix's `homebrew` block — this +# doesn't change that. It just upgrades those already-declared packages to +# their latest available version right now, same as `onActivation.autoUpdate +# = true` already does on every ./rebuild.sh — this is just a faster path +# when you want it done immediately without a full switch. section "Homebrew" run "Update formulae metadata" brew update run "Upgrade formulae" brew upgrade run "Upgrade casks" brew upgrade --cask run "Cleanup old versions" brew cleanup -# ── Zsh plugins (antidote) ──────────────────────────────────────────────────── -section "Zsh plugins (antidote)" -if command -v antidote >/dev/null 2>&1; then - run "Update plugins" antidote update -else - warn "antidote not found — skipping" -fi - # ── asdf plugins ───────────────────────────────────────────────────────────── +# Runtimes stay outside Nix's view by design (per-project .tool-versions +# overrides don't fit Nix's model) — this genuinely still needs a manual step. section "asdf plugins" if command -v asdf >/dev/null 2>&1; then run "Update all asdf plugins" asdf plugin update --all @@ -55,13 +68,6 @@ else warn "asdf not found — skipping" fi -# ── Claude Code CLI ─────────────────────────────────────────────────────────── -section "Claude Code" -if command -v claude >/dev/null 2>&1; then - run "Update Claude Code" claude update -else - warn "claude not found — skipping" -fi - # ── Summary ─────────────────────────────────────────────────────────────────── printf "\n${G}${W}Done.${R}\n" +printf "If flake.lock changed above, run ${W}./rebuild.sh${R} to apply it.\n" diff --git a/verify.sh b/verify.sh old mode 100644 new mode 100755 index b944610..454c62a --- a/verify.sh +++ b/verify.sh @@ -1,8 +1,12 @@ #!/usr/bin/env bash -# verify.sh: End-to-end smoke test of the macOS developer setup. +# verify.sh: Smoke test of the macOS developer setup. # -# Runs non-destructive checks against every component installed by setup.sh: -# binaries, symlinks (existence + target), config file content, GUI apps, fonts. +# Package/dotfile declarations themselves are validated structurally by +# `nix flake check --no-build` (run as part of this script) — that's the +# primary contract now, not this file. What's left here is what Nix can't +# express: is everything actually wired up on THIS machine right now, GUI +# app presence/signed-in state, and the handful of things bootstrap.sh still +# manages directly (git identity, asdf runtimes). # # Exit code 0 if all critical checks pass; 1 if any fail (with summary). # @@ -37,23 +41,35 @@ check_command() { fi } -# Verify a symlink exists AND points into the dotfiles repo. -check_symlink() { +# Verify a path resolves (through any number of symlink hops, e.g. the +# mkOutOfStoreSymlink -> nix-store -> repo chain home-manager creates) back +# into this repo. Used for "edit-in-place" files whose real content lives here. +check_resolves_to_repo() { local path="$1" label="${2:-$path}" - if [[ -L "$path" ]]; then - local target; target=$(readlink "$path") - if [[ "$target" == "$DOTFILES_DIR"* ]]; then - pass "$label → $target" + if [[ -e "$path" ]]; then + local resolved; resolved=$(realpath "$path" 2>/dev/null || echo "") + if [[ "$resolved" == "$DOTFILES_DIR"* ]]; then + pass "$label → $resolved" else - warn_m "$label is a symlink but points outside dotfiles repo: $target" + warn_m "$label exists but resolves outside dotfiles repo: $resolved" fi - elif [[ -e "$path" ]]; then - warn_m "$label exists but is not a symlink (expected symlink into dotfiles repo)" else fail "$label missing" fi } +# Verify a home-manager-*generated* file exists (its real content is a +# Nix-store-managed file, not something in this repo, so we just check it's +# there rather than expecting it to resolve back into the repo). +check_nonempty() { + local path="$1" label="${2:-$path}" + if [[ -s "$path" ]]; then + pass "$label exists and is non-empty" + else + fail "$label missing or empty" + fi +} + # Verify a config file contains an expected pattern. check_contains() { local file="$1" pattern="$2" label="$3" @@ -81,8 +97,22 @@ check_json() { echo printf "\033[1;36m══ macOS Developer Setup: Verification ══\033[0m\n" -# ── 1. Homebrew ─────────────────────────────────────────────────────────────── -info "1. Homebrew" +# ── 1. Nix / nix-darwin / Homebrew ──────────────────────────────────────────── +info "1. Nix / nix-darwin / Homebrew" +check_command nix "Nix" +if command -v darwin-rebuild >/dev/null 2>&1 || [[ -x /run/current-system/sw/bin/darwin-rebuild ]]; then + pass "darwin-rebuild available" +else + fail "darwin-rebuild not found — run ./bootstrap.sh" +fi +if command -v nix >/dev/null 2>&1; then + info "Validating flake (nix flake check --no-build) …" + if (cd "$DOTFILES_DIR" && nix flake check --no-build >/dev/null 2>&1); then + pass "flake.nix / configuration.nix / home.nix evaluate cleanly" + else + fail "flake evaluation failed — run: nix flake check --no-build" + fi +fi check_command brew "Homebrew" if command -v brew >/dev/null 2>&1; then prefix="$(brew --prefix)" @@ -92,19 +122,6 @@ if command -v brew >/dev/null 2>&1; then warn_m "Homebrew prefix: $prefix (expected /opt/homebrew on Apple Silicon)" fi fi -if grep -q "brew shellenv" "$HOME/.zprofile" 2>/dev/null; then - pass "Homebrew PATH persisted in ~/.zprofile" -else - fail "Homebrew PATH missing from ~/.zprofile (login shells won't find brew)" -fi -# Verify Brewfile is fully satisfied. -if command -v brew >/dev/null 2>&1; then - if brew bundle check --file="$DOTFILES_DIR/Brewfile" --quiet 2>/dev/null; then - pass "All Brewfile packages installed" - else - warn_m "Some Brewfile packages not installed — run: brew bundle" - fi -fi # ── 2. Language Runtimes (asdf) ─────────────────────────────────────────────── info "2. Language Runtimes (asdf)" @@ -122,10 +139,9 @@ if command -v asdf >/dev/null 2>&1; then pass "asdf $lang: $ver" fi else - fail "asdf $lang not configured (run: asdf plugin add $lang && asdf install)" + fail "asdf $lang not configured (run: ./bootstrap.sh)" fi done - # Verify shim binaries resolve. for bin in node python go java; do check_command "$bin" "$bin shim" done @@ -139,23 +155,14 @@ else warn_m "Login shell is $SHELL (expected zsh) — run: chsh -s $(which zsh 2>/dev/null || echo zsh)" fi check_command starship "Starship" -if [[ -f "/opt/homebrew/opt/antidote/share/antidote/antidote.zsh" ]]; then - pass "Antidote plugin manager installed" -else - fail "Antidote not found at expected path" -fi - -# .zshrc initializations -check_contains "$HOME/.zshrc" "antidote" ".zshrc sources antidote" -check_contains "$HOME/.zshrc" "starship init" ".zshrc initializes Starship" -check_contains "$HOME/.zshrc" "asdf" ".zshrc initializes asdf" -check_contains "$HOME/.zshrc" "zoxide init" ".zshrc initializes zoxide" -check_contains "$HOME/.zshrc" "atuin init" ".zshrc initializes atuin" -# .zsh_plugins.txt — all three plugins must be present -check_contains "$HOME/.zsh_plugins.txt" "zsh-autosuggestions" ".zsh_plugins.txt: zsh-autosuggestions" -check_contains "$HOME/.zsh_plugins.txt" "zsh-completions" ".zsh_plugins.txt: zsh-completions" -check_contains "$HOME/.zsh_plugins.txt" "zsh-syntax-highlighting" ".zsh_plugins.txt: zsh-syntax-highlighting" +# Native home-manager zsh plugins — antidote was retired in favor of these. +check_contains "$HOME/.zshrc" "zsh-autosuggestions" ".zshrc: autosuggestion enabled" +check_contains "$HOME/.zshrc" "zsh-syntax-highlighting" ".zshrc: syntax-highlighting enabled" +check_contains "$HOME/.zshrc" "starship init" ".zshrc: initializes Starship" +check_contains "$HOME/.zshrc" "asdf" ".zshrc: initializes asdf shims" +check_contains "$HOME/.zshrc" "zoxide" ".zshrc: initializes zoxide" +check_contains "$HOME/.zshrc" "atuin" ".zshrc: initializes atuin" # ── 4. Modern CLI Tools ─────────────────────────────────────────────────────── info "4. Modern CLI Tools" @@ -165,10 +172,11 @@ done # ── 4b. AI Coding CLIs ──────────────────────────────────────────────────────── info "4b. AI Coding CLIs" -check_command codex "codex (OpenAI Codex CLI)" +check_command claude "claude (Claude Code CLI)" +check_command codex "codex (OpenAI Codex CLI)" check_command opencode "opencode (AI coding agent)" -# ── 5. Git & GitHub CLI ─────────────────────────────────────────────────────── +# ── 5. Git & GitHub ─────────────────────────────────────────────────────────── info "5. Git & GitHub" check_command git "git" check_command gh "GitHub CLI" @@ -176,8 +184,8 @@ if command -v git >/dev/null 2>&1; then name=$(git config --get user.name 2>/dev/null || echo "") email=$(git config --get user.email 2>/dev/null || echo "") branch=$(git config --get init.defaultBranch 2>/dev/null || echo "") - [[ -n "$name" ]] && pass "git user.name: $name" || fail "git user.name not set" - [[ -n "$email" ]] && pass "git user.email: $email" || fail "git user.email not set" + [[ -n "$name" ]] && pass "git user.name: $name" || fail "git user.name not set (run: ./bootstrap.sh)" + [[ -n "$email" ]] && pass "git user.email: $email" || fail "git user.email not set (run: ./bootstrap.sh)" [[ -n "$branch" ]] && pass "git init.defaultBranch: $branch" \ || warn_m "git init.defaultBranch not set (will default to 'master')" fi @@ -185,62 +193,44 @@ fi # ── 6. Cloud Tools ──────────────────────────────────────────────────────────── info "6. Cloud Tools" check_command gcloud "gcloud (Google Cloud CLI)" -check_command gh "gh (GitHub CLI)" - -# ── 7. Claude Code ──────────────────────────────────────────────────────────── -info "7. Claude Code" -if command -v claude >/dev/null 2>&1; then - pass "claude on PATH" -elif [[ -x "$HOME/.local/bin/claude" ]]; then - fail "claude at ~/.local/bin/claude but not on PATH (add ~/.local/bin to PATH in ~/.zshrc)" -else - fail "Claude Code not found (run: curl -fsSL https://claude.ai/install.sh | bash)" -fi +check_command terraform "terraform" +check_command stripe "stripe" + +# ── 7. Dotfile Wiring ───────────────────────────────────────────────────────── +info "7. Dotfile Wiring" + +# Edit-in-place: real content lives in this repo, home-manager just symlinks +# (via mkOutOfStoreSymlink) so editing here takes effect without a rebuild. +check_resolves_to_repo "$HOME/.config/wezterm/wezterm.lua" "~/.config/wezterm/wezterm.lua" +check_resolves_to_repo "$HOME/.config/ghostty/config" "~/.config/ghostty/config" +check_resolves_to_repo "$HOME/.config/nvim/init.lua" "~/.config/nvim/init.lua" +check_resolves_to_repo "$HOME/.config/linearmouse/linearmouse.json" "~/.config/linearmouse/linearmouse.json" +check_resolves_to_repo "$HOME/.tool-versions" "~/.tool-versions" +check_resolves_to_repo "$HOME/.claude/CLAUDE.md" "~/.claude/CLAUDE.md (-> home/AGENTS.md)" +check_resolves_to_repo "$HOME/.codex/AGENTS.md" "~/.codex/AGENTS.md" +check_resolves_to_repo "$HOME/.config/opencode/AGENTS.md" "~/.config/opencode/AGENTS.md" +check_resolves_to_repo "$HOME/.claude/settings.json" "~/.claude/settings.json" + +# home-manager-generated: real content is a Nix-store-managed file, not +# something in this repo — just check it exists. +check_nonempty "$HOME/.zshrc" "~/.zshrc (home-manager generated)" +check_nonempty "$HOME/.config/starship.toml" "~/.config/starship.toml (home-manager generated)" + +# ── 8. Config File Integrity ────────────────────────────────────────────────── +info "8. Config File Integrity" -# ── 8. Dotfile Symlinks ─────────────────────────────────────────────────────── -info "8. Dotfile Symlinks" -check_symlink "$HOME/.zshrc" "~/.zshrc" -check_symlink "$HOME/.zsh_plugins.txt" "~/.zsh_plugins.txt" -check_symlink "$HOME/.tool-versions" "~/.tool-versions" -check_symlink "$HOME/.config/ghostty/config" "~/.config/ghostty/config" -check_symlink "$HOME/.config/starship.toml" "~/.config/starship.toml" -check_symlink "$HOME/.config/linearmouse/linearmouse.json" "~/.config/linearmouse/linearmouse.json" -check_symlink "$HOME/.config/wezterm/wezterm.lua" "~/.config/wezterm/wezterm.lua" -check_symlink "$HOME/.claude/CLAUDE.md" "~/.claude/CLAUDE.md" -check_symlink "$HOME/.claude/settings.json" "~/.claude/settings.json" -check_symlink "$HOME/.claude/statusline-command.sh" "~/.claude/statusline-command.sh" -check_symlink "$HOME/.codex/instructions.md" "~/.codex/instructions.md" -check_symlink "$HOME/.cursor/rules/git-commits.mdc" "~/.cursor/rules/git-commits.mdc" - -# ── 9. Config File Integrity ────────────────────────────────────────────────── -info "9. Config File Integrity" - -# Ghostty GHOSTTY_CFG="$HOME/.config/ghostty/config" -check_contains "$GHOSTTY_CFG" "font-family" "ghostty-config: font-family set" +check_contains "$GHOSTTY_CFG" "font-family" "ghostty-config: font-family set" check_contains "$GHOSTTY_CFG" "term = xterm-256color" "ghostty-config: term=xterm-256color (SSH safety)" -check_contains "$GHOSTTY_CFG" "theme" "ghostty-config: theme set" - -# Starship -if [[ -f "$HOME/.config/starship.toml" ]]; then - if [[ -s "$HOME/.config/starship.toml" ]]; then - pass "starship.toml exists and is non-empty" - else - warn_m "starship.toml is empty" - fi -else - fail "starship.toml not found" -fi +check_contains "$GHOSTTY_CFG" "theme" "ghostty-config: theme set" -# LinearMouse — must be valid JSON check_json "$HOME/.config/linearmouse/linearmouse.json" "linearmouse.json" -# Delta git config — included from configs/gitconfig check_contains "$HOME/.gitconfig" "include" "~/.gitconfig: includes configs/gitconfig" check_contains "$DOTFILES_DIR/configs/gitconfig" "pager = delta" "configs/gitconfig: delta pager configured" -# ── 10. GUI Applications ────────────────────────────────────────────────────── -info "10. GUI Applications" +# ── 9. GUI Applications ─────────────────────────────────────────────────────── +info "9. GUI Applications" APPS=( "Ghostty" "WezTerm" @@ -270,28 +260,23 @@ for app in "${APPS[@]}"; do fi done -# ── 11. Fonts ───────────────────────────────────────────────────────────────── -info "11. Nerd Fonts" +# ── 10. Nerd Fonts ───────────────────────────────────────────────────────────── +info "10. Nerd Fonts" for font_pattern in "JetBrainsMono" "FiraCode"; do label="${font_pattern} Nerd Font" - if brew list --cask 2>/dev/null | grep -qi "$(echo "$font_pattern" | tr '[:upper:]' '[:lower:]')"; then - pass "$label (Homebrew cask)" - elif compgen -G "$HOME/Library/Fonts/${font_pattern}*" >/dev/null 2>&1 \ - || compgen -G "/Library/Fonts/${font_pattern}*" >/dev/null 2>&1; then - pass "$label (font directory)" + if compgen -G "$HOME/Library/Fonts/${font_pattern}*" >/dev/null 2>&1 \ + || compgen -G "/Library/Fonts/${font_pattern}*" >/dev/null 2>&1 \ + || compgen -G "/Library/Fonts/Nix Fonts/${font_pattern}*" >/dev/null 2>&1; then + pass "$label" else warn_m "$label not detected (icons may render as boxes)" fi done -# ── 12. fzf Shell Integration ───────────────────────────────────────────────── -info "12. fzf Shell Integration" -if [[ -f "$HOME/.fzf.zsh" ]]; then - pass "~/.fzf.zsh exists (key bindings installed)" -else - fail "~/.fzf.zsh missing — run: \$(brew --prefix)/opt/fzf/install --key-bindings --completion --no-update-rc" -fi -check_contains "$HOME/.zshrc" "fzf.zsh" ".zshrc sources fzf key bindings" +# ── 11. fzf Integration ──────────────────────────────────────────────────────── +info "11. fzf Integration" +check_command fzf "fzf" +check_contains "$HOME/.zshrc" "FZF_CTRL_T_OPTS" ".zshrc: fzf preview options configured" # ── Summary ─────────────────────────────────────────────────────────────────── printf "\n\033[1;36m━━ Summary\033[0m\n" From ea2c06c5193f2dae097cc366c4ef7d15ee45a833 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 02:48:58 +0400 Subject: [PATCH 05/45] cleanup setup after migration to nix based configurations --- .cursor/rules/dotfiles-knowledge-base.mdc | 126 ----------------- AGENTS.md | 158 +++++++++++++++++++++ CLAUDE.md | 159 +--------------------- home.nix | 19 ++- home/.claude/settings.json | 5 +- 5 files changed, 177 insertions(+), 290 deletions(-) delete mode 100644 .cursor/rules/dotfiles-knowledge-base.mdc create mode 100644 AGENTS.md mode change 100644 => 120000 CLAUDE.md diff --git a/.cursor/rules/dotfiles-knowledge-base.mdc b/.cursor/rules/dotfiles-knowledge-base.mdc deleted file mode 100644 index 289ca02..0000000 --- a/.cursor/rules/dotfiles-knowledge-base.mdc +++ /dev/null @@ -1,126 +0,0 @@ ---- -description: Standards for this dotfiles repo — sources of truth, no-duplication rule, and checklists for adding tools -alwaysApply: true ---- - -# Dotfiles Repo Standards - -## 1. Search the repo before anything else - -For any question about this Mac dev setup: - -- **`docs/inventory.md`** — what is installed and how (start here for "what's in the setup") -- **`README.md`** — orientation, quick start, links to cheat sheets -- **`docs/`** — full command references; see [`README.md` Cheat Sheets section](../README.md#cheat-sheets--references) for the full annotated index -- **`Brewfile`** — authoritative package list; **`.tool-versions`** — authoritative runtime versions; **`setup.sh`** / **`verify.sh`** — bootstrap and smoke tests -- Root configs (`ghostty-config`, `.zshrc`, `starship.toml`) — live configuration - -| Question type | Start here | -|---|---| -| What is installed / how | `docs/inventory.md` | -| How-to / CLI reference | `docs/*-cheatsheet.md` | -| Bootstrap / wiring | `setup.sh`, `verify.sh`, `Brewfile` | -| Live config / "what is set?" | Root dotfiles (`ghostty-config`, `.zshrc`, etc.) | - ---- - -## 2. Sources of truth — one file owns each type of data - -Never duplicate these. Every other place must link, not repeat. - -| Data | Authoritative file | Rule | -|------|--------------------|------| -| Installed packages (formulae + casks) | `Brewfile` | Every `brew install` entry lives here | -| Language runtime versions | `.tool-versions` | Version numbers never written in docs | -| Zsh plugin list | `.zsh_plugins.txt` | Plugin names never written in docs | -| Human-readable inventory | `docs/inventory.md` | What's installed, how, one row per item | -| Detailed command references | `docs/*-cheatsheet.md` | Full usage, flags, examples, gotchas | -| Orientation + links | `README.md` | Overview only — no content, only links | - ---- - -## 3. No duplicate content across docs - -**One source, everywhere else links.** - -- If a table, code block, or list already exists in one doc, all other docs link to it — never copy it. -- `README.md` is orientation only: it describes categories and links out. No tool lists, keybinding tables, command blocks, or version numbers. -- Version numbers appear only in `.tool-versions`. Docs name the language but never the version. -- Plugin names appear only in `.zsh_plugins.txt`. Docs reference that file rather than listing plugins inline. -- New content from research goes in the single most relevant `docs/*-cheatsheet.md` — not in README as well. - ---- - -## 4. Adding a new tool, app, or runtime — full checklist - -Work through every applicable item. Skipping any item leaves the repo inconsistent. - -### Adding a Homebrew formula - -- [ ] Add to `Brewfile` under the correct section comment -- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` with "Installed via: `brew install`" -- [ ] Add to the matching category block in `setup.sh` (`want "..."` + `formula ...`) -- [ ] Add to the CLI tools loop in `verify.sh` if it provides a binary - -### Adding a Homebrew cask (GUI app) - -- [ ] Add to `Brewfile` under the correct section comment -- [ ] Add a row to the GUI Applications table in `docs/inventory.md` with "Installed via: `brew install --cask`" -- [ ] Add to the matching category block in `setup.sh` (`want "..."` + `cask_pkg ...`) -- [ ] Add to the `APPS` array in `verify.sh` -- [ ] If the app writes back to a config file, symlink that file into the dotfiles repo (see `linearmouse.json` pattern in `setup.sh`) - -### Adding a language runtime (asdf) - -- [ ] Add to `.tool-versions` — version number goes here and nowhere else -- [ ] Add a row to the Language Runtimes table in `docs/inventory.md` (language name + key only, no version) -- [ ] Add `asdf plugin add ` to the asdf section of `setup.sh` -- [ ] Add the language to the runtime loop in `verify.sh` - -### Adding a Zsh plugin - -- [ ] Add to `.zsh_plugins.txt` — plugin entry goes here and nowhere else -- [ ] Add a row to the Zsh plugins table in `docs/inventory.md` -- [ ] Add a `check_contains` line for the plugin in `verify.sh` - -### Removing anything - -- [ ] Remove from `Brewfile` / `.tool-versions` / `.zsh_plugins.txt` -- [ ] Remove the corresponding row from `docs/inventory.md` -- [ ] Remove from `setup.sh` -- [ ] Remove from `verify.sh` - ---- - -## 5. README is orientation only - -`README.md` must never contain: - -- Lists of tool or app names → link to `docs/inventory.md` -- Keybinding tables → link to the relevant cheatsheet -- Command examples already in a cheatsheet → link to the cheatsheet -- Version numbers → link to `.tool-versions` -- Plugin names → link to `.zsh_plugins.txt` - -When editing README, ask: "does this content already exist in a cheatsheet or inventory?" If yes, replace with a link. - ---- - -## 6. verify.sh must stay in sync - -`verify.sh` is the contract that the setup is complete and correct. Keep it in sync: - -- New CLI binary → add to the `check_command` loop -- New GUI app → add to the `APPS` array -- New symlink in `setup.sh` → add a `check_symlink` call -- New config file → add `check_contains` or `check_json` assertions for its critical settings -- New Zsh plugin → add a `check_contains` line for `.zsh_plugins.txt` -- New `.zshrc` initialization → add a `check_contains` line for `.zshrc` - ---- - -## 7. If the repo does not contain the answer - -- Use current upstream or official docs (prefer release notes when versions matter). -- State clearly when the answer comes from outside this repo. -- If the answer is stable and reusable, add it to the most relevant `docs/*-cheatsheet.md`. Match existing tone: skimmable, commands first, gotchas where they matter. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b85b827 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,158 @@ +# Claude Code Rules for this Dotfiles Repo + +## 1. Always search this repo first + +Before answering any question about this Mac dev setup, shell tools, CLI tools, configs, or anything that could be documented here — **read the repo first**. + +### Where to look (in order) + +| Question type | Search here first | +|---|---| +| What is installed / how | `docs/inventory.md` | +| How-to / CLI reference | `docs/cheatsheets/` (see index below) | +| Bootstrap / wiring | `flake.nix`, `configuration.nix`, `home.nix`, `bootstrap.sh`, `rebuild.sh`, `verify.sh` | +| Live config / "what is set?" | `home/.config/{wezterm,ghostty,nvim,herdr,linearmouse}/`, `home.nix`'s `programs.starship`/`programs.zsh` blocks | +| Setup walkthrough / daily commands | `README.md` | + +### docs/ index + +See [`README.md` — Cheat Sheets & References](README.md#cheat-sheets--references) for the full annotated index. Key lookup: + +| Topic | File | +|-------|------| +| Installed software (what, how installed) | `docs/inventory.md` | +| Nix / nix-darwin / home-manager workflow | `docs/cheatsheets/nix-cheatsheet.md` | +| Homebrew (via nix-homebrew) | `docs/cheatsheets/homebrew-cheatsheet.md` | +| Language runtimes | `docs/cheatsheets/asdf-cheatsheet.md` | +| Terminal keybindings / config | `docs/cheatsheets/ghostty-cheatsheet.md` | +| Git workflows | `docs/cheatsheets/git-cheatsheet.md` | +| Git TUI | `docs/cheatsheets/lazygit-cheatsheet.md` | +| Modern CLI tools | `docs/cheatsheets/modern-cli-cheatsheet.md` | +| Claude Code CLI | `docs/cheatsheets/claude-code-cheatsheet.md` | +| Cursor IDE | `docs/cheatsheets/cursor-cli-cheatsheet.md` | +| Docker | `docs/cheatsheets/docker-cheatsheet.md` | +| Vim/Neovim keybindings | `docs/cheatsheets/vim-cheat-sheet.md` | + +### How to search + +Use `rg` (ripgrep) to find relevant content fast before doing a full file read: + +``` +rg "" /Users/lakshyadevsingh/dotfiles/docs/ -l +rg "" -n +``` + +--- + +## 2. Sources of truth — one file owns each type of data + +Never duplicate these. Every other place must link, not repeat. + +| Data | Authoritative file | What goes here | +|------|--------------------|----------------| +| GUI apps (casks), asdf itself + build deps, tapped tools (terraform, stripe) | `configuration.nix`'s `homebrew` block | Every `homebrew.brews` / `homebrew.casks` entry. Rule of thumb: `.app` bundles and things not sensibly packaged in nixpkgs stay here. | +| CLI tools available in nixpkgs | `home.nix`'s `home.packages` | Everything installable straight from nixpkgs (ripgrep, fd, bat, lazygit, etc.) | +| Zsh plugins, aliases, keybinds, Starship prompt | `home.nix`'s `programs.zsh` / `programs.starship` blocks | Antidote is retired — native home-manager zsh plugin toggles replace it. There is no more standalone plugin-list file. | +| Dotfile symlink targets | `home.nix`'s `home.file` / `mkOutOfStoreSymlink` entries | Every path under `~` that should point back into this repo | +| Language runtime versions | `.tool-versions` | Version numbers only — never written in docs. Runtimes stay asdf-managed by design (not Nix-managed) — see README's Design Decisions. | +| Human-readable inventory | `docs/inventory.md` | What's installed, how, one row per item | +| Detailed command references | `docs/cheatsheets/*-cheatsheet.md` | Full usage, flags, examples, gotchas | +| Orientation + links | `README.md` | Overview only — links to cheatsheets, no duplicated content | + +--- + +## 3. No duplicate content across docs + +**One source, everywhere else links.** + +- If a table, code block, or list already exists in one doc, all other docs must link to it — never copy it. +- `README.md` is orientation only: it describes categories and links out. It does not contain tool lists, keybinding tables, command blocks, or version numbers. +- Version numbers appear only in `.tool-versions`. Docs name the language (e.g. "Node.js") but never the version. +- Package lists appear only in `configuration.nix` (Homebrew) and `home.nix` (Nix packages). Docs say "see `configuration.nix`" / "see `home.nix`" rather than listing packages. +- When adding content from the web: place it in the single most relevant `docs/cheatsheets/*-cheatsheet.md`. Do not add it to README as well. + +--- + +## 4. Adding a new tool, app, or runtime — full checklist + +Work through every applicable item. Skipping any item leaves the repo inconsistent. + +### Adding a Homebrew cask (GUI app) + +- [ ] Add to `configuration.nix`'s `homebrew.casks` +- [ ] Add a row to the GUI Applications table in `docs/inventory.md` (include "Installed via: `brew install --cask`" or "configuration.nix's homebrew.casks") +- [ ] Add to the `APPS` array in `verify.sh` +- [ ] If the app writes back to a config file, add a `home.file` entry in `home.nix` using `config.lib.file.mkOutOfStoreSymlink` (NOT plain `home.file`, which symlinks from the read-only Nix store) plus a `home.activation` script that backs up any pre-existing real file before the symlink lands — see the `linearmouse.json` pattern in `home.nix` (`home.activation.backupLinearMouseConfig`) +- [ ] Run `./rebuild.sh` + +### Adding a Homebrew formula (only for things that must stay Homebrew — e.g. asdf, build-support libs, tapped tools) + +- [ ] Add to `configuration.nix`'s `homebrew.brews` (add a `homebrew.taps` entry too if it's from a third-party tap) +- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: `brew install`") +- [ ] Run `./rebuild.sh` + +### Adding a Nix-native CLI package (tools available in nixpkgs — the default choice for new CLI tools) + +- [ ] Check it exists in nixpkgs first (`nix search nixpkgs `), not `brew search` +- [ ] Add to `home.nix`'s `home.packages` +- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: Nix (home.packages)") +- [ ] Add to the `check_command` loop in `verify.sh` if it provides a binary +- [ ] Run `./rebuild.sh` + +### Adding a language runtime (asdf) + +- [ ] Add to `.tool-versions` (version number goes here and nowhere else) +- [ ] Add a row to the Language Runtimes table in `docs/inventory.md` (language name and `.tool-versions` key only — no version number) +- [ ] Run `./bootstrap.sh` (registers the asdf plugin and runs `asdf install` for anything new in `.tool-versions`) +- [ ] Confirm the language's runtime-version check loop in `verify.sh` covers it + +### Adding a zsh alias / keybind + +- [ ] Add to `home.nix`'s `programs.zsh.shellAliases` (aliases) or `programs.zsh.initContent` (keybinds, env vars, PATH changes) +- [ ] Run `./rebuild.sh` + +### Removing anything + +- [ ] Remove from `configuration.nix` / `home.nix` / `.tool-versions` +- [ ] Remove the corresponding row from `docs/inventory.md` +- [ ] Remove from `verify.sh` (`check_command` loop, `APPS` array, or `check_contains`/`check_resolves_to_repo` calls as applicable) +- [ ] Run `./rebuild.sh` — note `homebrew.onActivation.cleanup = "none"` means removed Homebrew packages are left installed but undeclared, not force-uninstalled; `brew uninstall` manually if you want it gone from disk too + +--- + +## 5. README is orientation only + +`README.md` must never contain: +- Lists of tool or app names (link to `docs/inventory.md`) +- Keybinding tables (link to `docs/cheatsheets/ghostty-cheatsheet.md` or `docs/cheatsheets/modern-cli-cheatsheet.md`) +- Command examples that are already in a cheatsheet (link to the cheatsheet) +- Version numbers (link to `.tool-versions`) +- Package lists (link to `configuration.nix` / `home.nix`) + +When editing README, ask: "does this content already exist in a cheatsheet or inventory?" If yes, replace with a link. + +--- + +## 6. verify.sh and nix flake check — two layers of contract + +`nix flake check --no-build` (and `nix build .#darwinConfigurations.mac.system --dry-run`) is the **primary** contract now for anything declared in `configuration.nix`/`home.nix` — if a package or symlink is declared wrong, the flake fails to evaluate or build before you ever get to `verify.sh`. `verify.sh` itself now runs `nix flake check` as its first check, then covers what Nix genuinely can't express on its own: + +- Whether things are *actually* wired up on THIS machine right now (not just declared correctly) +- GUI app presence and `/Applications` state (Nix can't verify sign-in state) +- asdf runtime versions matching `.tool-versions` (asdf is deliberately outside Nix's management) +- Git identity (deliberately outside home-manager's `programs.git`) +- LinearMouse JSON content integrity (a write-back file, worth sanity-checking regardless of installer mechanism) + +Keep `verify.sh` in sync: +- New CLI binary installed → add to the `check_command` loop +- New GUI app installed → add to the `APPS` array +- New symlink added in `home.nix` → add a `check_resolves_to_repo` call (for edit-in-place files) or `check_nonempty` (for home-manager-generated files like `.zshrc`/`starship.toml`) +- New config file added → add `check_contains` or `check_json` assertions for its critical settings + +--- + +## 7. If the repo does not have the answer + +1. **Say so explicitly.** +2. **Search the web** using the WebSearch tool. Prefer official docs and release notes over blog posts. +3. **Update the repo** if the answer is stable and reusable — place it in the most relevant `docs/cheatsheets/*-cheatsheet.md`. Match existing tone: skimmable, commands first, gotchas where they matter. Do this proactively in Agent sessions. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index b85b827..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,158 +0,0 @@ -# Claude Code Rules for this Dotfiles Repo - -## 1. Always search this repo first - -Before answering any question about this Mac dev setup, shell tools, CLI tools, configs, or anything that could be documented here — **read the repo first**. - -### Where to look (in order) - -| Question type | Search here first | -|---|---| -| What is installed / how | `docs/inventory.md` | -| How-to / CLI reference | `docs/cheatsheets/` (see index below) | -| Bootstrap / wiring | `flake.nix`, `configuration.nix`, `home.nix`, `bootstrap.sh`, `rebuild.sh`, `verify.sh` | -| Live config / "what is set?" | `home/.config/{wezterm,ghostty,nvim,herdr,linearmouse}/`, `home.nix`'s `programs.starship`/`programs.zsh` blocks | -| Setup walkthrough / daily commands | `README.md` | - -### docs/ index - -See [`README.md` — Cheat Sheets & References](README.md#cheat-sheets--references) for the full annotated index. Key lookup: - -| Topic | File | -|-------|------| -| Installed software (what, how installed) | `docs/inventory.md` | -| Nix / nix-darwin / home-manager workflow | `docs/cheatsheets/nix-cheatsheet.md` | -| Homebrew (via nix-homebrew) | `docs/cheatsheets/homebrew-cheatsheet.md` | -| Language runtimes | `docs/cheatsheets/asdf-cheatsheet.md` | -| Terminal keybindings / config | `docs/cheatsheets/ghostty-cheatsheet.md` | -| Git workflows | `docs/cheatsheets/git-cheatsheet.md` | -| Git TUI | `docs/cheatsheets/lazygit-cheatsheet.md` | -| Modern CLI tools | `docs/cheatsheets/modern-cli-cheatsheet.md` | -| Claude Code CLI | `docs/cheatsheets/claude-code-cheatsheet.md` | -| Cursor IDE | `docs/cheatsheets/cursor-cli-cheatsheet.md` | -| Docker | `docs/cheatsheets/docker-cheatsheet.md` | -| Vim/Neovim keybindings | `docs/cheatsheets/vim-cheat-sheet.md` | - -### How to search - -Use `rg` (ripgrep) to find relevant content fast before doing a full file read: - -``` -rg "" /Users/lakshyadevsingh/dotfiles/docs/ -l -rg "" -n -``` - ---- - -## 2. Sources of truth — one file owns each type of data - -Never duplicate these. Every other place must link, not repeat. - -| Data | Authoritative file | What goes here | -|------|--------------------|----------------| -| GUI apps (casks), asdf itself + build deps, tapped tools (terraform, stripe) | `configuration.nix`'s `homebrew` block | Every `homebrew.brews` / `homebrew.casks` entry. Rule of thumb: `.app` bundles and things not sensibly packaged in nixpkgs stay here. | -| CLI tools available in nixpkgs | `home.nix`'s `home.packages` | Everything installable straight from nixpkgs (ripgrep, fd, bat, lazygit, etc.) | -| Zsh plugins, aliases, keybinds, Starship prompt | `home.nix`'s `programs.zsh` / `programs.starship` blocks | Antidote is retired — native home-manager zsh plugin toggles replace it. There is no more standalone plugin-list file. | -| Dotfile symlink targets | `home.nix`'s `home.file` / `mkOutOfStoreSymlink` entries | Every path under `~` that should point back into this repo | -| Language runtime versions | `.tool-versions` | Version numbers only — never written in docs. Runtimes stay asdf-managed by design (not Nix-managed) — see README's Design Decisions. | -| Human-readable inventory | `docs/inventory.md` | What's installed, how, one row per item | -| Detailed command references | `docs/cheatsheets/*-cheatsheet.md` | Full usage, flags, examples, gotchas | -| Orientation + links | `README.md` | Overview only — links to cheatsheets, no duplicated content | - ---- - -## 3. No duplicate content across docs - -**One source, everywhere else links.** - -- If a table, code block, or list already exists in one doc, all other docs must link to it — never copy it. -- `README.md` is orientation only: it describes categories and links out. It does not contain tool lists, keybinding tables, command blocks, or version numbers. -- Version numbers appear only in `.tool-versions`. Docs name the language (e.g. "Node.js") but never the version. -- Package lists appear only in `configuration.nix` (Homebrew) and `home.nix` (Nix packages). Docs say "see `configuration.nix`" / "see `home.nix`" rather than listing packages. -- When adding content from the web: place it in the single most relevant `docs/cheatsheets/*-cheatsheet.md`. Do not add it to README as well. - ---- - -## 4. Adding a new tool, app, or runtime — full checklist - -Work through every applicable item. Skipping any item leaves the repo inconsistent. - -### Adding a Homebrew cask (GUI app) - -- [ ] Add to `configuration.nix`'s `homebrew.casks` -- [ ] Add a row to the GUI Applications table in `docs/inventory.md` (include "Installed via: `brew install --cask`" or "configuration.nix's homebrew.casks") -- [ ] Add to the `APPS` array in `verify.sh` -- [ ] If the app writes back to a config file, add a `home.file` entry in `home.nix` using `config.lib.file.mkOutOfStoreSymlink` (NOT plain `home.file`, which symlinks from the read-only Nix store) plus a `home.activation` script that backs up any pre-existing real file before the symlink lands — see the `linearmouse.json` pattern in `home.nix` (`home.activation.backupLinearMouseConfig`) -- [ ] Run `./rebuild.sh` - -### Adding a Homebrew formula (only for things that must stay Homebrew — e.g. asdf, build-support libs, tapped tools) - -- [ ] Add to `configuration.nix`'s `homebrew.brews` (add a `homebrew.taps` entry too if it's from a third-party tap) -- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: `brew install`") -- [ ] Run `./rebuild.sh` - -### Adding a Nix-native CLI package (tools available in nixpkgs — the default choice for new CLI tools) - -- [ ] Check it exists in nixpkgs first (`nix search nixpkgs `), not `brew search` -- [ ] Add to `home.nix`'s `home.packages` -- [ ] Add a row to the appropriate CLI tools section in `docs/inventory.md` (include "Installed via: Nix (home.packages)") -- [ ] Add to the `check_command` loop in `verify.sh` if it provides a binary -- [ ] Run `./rebuild.sh` - -### Adding a language runtime (asdf) - -- [ ] Add to `.tool-versions` (version number goes here and nowhere else) -- [ ] Add a row to the Language Runtimes table in `docs/inventory.md` (language name and `.tool-versions` key only — no version number) -- [ ] Run `./bootstrap.sh` (registers the asdf plugin and runs `asdf install` for anything new in `.tool-versions`) -- [ ] Confirm the language's runtime-version check loop in `verify.sh` covers it - -### Adding a zsh alias / keybind - -- [ ] Add to `home.nix`'s `programs.zsh.shellAliases` (aliases) or `programs.zsh.initContent` (keybinds, env vars, PATH changes) -- [ ] Run `./rebuild.sh` - -### Removing anything - -- [ ] Remove from `configuration.nix` / `home.nix` / `.tool-versions` -- [ ] Remove the corresponding row from `docs/inventory.md` -- [ ] Remove from `verify.sh` (`check_command` loop, `APPS` array, or `check_contains`/`check_resolves_to_repo` calls as applicable) -- [ ] Run `./rebuild.sh` — note `homebrew.onActivation.cleanup = "none"` means removed Homebrew packages are left installed but undeclared, not force-uninstalled; `brew uninstall` manually if you want it gone from disk too - ---- - -## 5. README is orientation only - -`README.md` must never contain: -- Lists of tool or app names (link to `docs/inventory.md`) -- Keybinding tables (link to `docs/cheatsheets/ghostty-cheatsheet.md` or `docs/cheatsheets/modern-cli-cheatsheet.md`) -- Command examples that are already in a cheatsheet (link to the cheatsheet) -- Version numbers (link to `.tool-versions`) -- Package lists (link to `configuration.nix` / `home.nix`) - -When editing README, ask: "does this content already exist in a cheatsheet or inventory?" If yes, replace with a link. - ---- - -## 6. verify.sh and nix flake check — two layers of contract - -`nix flake check --no-build` (and `nix build .#darwinConfigurations.mac.system --dry-run`) is the **primary** contract now for anything declared in `configuration.nix`/`home.nix` — if a package or symlink is declared wrong, the flake fails to evaluate or build before you ever get to `verify.sh`. `verify.sh` itself now runs `nix flake check` as its first check, then covers what Nix genuinely can't express on its own: - -- Whether things are *actually* wired up on THIS machine right now (not just declared correctly) -- GUI app presence and `/Applications` state (Nix can't verify sign-in state) -- asdf runtime versions matching `.tool-versions` (asdf is deliberately outside Nix's management) -- Git identity (deliberately outside home-manager's `programs.git`) -- LinearMouse JSON content integrity (a write-back file, worth sanity-checking regardless of installer mechanism) - -Keep `verify.sh` in sync: -- New CLI binary installed → add to the `check_command` loop -- New GUI app installed → add to the `APPS` array -- New symlink added in `home.nix` → add a `check_resolves_to_repo` call (for edit-in-place files) or `check_nonempty` (for home-manager-generated files like `.zshrc`/`starship.toml`) -- New config file added → add `check_contains` or `check_json` assertions for its critical settings - ---- - -## 7. If the repo does not have the answer - -1. **Say so explicitly.** -2. **Search the web** using the WebSearch tool. Prefer official docs and release notes over blog posts. -3. **Update the repo** if the answer is stable and reusable — place it in the most relevant `docs/cheatsheets/*-cheatsheet.md`. Match existing tone: skimmable, commands first, gotchas where they matter. Do this proactively in Agent sessions. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..b706712 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +/Users/lakshyadevsingh/github/dotfiles/AGENTS.md \ No newline at end of file diff --git a/home.nix b/home.nix index 0efad88..cd67c84 100644 --- a/home.nix +++ b/home.nix @@ -54,12 +54,19 @@ in # Modern CLI replacements ls = "eza --icons --group-directories-first"; ll = "eza -lah --git --icons"; - lt = "eza --tree --level=2 --icons"; - la = "ls -a"; + lt = "eza --tree --icons"; + lt2 = "lt --level=2"; + lt3 = "lt --level=3"; + lt4 = "lt --level=4"; + lt5 = "lt --level=5"; + lt6 = "lt --level=6"; + lsa = "ls -a"; lla = "ll -a"; cat = "bat --paging=never"; top = "btop"; du = "dust"; + vi = "nvim"; + vim = "vi"; # Git shortcuts g = "git"; @@ -67,6 +74,7 @@ in gd = "git diff"; gds = "git diff --staged"; gl = "git log --oneline --graph --decorate -20"; + gla = "git log"; lg = "lazygit"; add = "git add ."; push = "git push"; @@ -76,9 +84,12 @@ in reload = "exec zsh"; # High-agency agent shortcuts — opt-in, bypass permission prompts. - cc = "claude --dangerously-skip-permissions"; - co = "codex --full-auto"; + cc = "claude"; + ccc = "claude --dangerously-skip-permissions"; + co = "codex"; + coo = "codex --full-auto"; aa = "agent"; + aaa = "agent -f"; }; initContent = lib.mkMerge [ diff --git a/home/.claude/settings.json b/home/.claude/settings.json index e3ab775..e0a30f3 100644 --- a/home/.claude/settings.json +++ b/home/.claude/settings.json @@ -1,7 +1,8 @@ { - "theme": "dark-ansi", "statusLine": { "type": "command", "command": "input=$(cat); model=$(echo \"$input\" | jq -r '.model.display_name'); used=$(echo \"$input\" | jq -r '.context_window.used_percentage // empty'); if [ -n \"$used\" ]; then printf \"%s | ctx: %.0f%% used\" \"$model\" \"$used\"; else printf \"%s\" \"$model\"; fi" - } + }, + "theme": "dark-ansi", + "skipDangerousModePermissionPrompt": true } From b124c28b0c032fdd359b69438669e4928cbf0907 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:49:34 +0400 Subject: [PATCH 06/45] fix(nix): dedupe nerd fonts to nix-only, drop homebrew casks font-jetbrains-mono-nerd-font and font-fira-code-nerd-font were declared as Homebrew casks here while home.nix already installs the same fonts natively via nix (nerd-fonts.jetbrains-mono, nerd-fonts.fira-code). Drop the Homebrew duplicates so fonts have a single source of truth per CLAUDE.md section 2. Co-Authored-By: Claude Sonnet 5 --- configuration.nix | 3 --- 1 file changed, 3 deletions(-) diff --git a/configuration.nix b/configuration.nix index 6dc1003..bc4e71a 100644 --- a/configuration.nix +++ b/configuration.nix @@ -100,9 +100,6 @@ "granola" "postman" "whimsical" - # Developer fonts - "font-jetbrains-mono-nerd-font" - "font-fira-code-nerd-font" ]; }; } From 220d8e5c49322331cc6861d269d1ac12b7827059 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:49:47 +0400 Subject: [PATCH 07/45] fix(zsh): document lt alias depth-limit removal lt was changed to full-depth eza tree listing with lt2 carved out as the capped-at-2-levels variant (former lt default), but the intent was undocumented. Add a one-line comment so the split doesn't need to be reverse-engineered from history. Co-Authored-By: Claude Sonnet 5 --- home.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/home.nix b/home.nix index cd67c84..13e5290 100644 --- a/home.nix +++ b/home.nix @@ -54,6 +54,7 @@ in # Modern CLI replacements ls = "eza --icons --group-directories-first"; ll = "eza -lah --git --icons"; + # lt = full-depth tree listing; lt2 = capped at 2 levels (former lt default). lt = "eza --tree --icons"; lt2 = "lt --level=2"; lt3 = "lt --level=3"; From a2311e57d541efa7cf5de815678f1d6d93f76d52 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:50:16 +0400 Subject: [PATCH 08/45] fix(verify): add missing symlink checks for herdr and cheatsheets home.nix declares mkOutOfStoreSymlink entries for .config/herdr and Documents/workspace/my-matrix/a-utils/cheatsheets, but verify.sh had no corresponding checks, contrary to CLAUDE.md section 6 ("new symlink added in home.nix -> add a check_resolves_to_repo call"). herdr uses the existing check_resolves_to_repo (hard fail if missing), since ~/.config/herdr is guaranteed once home-manager applies. The cheatsheets symlink target lives under an external workspace (Documents/workspace/my-matrix) that isn't part of this repo and won't exist on every machine it's cloned onto, so hard failing there would produce a false-negative red X on an otherwise healthy setup. Added a new check_resolves_to_repo_optional helper that mirrors check_resolves_to_repo but downgrades "target missing" to a warn instead of a fail, and used it only for the cheatsheets check. Co-Authored-By: Claude Sonnet 5 --- verify.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/verify.sh b/verify.sh index 454c62a..55e1e4b 100755 --- a/verify.sh +++ b/verify.sh @@ -58,6 +58,24 @@ check_resolves_to_repo() { fi } +# Same as check_resolves_to_repo, but for symlink targets that depend on +# machine-specific state outside this repo (e.g. an external workspace +# checkout that isn't guaranteed to exist on every machine this repo is +# cloned onto). A missing target here is a warn, not a hard fail. +check_resolves_to_repo_optional() { + local path="$1" label="${2:-$path}" + if [[ -e "$path" ]]; then + local resolved; resolved=$(realpath "$path" 2>/dev/null || echo "") + if [[ "$resolved" == "$DOTFILES_DIR"* ]]; then + pass "$label → $resolved" + else + warn_m "$label exists but resolves outside dotfiles repo: $resolved" + fi + else + warn_m "$label missing (optional: depends on external workspace)" + fi +} + # Verify a home-manager-*generated* file exists (its real content is a # Nix-store-managed file, not something in this repo, so we just check it's # there rather than expecting it to resolve back into the repo). @@ -205,6 +223,8 @@ check_resolves_to_repo "$HOME/.config/wezterm/wezterm.lua" "~/.config/w check_resolves_to_repo "$HOME/.config/ghostty/config" "~/.config/ghostty/config" check_resolves_to_repo "$HOME/.config/nvim/init.lua" "~/.config/nvim/init.lua" check_resolves_to_repo "$HOME/.config/linearmouse/linearmouse.json" "~/.config/linearmouse/linearmouse.json" +check_resolves_to_repo "$HOME/.config/herdr/config.toml" "~/.config/herdr/config.toml" +check_resolves_to_repo_optional "$HOME/Documents/workspace/my-matrix/a-utils/cheatsheets" "~/Documents/workspace/my-matrix/a-utils/cheatsheets (optional: external workspace)" check_resolves_to_repo "$HOME/.tool-versions" "~/.tool-versions" check_resolves_to_repo "$HOME/.claude/CLAUDE.md" "~/.claude/CLAUDE.md (-> home/AGENTS.md)" check_resolves_to_repo "$HOME/.codex/AGENTS.md" "~/.codex/AGENTS.md" From ee0c01c259d19376d5a4da5694588596b3b148b7 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:50:53 +0400 Subject: [PATCH 09/45] chore(cleanup): remove dead root .zshrc and .zsh_plugins.txt from antidote era Both are confirmed dead residue from the pre-migration Homebrew + antidote setup. The real ~/.zshrc is now home-manager-generated from home.nix's programs.zsh block; antidote is retired. Neither file is referenced by bootstrap.sh, rebuild.sh, or home.nix, and docs/inventory.md already asserted both no longer exist. Co-Authored-By: Claude Sonnet 5 --- .zsh_plugins.txt | 17 --------- .zshrc | 91 ------------------------------------------------ 2 files changed, 108 deletions(-) delete mode 100644 .zsh_plugins.txt delete mode 100644 .zshrc diff --git a/.zsh_plugins.txt b/.zsh_plugins.txt deleted file mode 100644 index 1869e34..0000000 --- a/.zsh_plugins.txt +++ /dev/null @@ -1,17 +0,0 @@ -# ~/.zsh_plugins.txt -# Antidote plugin bundle. Kept intentionally minimal; each plugin earns its -# place by solving a specific problem. Add more as needed. -# -# Load order matters: zsh-syntax-highlighting MUST load last (per its docs). -# zsh-autosuggestions and zsh-completions are order-flexible but listed before. - -# Command autosuggestions based on history (press the right arrow to accept) -zsh-users/zsh-autosuggestions - -# Extra completion definitions for common tools (git, docker, kubectl, etc.) -zsh-users/zsh-completions - -# Syntax highlighting as you type (valid commands green, invalid red). -# IMPORTANT: must be the LAST plugin loaded, otherwise other plugins can -# unbind its widget hooks. See: https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md -zsh-users/zsh-syntax-highlighting diff --git a/.zshrc b/.zshrc deleted file mode 100644 index 23be9c8..0000000 --- a/.zshrc +++ /dev/null @@ -1,91 +0,0 @@ -# ~/.zshrc -# macOS developer environment. Wires together Homebrew, asdf, starship, antidote, and modern CLI tools. -# -# Load order matters here. Each section below explains why. - -# ---- 1. Homebrew PATH (Apple Silicon) ---- -# Must come first; most other tools live under /opt/homebrew. -eval "$(/opt/homebrew/bin/brew shellenv)" - -# ---- 2. asdf shims ---- -# Must come before any tool that might invoke node/python/go. Uses the Go-rewrite -# style (asdf 0.16+); the old `source asdf.sh` approach is deprecated. -export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" - -# ---- 2b. Claude Code (native installer puts binary here) ---- -export PATH="$HOME/.local/bin:$PATH" - -# ---- 3. Atuin (SQLite shell history) ---- -# MUST load before antidote, so that zsh-syntax-highlighting (loaded LAST by -# antidote) can hook atuin's rebound Ctrl+R widget. Reversing this order causes -# autosuggestions/syntax-highlighting to silently fail. -# Replaces Ctrl+R with a full-screen history search UI. -eval "$(atuin init zsh)" - -# ---- 4. Antidote plugin manager ---- -# Loads the static plugin bundle. Plugin list lives in ~/.zsh_plugins.txt. -# Plugin order inside that file matters: zsh-syntax-highlighting MUST be last. -source /opt/homebrew/opt/antidote/share/antidote/antidote.zsh -antidote load - -# ---- 5. Starship prompt ---- -# Must come after plugins so it renders on top of their setup. Config lives -# in ~/.config/starship.toml. -eval "$(starship init zsh)" - -# ---- 6. zoxide (smarter cd) ---- -# `z ` jumps to any directory you've visited. Kept as a separate command -# from `cd` on purpose: aliasing `cd` to `z` breaks scripts that expect POSIX cd behavior. -eval "$(zoxide init zsh)" - -# ---- 7. fzf (fuzzy finder) ---- -# Binds Ctrl+R (history), Ctrl+T (file picker), Alt+C (directory picker). -# Note: atuin (loaded earlier) also binds Ctrl+R. Whichever loads LAST wins; -# here fzf wins at binding, but atuin's UI is invoked by fzf's Ctrl+R hook. -# Requires `$(brew --prefix)/opt/fzf/install` to have been run once. -[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh - -# fzf preview integration. Uses bat for file contents, eza for directory trees. -export FZF_CTRL_T_OPTS="--preview 'bat -n --color=always --line-range :500 {}'" -export FZF_ALT_C_OPTS="--preview 'eza --tree --color=always {} | head -200'" - -# ---- 8. Modern CLI aliases ---- -# Drop-in replacements. The classics remain available under their original names. -alias ls='eza --icons --group-directories-first' -alias ll='eza -lah --git --icons' -alias lt='eza --tree --level=2 --icons' -alias cat='bat --paging=never' -alias top='btop' -alias du='dust' - -# Deliberately NOT aliased: -# - grep -> rg (rg has different flag semantics; breaks scripts/pipes) -# - find -> fd (same reason) -# - cd -> z (zoxide; breaks scripts assuming POSIX cd) -# Use `rg`, `fd`, and `z` as their own commands instead. - -# ---- 9. Navigation shortcuts ---- -alias ..='cd ..' -alias ...='cd ../..' -alias ....='cd ../../..' - -# ---- 10. Git shortcuts ---- -alias g='git' -alias gs='git status' -alias gd='git diff' -alias gds='git diff --staged' -alias gl='git log --oneline --graph --decorate -20' -alias lg='lazygit' - -# ---- 11. Reload helper ---- -alias reload='exec zsh' - -# ---- 12. Docker CLI completions ---- -# Markers kept verbatim so Docker Desktop detects them and skips re-appending on first launch. -# The following lines have been added by Docker Desktop to enable Docker CLI completions. -if [[ -d "$HOME/.docker/completions" ]]; then - fpath=($HOME/.docker/completions $fpath) -fi -autoload -Uz compinit -compinit -# End of Docker CLI completions From 91d49191149c3d06caa4be8255671e98cd9ddf76 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:51:23 +0400 Subject: [PATCH 10/45] docs(cheatsheets): fix stale antidote/.zshrc references post-migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit modern-cli-cheatsheet.md claimed configs live in configs/ or the root .zshrc — neither exists anymore post-migration (see prior commit). Configs now live under home/.config/* (edit-in-place symlinks) or are home-manager-generated from home.nix's programs.zsh/programs.starship blocks. homebrew-cheatsheet.md's example team Brewfile still listed brew "antidote", which is retired and could mislead a reader copying the example. Co-Authored-By: Claude Sonnet 5 --- docs/cheatsheets/homebrew-cheatsheet.md | 1 - docs/cheatsheets/modern-cli-cheatsheet.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/cheatsheets/homebrew-cheatsheet.md b/docs/cheatsheets/homebrew-cheatsheet.md index 2144213..99103ee 100644 --- a/docs/cheatsheets/homebrew-cheatsheet.md +++ b/docs/cheatsheets/homebrew-cheatsheet.md @@ -268,7 +268,6 @@ brew "git" brew "gh" brew "asdf" brew "starship" -brew "antidote" brew "fzf" brew "ripgrep" brew "zoxide" diff --git a/docs/cheatsheets/modern-cli-cheatsheet.md b/docs/cheatsheets/modern-cli-cheatsheet.md index 3c33826..b2fcbd0 100644 --- a/docs/cheatsheets/modern-cli-cheatsheet.md +++ b/docs/cheatsheets/modern-cli-cheatsheet.md @@ -716,4 +716,4 @@ Most tools in this cheat sheet respect these locations: | zoxide | None; controlled by env vars; database at `~/.local/share/zoxide/db.zo` | | fd | None; use shell aliases if you want different defaults | -All configs in this repo are either in `configs/` (`starship.toml`, `ghostty-config`, etc.), in the root (`.zshrc`), or commented into `.zshrc` itself for these tools. +All configs in this repo either live under `home/.config/*` (edit-in-place symlinks — see `home.nix`'s `home.file` entries) or are generated by home-manager from `home.nix`'s `programs.zsh` / `programs.starship` blocks. From fbeab4e68dce6a39bad04ef08cd8f67c0dbaed5f Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 03:53:42 +0400 Subject: [PATCH 11/45] refactor(git): move git config into home-manager, retire configs/ folder configs/gitconfig existed solely to carry delta/merge/pager config, wired into ~/.gitconfig via bootstrap.sh's include.path, because home-manager's programs.git was believed to make ~/.gitconfig an immutable Nix-store symlink. Verified against the exact home-manager revision pinned in flake.lock (af2beae5f0fae0a4310cc0e6aef2572f56090353, release-26.05): programs.git's generated config actually goes to the XDG path ~/.config/git/config, never touching ~/.gitconfig. That justification was stale. - home.nix: enable programs.git with the delta/merge settings migrated in declaratively (core.pager, interactive.diffFilter, delta.navigate/light/line-numbers/side-by-side, merge.conflictStyle). Replaced the stale "deliberately NOT enabled" comment with one explaining the real XDG mechanism. - bootstrap.sh: removed the now-unnecessary include.path wiring block (git already reads ~/.config/git/config alongside ~/.gitconfig with no explicit include needed). Updated nearby comments; identity prompting (default branch, user.name, user.email) is untouched. - Deleted configs/gitconfig and the now-empty configs/ directory, aligning with this repo's established pattern that everything home-related lives under home/ or is declared in home.nix, not in a stray top-level folder. - verify.sh: replaced the two checks tied to the old include.path mechanism with a single check that ~/.config/git/config exists and contains the delta pager config. - Updated README.md (layout table + repo tree), docs/inventory.md, docs/cheatsheets/modern-cli-cheatsheet.md, and AGENTS.md (the CLAUDE.md-symlinked source file) to describe the new flow: identity stays bootstrap.sh-driven via mutable ~/.gitconfig; pager/merge/ delta config is now nix-managed via home.nix, applied to the XDG path. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 2 +- README.md | 3 --- bootstrap.sh | 18 ++++++---------- configs/gitconfig | 20 ----------------- docs/cheatsheets/modern-cli-cheatsheet.md | 10 +++------ docs/inventory.md | 2 +- home.nix | 26 ++++++++++++++++++----- verify.sh | 3 +-- 8 files changed, 34 insertions(+), 50 deletions(-) delete mode 100644 configs/gitconfig diff --git a/AGENTS.md b/AGENTS.md index b85b827..7648105 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -140,7 +140,7 @@ When editing README, ask: "does this content already exist in a cheatsheet or in - Whether things are *actually* wired up on THIS machine right now (not just declared correctly) - GUI app presence and `/Applications` state (Nix can't verify sign-in state) - asdf runtime versions matching `.tool-versions` (asdf is deliberately outside Nix's management) -- Git identity (deliberately outside home-manager's `programs.git`) +- Git identity (still `bootstrap.sh`-driven, written directly to mutable `~/.gitconfig`; pager/merge/delta config is nix-managed via `programs.git` in `home.nix`, applied to `~/.config/git/config`) - LinearMouse JSON content integrity (a write-back file, worth sanity-checking regardless of installer mechanism) Keep `verify.sh` in sync: diff --git a/README.md b/README.md index 8b38ddc..23b8370 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,6 @@ This repo is meant to be forked and personalized. The files worth editing: | `home/.config/herdr/config.toml` | herdr (terminal agent multiplexer) keybindings. Edit-in-place, no rebuild needed. | | `home/.config/linearmouse/linearmouse.json` | Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. | | `home/AGENTS.md` | Shared agent instructions, symlinked into Claude Code, Codex, and opencode. | -| `configs/gitconfig` | Delta pager + git config, included via `~/.gitconfig`'s `include.path` (wired by `bootstrap.sh`, kept outside home-manager so personal identity stays mutable). | After any changes, commit them to your dotfiles repo. Other machines pick up changes with `git pull && ./rebuild.sh` (or `./bootstrap.sh` on a machine that hasn't been bootstrapped yet). @@ -464,8 +463,6 @@ dotfiles/ ├── home.nix # home-manager: CLI packages, zsh/starship, dotfile symlinks ├── Brewfile # RETIRED — pointer stub, superseded by configuration.nix ├── .tool-versions # asdf runtime versions (Node, Python, Go, Java) -├── configs/ -│ └── gitconfig # Delta pager + git config, included via ~/.gitconfig ├── home/ # edit-in-place source for home-manager's mkOutOfStoreSymlink files │ ├── AGENTS.md # shared agent instructions (Claude, Codex, opencode) │ ├── .claude/ diff --git a/bootstrap.sh b/bootstrap.sh index e5d824a..814006b 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -19,8 +19,9 @@ # 6. Registers asdf plugins and installs runtimes from .tool-versions # (per-project .tool-versions overrides don't fit Nix's model, so # runtimes stay asdf-managed by design). -# 7. Prompts for git identity (kept out of home-manager's programs.git so -# ~/.gitconfig stays mutable). +# 7. Prompts for git identity (still written directly to ~/.gitconfig — +# pager/merge/delta config is now nix-managed separately via +# ~/.config/git/config). # # After this, `darwin-rebuild` exists on PATH and you're on the normal # workflow: edit files, then run ./rebuild.sh. @@ -116,9 +117,10 @@ else fi # ── 7. Git Configuration ───────────────────────────────────────────────────── -# Deliberately not managed by home-manager's programs.git: that would make -# ~/.gitconfig an immutable Nix-store symlink, and personal identity below -# needs to stay easily settable per-machine. +# Identity stays here (not home-manager's programs.git) so it's easily +# settable per-machine and writes directly to mutable ~/.gitconfig. +# Pager/merge/delta config is nix-managed separately via home.nix's +# programs.git.settings, applied to ~/.config/git/config. section "Git Configuration" _branch=main @@ -154,12 +156,6 @@ else [[ -n "$_cur_email" ]] && done_ "git user.email = $_cur_email" || warn "git user.email not set — run: git config --global user.email \"you@example.com\"" fi -# Wire delta + merge config via git include (keeps personal user info separate). -if ! git config --global --get-all include.path | grep -qF "$DOTFILES_DIR/configs/gitconfig"; then - git config --global --add include.path "$DOTFILES_DIR/configs/gitconfig" -fi -done_ "git include.path wired to configs/gitconfig" - if [[ "$SHELL" != *"zsh"* ]]; then warn "Your login shell is $SHELL. Run: chsh -s $(which zsh)" fi diff --git a/configs/gitconfig b/configs/gitconfig deleted file mode 100644 index b2d3ac3..0000000 --- a/configs/gitconfig +++ /dev/null @@ -1,20 +0,0 @@ -# Shared git tool config — tracked in dotfiles, included via: -# git config --global include.path ~/dotfiles/gitconfig -# -# Personal identity (user.name, user.email) is intentionally absent; -# bootstrap.sh sets those directly via git config --global. - -[core] - pager = delta - -[interactive] - diffFilter = delta --color-only - -[delta] - navigate = true - light = false - line-numbers = true - side-by-side = true - -[merge] - conflictStyle = zdiff3 diff --git a/docs/cheatsheets/modern-cli-cheatsheet.md b/docs/cheatsheets/modern-cli-cheatsheet.md index b2fcbd0..34df363 100644 --- a/docs/cheatsheets/modern-cli-cheatsheet.md +++ b/docs/cheatsheets/modern-cli-cheatsheet.md @@ -462,15 +462,11 @@ Delta is a pager for git diffs. Once configured, every `git diff`, `git log -p`, ### Setup -Automated by `bootstrap.sh` — no manual steps needed. The config lives in `dotfiles/configs/gitconfig` and is wired into `~/.gitconfig` via `[include]`, leaving your personal identity settings untouched. This is deliberately kept outside home-manager's `programs.git` — enabling it would make `~/.gitconfig` an immutable Nix-store symlink, breaking `bootstrap.sh`'s git-identity prompt. +Declared in `home.nix`'s `programs.git.settings` (home-manager) and applied via `./rebuild.sh` — no manual steps needed. Personal identity (`user.name`/`user.email`/`init.defaultBranch`) stays separate: it's prompted for and written directly to `~/.gitconfig` by `bootstrap.sh`. The pager/merge config below is written by home-manager to `~/.config/git/config` (XDG path), which git reads and merges alongside `~/.gitconfig` automatically — no `include.path` wiring needed. -To apply manually on an existing machine: +To apply manually on an existing machine, add the equivalent block to `home.nix`'s `programs.git.settings` and run `./rebuild.sh`. -```bash -git config --global --add include.path ~/dotfiles/configs/gitconfig -``` - -Config applied (`dotfiles/configs/gitconfig`): +Config applied (generated at `~/.config/git/config`): ```ini [core] diff --git a/docs/inventory.md b/docs/inventory.md index f5a35d8..8c262d2 100644 --- a/docs/inventory.md +++ b/docs/inventory.md @@ -78,7 +78,7 @@ Installed via `home.nix`'s `home.packages` (Nix), unless noted otherwise. | `eza` | `ls` | Modern listing with icons, git status, tree view | | `zoxide` (`z`) | `cd` | Learns habits; jump to dirs by partial name — `home.nix`'s `programs.zoxide` (Nix, home-manager module, handles shell init) | | `fzf` | — | Fuzzy finder for history, files, branches, processes — `home.nix`'s `programs.fzf` (Nix, home-manager module, handles shell integration) | -| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; config in `configs/gitconfig`, wired via `[include]` in `~/.gitconfig` (nixpkgs package name is `delta`, not `git-delta`) | +| `delta` | `diff` pager | Syntax-highlighted, side-by-side git diffs; declared in `home.nix`'s `programs.git.settings` (home-manager, applied to `~/.config/git/config`) (nixpkgs package name is `delta`, not `git-delta`) | | `lazygit` (`lg`) | — | Full terminal UI for git | | `btop` | `top` / `htop` | Modern resource monitor with graphs | | `dust` | `du` | Tree-based disk usage visualizer | diff --git a/home.nix b/home.nix index 13e5290..1c001fd 100644 --- a/home.nix +++ b/home.nix @@ -186,11 +186,27 @@ in enableZshIntegration = true; }; - # Note: programs.git is deliberately NOT enabled here. It would make - # ~/.gitconfig a Nix-store-managed file, which breaks bootstrap.sh's - # `git config --global user.name/user.email` identity prompt (can't write - # to an immutable symlink). configs/gitconfig + the include.path wiring - # stay exactly as bootstrap.sh already sets them up, until that's revisited. + # programs.git writes its generated config to the XDG path + # ~/.config/git/config (confirmed for the home-manager rev pinned in + # flake.lock, release-26.05) — it never touches ~/.gitconfig. That means + # bootstrap.sh's interactive identity prompt (git config --global + # user.name/user.email, written directly to ~/.gitconfig) keeps working + # untouched, while delta/merge/pager config lives here declaratively + # instead of in a separate configs/gitconfig file. + programs.git = { + enable = true; + settings = { + core.pager = "delta"; + interactive.diffFilter = "delta --color-only"; + delta = { + navigate = true; + light = false; + "line-numbers" = true; + "side-by-side" = true; + }; + merge.conflictStyle = "zdiff3"; + }; + }; # Edit-in-place: the real files stay in this repo, ~/.config just points at them. home.file.".config/wezterm".source = diff --git a/verify.sh b/verify.sh index 55e1e4b..76f0a45 100755 --- a/verify.sh +++ b/verify.sh @@ -246,8 +246,7 @@ check_contains "$GHOSTTY_CFG" "theme" "ghostty-config: theme set check_json "$HOME/.config/linearmouse/linearmouse.json" "linearmouse.json" -check_contains "$HOME/.gitconfig" "include" "~/.gitconfig: includes configs/gitconfig" -check_contains "$DOTFILES_DIR/configs/gitconfig" "pager = delta" "configs/gitconfig: delta pager configured" +check_contains "$HOME/.config/git/config" "pager = delta" "~/.config/git/config: delta pager configured (home-manager)" # ── 9. GUI Applications ─────────────────────────────────────────────────────── info "9. GUI Applications" From 3385ec9fae1b5b453fa0b158d313ef61ac0dc335 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 04:01:35 +0400 Subject: [PATCH 12/45] fix(verify): match quoted value in generated git config check home-manager's nix ini renderer quotes string values, so the generated ~/.config/git/config has `pager = "delta"`, not `pager = delta`. Only caught by actually running verify.sh post-rebuild, not by nix flake check or a dry-run build. Co-Authored-By: Claude Sonnet 5 --- verify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verify.sh b/verify.sh index 76f0a45..52629e7 100755 --- a/verify.sh +++ b/verify.sh @@ -246,7 +246,7 @@ check_contains "$GHOSTTY_CFG" "theme" "ghostty-config: theme set check_json "$HOME/.config/linearmouse/linearmouse.json" "linearmouse.json" -check_contains "$HOME/.config/git/config" "pager = delta" "~/.config/git/config: delta pager configured (home-manager)" +check_contains "$HOME/.config/git/config" 'pager = "delta"' "~/.config/git/config: delta pager configured (home-manager)" # ── 9. GUI Applications ─────────────────────────────────────────────────────── info "9. GUI Applications" From 9cb66b48ce9b737d03eefff88b704fc7b46aa1f9 Mon Sep 17 00:00:00 2001 From: lakshyads05 Date: Sun, 12 Jul 2026 04:21:00 +0400 Subject: [PATCH 13/45] docs(cheatsheets): add herdr cheat sheet Documents herdr's session/workspace/tab/pane model, this repo's tmux-style keybindings, worktree integration, agent detection, and socket API for scripting multi-agent workflows. --- README.md | 1 + docs/cheatsheets/README.md | 1 + docs/cheatsheets/herdr-cheatsheet.md | 315 +++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 docs/cheatsheets/herdr-cheatsheet.md diff --git a/README.md b/README.md index 23b8370..a1c3c1f 100644 --- a/README.md +++ b/README.md @@ -440,6 +440,7 @@ Full command references for the tools that get the most daily use. These live in - **[Docker cheat sheet](docs/cheatsheets/docker-cheatsheet.md)**: images, containers, volumes, networks, Docker Compose, Dockerfile basics, disk cleanup, troubleshooting - **[Claude Code cheat sheet](docs/cheatsheets/claude-code-cheatsheet.md)**: CLI flags, slash commands, keyboard shortcuts, permission modes, CLAUDE.md, hooks, MCP, subagents, models and cost - **[Cursor CLI cheat sheet](docs/cheatsheets/cursor-cli-cheatsheet.md)**: agent modes (Agent/Plan/Ask), slash commands, cloud handoff, MCP integration, rules and skills, subagents +- **[Herdr cheat sheet](docs/cheatsheets/herdr-cheatsheet.md)**: session/workspace/tab/pane model, tmux-style keybindings, worktrees, agent integrations, socket API for scripting agents Each is written as a skimmable reference, not a tutorial. Use them when you need to look something up. diff --git a/docs/cheatsheets/README.md b/docs/cheatsheets/README.md index 6304006..128d667 100644 --- a/docs/cheatsheets/README.md +++ b/docs/cheatsheets/README.md @@ -34,6 +34,7 @@ Every cheat sheet here has frontmatter `tag:`/`related:` for Obsidian's tag pane - [[claude-code-cheatsheet]] — Claude Code CLI: flags, slash commands, hooks, MCP, subagents - [[cursor-cli-cheatsheet]] — Cursor CLI: Agent/Plan/Ask modes, Cloud Handoff, MCP, subagents +- [[herdr-cheatsheet]] — herdr agent multiplexer: sessions/workspaces/tabs/panes, tmux-style keybindings, socket API for scripting agents ## Containers diff --git a/docs/cheatsheets/herdr-cheatsheet.md b/docs/cheatsheets/herdr-cheatsheet.md new file mode 100644 index 0000000..ce3585d --- /dev/null +++ b/docs/cheatsheets/herdr-cheatsheet.md @@ -0,0 +1,315 @@ +--- +tag: + - type/cheatsheet + - topic/herdr + - topic/terminal +related: + - "[[ghostty-cheatsheet]]" + - "[[claude-code-cheatsheet]]" + - "[[cursor-cli-cheatsheet]]" + - "[[git-cheatsheet]]" +--- + +# Herdr Cheat Sheet + +A reference for herdr, the terminal workspace manager for AI coding agents used in this setup (tmux-style `Ctrl+B` prefix, config at `home/.config/herdr/config.toml`, edit-in-place — no rebuild needed). herdr runs a persistent background server; the terminal you attach to is a thin client, so agent sessions survive terminal restarts and can be detached/reattached like tmux. + +Official docs: + +--- + +## Table of Contents + +- [Mental Model: Session → Workspace → Tab → Pane](#mental-model-session--workspace--tab--pane) +- [Launching & Sessions](#launching--sessions) +- [Prefix Keybindings (this repo's config)](#prefix-keybindings-this-repos-config) +- [Default Keybindings (unconfigured actions)](#default-keybindings-unconfigured-actions) +- [Workspaces](#workspaces) +- [Tabs](#tabs) +- [Panes](#panes) +- [Worktrees](#worktrees) +- [Agent Integrations](#agent-integrations) +- [Socket API — Scripting Herdr](#socket-api--scripting-herdr) +- [Remote & Server](#remote--server) +- [Configuration](#configuration) +- [Common Workflows](#common-workflows) +- [Tips & Gotchas](#tips--gotchas) + +--- + +## Mental Model: Session → Workspace → Tab → Pane + +herdr's structure nests four levels, each with its own subcommand namespace: + +| Level | Analogy | What it holds | +|---|---|---| +| Session | tmux session | A named, persistent server-backed connection. Survives detach/terminal close. | +| Workspace | tmux session-within-a-session / project | A logical project context (often one per repo or git worktree). Has its own set of tabs. | +| Tab | tmux window | A named tab within a workspace, holding one or more panes. | +| Pane | tmux pane | A split terminal running a shell or a detected AI agent (Claude Code, Codex, Cursor, etc.). | + +herdr auto-detects which coding agent is running in a pane (via [integrations](#agent-integrations)) and tracks its state (`idle` / `working` / `blocked` / `unknown`), which drives notifications and the `herdr agent wait` / `herdr wait` scripting commands. + +--- + +## Launching & Sessions + +```bash +herdr # launch or attach to the persistent (default) session +herdr --session # use or create a named persistent session +herdr --no-session # run monolithically, no server/client (escape hatch) +herdr --remote user@host # attach through SSH to a remote herdr server +herdr status # local client + running server status +``` + +| Command | Action | +|---|---| +| `herdr session list [--json]` | List named sessions | +| `herdr session attach ` | Attach to a named session | +| `herdr session stop ` | Stop a session (`default` targets the default session) | +| `herdr session delete ` | Delete a named session | + +Detach without killing anything with the `detach` keybinding (default `prefix+q`) — the server keeps running headless, agents keep working, and `herdr` or `herdr session attach ` picks the session back up. + +--- + +## Prefix Keybindings (this repo's config) + +All prefix actions require pressing `Ctrl+B` first, then the key — same two-step model as tmux. This repo intentionally mirrors tmux's split keys (`"` / `%`) instead of herdr's defaults. + +| Key | Action | +|---|---| +| `Ctrl+B h` / `j` / `k` / `l` | Focus pane left / down / up / right | +| `Ctrl+B "` | Split horizontal | +| `Ctrl+B %` | Split vertical | +| `Ctrl+B c` | New tab | +| `Ctrl+B &` | Close tab | +| `Ctrl+B w` | Workspace picker | +| `Ctrl+B g` | Goto (jump menu) | +| `Ctrl+B y` | Enter copy mode | + +Copy mode's internal keys (`v`/`space` select, `y`/`Enter` copy, `q`/`Esc` cancel) aren't configurable. Edit `home/.config/herdr/config.toml` to change any of these — changes apply live via `herdr server reload-config` or the `reload_config` keybinding (default `prefix+shift+r`), no rebuild needed. + +--- + +## Default Keybindings (unconfigured actions) + +Everything below is herdr's out-of-the-box binding for actions this repo hasn't overridden. See `herdr --default-config` for the full annotated list. + +| Key | Action | +|---|---| +| `prefix+?` | Help | +| `prefix+s` | Settings | +| `prefix+q` | Detach | +| `prefix+shift+r` | Reload config | +| `prefix+o` | Open notification target | +| `prefix+shift+n` | New workspace | +| `prefix+shift+g` | New worktree | +| `prefix+shift+w` | Rename workspace | +| `prefix+shift+d` | Close workspace | +| `prefix+shift+t` | Rename tab | +| `prefix+p` / `prefix+n` | Previous / next tab | +| `prefix+1..9` | Switch tab by index | +| `prefix+shift+p` | Rename pane | +| `prefix+e` | Edit scrollback | +| `prefix+tab` / `prefix+shift+tab` | Cycle pane next / previous | +| `prefix+z` | Zoom pane (alias: fullscreen) | +| `prefix+r` | Resize mode | +| `prefix+b` | Toggle sidebar | + +Indexed bindings (`focus_agent`, `switch_workspace`) and navigate-mode movement keys are opt-in — see the commented block in `herdr --default-config`. + +--- + +## Workspaces + +```bash +herdr workspace list +herdr workspace create [--cwd PATH] [--label TEXT] [--focus] +herdr workspace get +herdr workspace focus +herdr workspace rename