Skip to content

Latest commit

 

History

History
211 lines (144 loc) · 6.19 KB

File metadata and controls

211 lines (144 loc) · 6.19 KB

AGENTS.md

Instructions for AI coding agents (Claude Code and similar) working in this repo.


Repo layout

This is a regular git repository. It contains two kinds of things side by side:

  1. Dotfiles — the actual config files (.gitconfig, .tmux.conf, .hammerspoon/, .config/karabiner/, .config/nvim/, etc.)
  2. Nix configurationflake.nix and nix/ which use nix-darwin and home-manager to deploy those dotfiles and manage the macOS system

home-manager deploys dotfiles by creating symlinks in $HOME that point back into the repo checkout (via config.lib.file.mkOutOfStoreSymlink). The repo must be cloned to ~/.dotfiles — this path is defined in nix/modules/dotfile-links.nix as repoPath.


How to apply changes

darwin-rebuild switch --flake ~/.dotfiles#personal-mbp

Only the owner of the machine can run this. Agents should never run it.


How to validate without applying

# Check flake evaluation (syntax + type errors)
nix flake check ~/.dotfiles

# Build without switching (downloads closures, verifies linkage)
nix build ~/.dotfiles#darwinConfigurations.personal-mbp.system

These are safe to run. Run them after any change to nix/ files.


Commit conventions

Follow Conventional Commits.

Format: <type>(<scope>): <subject>

Types: feat, fix, refactor, docs, chore, test, ci

Scopes for this repo: flake, homebrew, macos-defaults, packages, git, shell, tmux, dotfile-links, readme, agents

Subject line

  • ≤50 characters
  • Imperative mood: "add feature" not "added feature" or "adds feature"
  • No trailing period
  • Lowercase after the type prefix

Body

  • Separate from subject with a blank line
  • Wrap at 72 characters
  • Explain the WHY and WHAT, not the HOW (the diff shows the how)
  • Use bullet points where appropriate (hyphen followed by a single space)
  • Use a hanging indent for multi-line bullets

Multiple commits

Split changes into multiple atomic commits when they touch different concerns. One commit per logical change.

File Count Minimum Commits
3+ files 2+ commits
5+ files 3+ commits

Combine files in the same commit only when tightly coupled (e.g., implementation + its direct test, or a type definition + the only file that uses it).

Other rules

  • Commits must only introduce code changes relevant to the logical change at hand
  • Never amend or rebase commits already pushed to shared branches
  • Never commit secrets, API keys, or credentials

Worktrees

Parallel subagents require worktrees. Each subagent MUST work in its own worktree (wt switch <branch>), not the main repo. Never share working directories.


Pull requests

Describe what the code does now — not discarded approaches, prior iterations, or alternatives. Only describe what's in the diff.

Use plain, factual language. A bug fix is a bug fix, not a "critical stability improvement." Avoid: critical, crucial, essential, significant, comprehensive, robust, elegant.


What not to touch

Do not delete, overwrite, or reorganize these files — they are the active dotfiles or are retained for historical reference:

  • Active dotfiles managed as symlinks: edit in place in the repo, changes are live immediately (no rebuild needed)
  • nix/modules/dotfile-links.nix repoPath variable — do not change without also updating README.md bootstrap instructions to match

Adding a new package

  1. GUI app or font? → Add to homebrew.casks in nix/modules/homebrew.nix
  2. CLI tool available in nixpkgs? → Add to environment.systemPackages in nix/modules/packages.nix
  3. CLI tool NOT in nixpkgs, or from a custom tap? → Add to homebrew.brews in nix/modules/homebrew.nix (add the tap to homebrew.taps if needed)
  4. App Store app? → Add to homebrew.masApps in nix/modules/homebrew.nix with "App Name" = <numeric-id> syntax

To check if a package exists in nixpkgs:

nix search nixpkgs <name>

After any change to nix/ files, validate before committing:

nix flake check ~/.dotfiles

Updating flake inputs

cd ~/.dotfiles
nix flake update                  # update all inputs (nixpkgs, nix-darwin, home-manager)
nix flake update nixpkgs          # update a single input
# validate, then commit the lock file
nix flake check
git add flake.lock
git commit -m "chore(flake): update inputs"

When to use a programs.* module vs. a symlink

Use a programs.* home-manager module when:

  • A module exists that understands the config's structure (e.g. programs.git, programs.zsh, programs.tmux)
  • The file is never written to by an external tool
  • Nix option validation or composability is genuinely useful

Use a mkOutOfStoreSymlink home.file entry (dotfile-links.nix) when:

  • No useful Nix module exists — inlining the content as a raw home.file.text string buys nothing: no validation, no composability, just rebuild friction
  • An external tool writes back to the file (Karabiner exports karabiner.json when you change settings in its UI; LazyVim writes into .config/nvim/)
  • The file needs a fast iteration loop — Hammerspoon Lua, Neovim config, Karabiner mappings are edited, reloaded, and tweaked in seconds; a full darwin-rebuild switch for each tweak is the wrong tradeoff

Never use plain .source = ./path (without mkOutOfStoreSymlink) — that copies the file into the read-only Nix store and breaks any tool that tries to write to it.


Adding a new dotfile link

All home.file entries live in nix/modules/dotfile-links.nix. Use the link helper for simple paths:

home.file.".some-file".source =
  config.lib.file.mkOutOfStoreSymlink "${repoPath}/.some-file";

Or use the shorthand for paths that don't need an inline comment:

home.file.".some-config" = link ".some-config";

Never push

Agents must never run git push or any command that modifies a remote (git push, git push --force, gh pr create that triggers a push, etc.).

All commits are local only. Pushing is the owner's responsibility after reviewing the final state.