This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a dotfiles repository using Homeshick conventions. All dotfiles live under home/ and get symlinked to ~ by homeshick. The repository uses literate configuration extensively—.org files tangle to generate actual config files.
Important: Files in this repo are symlinked to ~ maintaining the same hierarchy. There's never a need to "check from the home directory" - always work with files in this repo directly. If symlinks seem missing, run homeshick link -f dotfiles to ensure all links are in place.
After editing any .org config file, tangle it to regenerate the output:
# Tangle Spacemacs config
emacs --batch -l org --eval '(org-babel-tangle-file "home/dotspacemacs.org")'
# Tangle Hammerspoon config
emacs --batch -l org --eval '(org-babel-tangle-file "home/.hammerspoon/init.org")'Or from within Emacs: C-c C-v t on the org file.
# Link dotfiles to home directory
homeshick link dotfiles
# Check for unlinked files
homeshick check dotfiles# Sync mail
mbsync -a
# Reindex after config changes (all addresses are aliases via Fastmail)
mu init --maildir=~/Maildir/Fastmail \
--my-address=rod@rodknowlton.com \
--my-address=rod.knowlton@gmail.com \
--my-address=codelahoma@gmail.com \
--my-address=knowshank@knowshank.com \
--my-address=knowshank@fastmail.com
mu index| Source File | Generates | Purpose |
|---|---|---|
home/dotspacemacs.org |
home/.spacemacs.d/init.el |
Spacemacs configuration |
home/.hammerspoon/init.org |
home/.hammerspoon/init.lua |
macOS automation |
home/.hammerspoon/menuHammerCustomConfig.org |
menuHammerCustomConfig.lua |
Modal menu system |
Never edit generated files directly—they contain "DO NOT EDIT" warnings and will be overwritten.
Located in home/.emacs.d/private/:
gtd-zettelkasten/— Symlink to external repo (~/github/org-gtd-zettelkasten/layers/gtd-zettelkasten). Full GTD workflow with org-roam integration. Keybindings underSPC o o.rk-layout/— Frame geometry persistence for GUI Emacs.
- Emacs Daemon: Emacs runs as a daemon via Homebrew service (
brew services restart emacs-plus@30). Useemacsclient -cto create new frames. Hammerspoon'shyper+jhandles this automatically—focusing existing frames or creating new ones via emacsclient. - Emacs ↔ Hammerspoon: URLs from Emacs route through Hammerspoon's URLDispatcher to select browser. Edit-in-Emacs support via editWithEmacs.spoon.
- Email: mu4e → mbsync → Fastmail. Passwords via
pass(password-store) with auth-source-pass. - Shell: Zsh with oh-my-zsh, powerlevel10k theme.
Ctrl-Ftriggers tmux-sessionizer.
- Org files:
~/Dropbox/org/(GTD ingtd/, notes innotes/, reviews inreviews/) - Mail:
~/Maildir/Fastmail/ - Password store:
~/.password-store/
home/.oh-my-zsh→ codelahoma/oh-my-zsh (rodk branch)
Layer-specific variables are set in the layer declaration in dotspacemacs.org, not in user-config:
(mu4e :variables
mu4e-installation-path "/opt/homebrew/share/emacs/site-lisp/mu/mu4e"
mu4e-mu-binary "/opt/homebrew/bin/mu")The dotspacemacs.org uses noweb syntax. Source blocks tagged with :noweb-ref config-layers or :noweb-ref user-config are collected into the appropriate sections during tangling.
The tmux config has allow-passthrough on (line 446 of .tmux.conf.local) which lets applications send escape sequences directly to iTerm2. If scrollback stops working inside tmux:
- Quick fix: Start a fresh tmux server (
tmux kill-server && tmux) - Diagnose: Corrupted terminal state is often the cause, not config changes
- Test passthrough:
tmux set -g allow-passthrough offtemporarily
To capture terminal escape sequences for debugging:
script -q /tmp/output.txt
# run commands, then exit
cat -v /tmp/output.txt | less # look for ^[[2J, ^[[3J, ^[[?1049hIf Emacs GUI is unresponsive or stuck in a prompt you can't escape:
# Graceful quit via emacsclient (saves buffers first)
emacsclient -e '(save-buffers-kill-emacs)'
# If that fails, send SIGTERM (gives Emacs chance to clean up)
pkill -TERM Emacs
# Last resort: force kill
pkill -9 EmacsSymptoms: mu4e kicks you back to main menu after update, or shows "Update process returned with non-zero exit code"
Diagnosis:
mbsync -a 2>&1; echo "Exit code: $?"Common errors:
Maildir error: duplicate UID X in /path/to/folderMaildir error: UID X is beyond highest assigned UID Y
History:
- 2026-01-21: Trash folder had ~65 duplicate UIDs (111-299 range) plus UIDs beyond valid range (191840+). Old files from Oct 2024 sync conflicted with Jan 2026 files.
- 2026-01-21 (same day, later): Trashing a single email triggered duplicate UID 300. Root cause: 296 orphaned files from Oct 2024 (filename prefix
1768500288.*) were never cleaned up and had UIDs overlapping with current UID assignments. Fix: Remove ALL orphaned files from old sync batch, not just duplicates found so far.
Root cause pattern: Old sync creates files, sync state gets corrupted/reset, server reassigns same UIDs to new emails, old orphaned files conflict. Check for files with old timestamps that don't match recent sync patterns.
Fix duplicate UIDs:
# Find duplicates in a folder
ls -1 ~/Maildir/Fastmail/Trash/cur/ | sed 's/.*,U=\([0-9]*\).*/\1/' | sort -n | uniq -d
# Find files for a specific UID
find ~/Maildir/Fastmail/Trash -name "*,U=204:*"
# Remove older duplicate (check timestamps, keep newer)
rm -f ~/Maildir/Fastmail/Trash/cur/OLDER_FILE_HEREFix out-of-range UIDs:
# Find UIDs beyond limit (check .uidvalidity for max)
ls ~/Maildir/Fastmail/Trash/cur/ | grep -o 'U=[0-9]*' | sed 's/U=//' | awk '$1 > 3073 {print}'
# Remove files with invalid UIDs - they'll re-sync from server
rm -f ~/Maildir/Fastmail/Trash/cur/*U=INVALID_UID*After fixing, run mbsync -a again until exit code is 0, then reindex mu4e:
mu index --rebuild