Imported from valbeat/dotfiles (
AGENTS.md). Install upstream withnpx skills add valbeat/dotfiles. Copyright stays with the author.
Repository Guidelines
Project Structure & Module Organization
- Core dotfiles live at repo root (
.zshrc,.vimrc,.gitconfig,.tmux.conf, etc.). Each is symlinked into$HOMEby home-manager (darwin/home.nix); new root dotfiles must be added to itsdotfileslist. That list is for files and for directories this repository owns outright (.vim); a directory shared with other tools goes inconfigFilesoragentFilesinstead, entry by entry. agents/AGENTS.mdis the user-level instruction file, shared by all three agents and in effect in every project.darwin/home.nixlinks that single file to~/.claude/CLAUDE.md,~/.codex/AGENTS.mdand~/.gemini/GEMINI.md(globalInstructions), so there is nothing to keep in sync: edit it once. It is structured as a common section plus one section per agent, each of which is told to follow only its own.- It must never be moved to
./CLAUDE.mdor./.claude/CLAUDE.md. Claude Code would then read it as this repository's project instructions and stop falling back toAGENTS.md— the same trap as #154. - This file (
AGENTS.mdat the root) is the project instruction file, and the only one; Claude Code reads it directly (v2.1.277+). That fallback applies only while no./CLAUDE.md,./.claude/CLAUDE.mdorCLAUDE.local.mdexists, so never add one here. .claude/holds the files this repo owns for Claude Code:agents/,hooks/andstatusline.sh(skills do not live here, see below).darwin/home.nixlinks those files individually into~/.claude,~/.codexand~/.gemini, which are real directories holding each tool's runtime state (sessions, plugins, logs); never link the directories themselves, andtools/migrate-agent-dirs.shconverts an old whole-directory link. Everything else under them is runtime state and untracked..codex/and.gemini/no longer hold tracked files — Codex custom prompts are deprecated, so Codex gets its skills from the plugin marketplaces instead..config/is the same case and uses the same rule:~/.configis shared with every XDG-following tool (gh,gcloud,fish,karabiner,git, ...), sohome.nixlinks only the entries this repo owns (configFiles), never~/.configitself. Linking the directory hides every unmanaged entry — home-manager moves the real one to~/.config.hm-backup— and those tools silently lose their configuration and credentials. New entries go in bothconfigFilesand the.config/*allow-list in.gitignore.MIGRATE_DIRS=.config tools/migrate-agent-dirs.shconverts an old whole-directory link.darwin/holds the nix-darwin configuration:configuration.nix(entry),system-defaults.nix(macOS defaults),home.nix(dotfile symlinks),homebrew.nix(taps/brews/casks), andclaude.nix+claude/merge.jq(managed keys of the Claude Code / Antigravity CLIsettings.json).darwin/orca.nixapplies the Orca automation definitions from the private flake inputorca-automations(valbeat/orca-automations) at activation. That repository is the single source of truth: never change automations only in Orca. Changes made in Orca (the app, another agent) must be written back with itsscripts/export.shand merged; otherwise apply stops with a conflict and leaves Orca untouched. After merging there, runnix flake update orca-automationshere and switch. Each host also declares anorcaHostRole(flake.nix, written to~/.local/state/orca-automations/roleand passed toapply.sh): definitions name the roles they run under and only the matching ones reach that Mac's Orca, so a second machine does not fire the same PR-opening automation twice. A new host runs nothing until it is given a role.flake.nixwires it together and exposes every command as an app:build/switch/update(system) andtest/settings-diff/patches/hunk-skill(repo tasks, formerly theMakefile). Vim-related assets sit under.vim/(plugins, colors, rc snippets).tools/keeps the non-nix helpers:patches/(plugin-cache patches),settings-diff.sh, andtests/(test-*.sh). They are run through the flake apps (nix run .#testand friends); there is noMakefile.
Build, Test, and Development Commands
nix run .#build: dry-run build of the darwin system; CI runs this on every PR. Run it after touchingdarwin/orflake.nix.nix run .#switch: fetches inputs as the invoking user first (the privateorca-automationsinput needs the user's SSH key), then builds and activates (system defaults + symlinks + Homebrew + settings.json merge; runs as root). An agent can run it only after the user runssudo -vin a real terminal: the sudo timestamp is shared per user for 15 minutes (darwin/configuration.nix). Ask for that instead of handing the command back, and run it by the expanded absolute path (nix run /Users/<user>/src/github.com/valbeat/dotfiles#switch; the allowlist indarwin/claude.nixmatches that exact text, not~or.#switch) so the flake in the current directory is never the one activated.nix run .#update: pulls latestmainand updates git submodules. The public flake inputs (nixpkgs, nix-darwin, home-manager) are updated by.github/workflows/flake-update.ymlevery Monday as a pull request;orca-automationsis private and updated locally withnix flake update orca-automations.nix fmt: formats all.nixfiles with nixfmt; CI fails on unformatted files (nix fmt -- --ci) and also runsnix flake check --no-build.nix run .#test: runstools/tests/test-*.sh(settings merge, settings-diff, agent-dir migration). When changingmerge.jq, update the test first — andgit adda brand-new test file before running, see Gotchas.nix run .#settings-diff: shows which managed keys the next switch would reset in~/.claude/settings.jsonand~/.gemini/antigravity-cli/settings.json.nix run .#patches: applies theclaude -preplacement patches to plugin caches (idempotent; rerun after a plugin update overwrites them).nix run .#hunk-skill: re-syncs the vendoredhunk-reviewskill intoagent-plugins-privateafterbrew upgrade hunk(then bumpportable-toolsand ship it).PLUGINS_PRIVATEoverrides the destination checkout.
Claude Code / Codex Settings
~/.claude/settings.json,~/.gemini/antigravity-cli/settings.json, and~/.codex/config.tomlare untracked: the tools write runtime state into them (/model,/config, auto mode, plugin installs, Orca's agent-status hooks), and committing them would leak machine- and project-local data into this public repo.- Intended settings (permissions, own hooks,
enabledPlugins, UI preferences) go indarwin/claude.nix;nix run .#switchmerges them into the live files viadarwin/claude/merge.jq(managed keys win, unnamed keys pass through, hooks are unioned).modelis deliberately not managed. - A managed key changed locally via
/configorclaude plugin installis reset on the next switch. Checknix run .#settings-difffirst and port anything worth keeping todarwin/claude.nix. - Skills never live in this repo. They live in the
valbeat/agent-plugins-private(default; environment-coupled and experimental) andvalbeat/agent-plugins(public; generalized) marketplaces and are invoked with a namespace (/personal-tools:review,/git-workflow:commit). Codex reads the same marketplaces:agent-plugins-privatelists onlyportable-tools(skills that are safe outside Claude Code) in.agents/plugins/marketplace.json, which Codex prefers over.claude-plugin/marketplace.json; everything else stays Claude-only. Update Codex withcodex plugin marketplace upgrade. Placement rules and the inventory procedure are in/skill-management:skill-inventory. To update one: edit the marketplace repo (~/src/github.com/valbeat/agent-plugins{,-private}), bumpversioninplugin.json(an unchanged version is skipped), push, then runclaude plugin marketplace updateandclaude plugin update.
Gotchas
- The flake apps run the repo's files as git sees them: a modified tracked file is picked up without committing, but a file that is still untracked is not in the flake source at all.
git adda newly created test or tool beforenix run .#test/nix run .#patches, or it is silently skipped. - CI does not fetch the private
orca-automationsinput:nix run .#buildthere overrides it withci/orca-automations-stub. Locally, reproduce the CI check withnix run .#build -- --override-input orca-automations "path:$PWD/ci/orca-automations-stub". - Switching branches rewrites the live Claude/Codex/Gemini config. The linked files (
agents/AGENTS.md,.claude/agents/,.claude/hooks/,.claude/statusline.sh) resolve into this checkout, sogit checkoutswaps them for every running session. Runtime state is no longer affected: it lives under$HOME, outside this repository. Do branch work in a worktree, never switch during--loopor long autonomous runs, and confirmgit branch --show-currentbefore committing (another session may have switched the shared checkout). Push explicitly withgit push origin <branch>. - Checking out a stale
mainthat still trackedsettings.jsonoverwrites the live file, and the followinggit pulldeletes it. Rungit fetchfirst, checkgit diff --stat HEAD..origin/main -- .claude/settings.json .gemini/settings.json, and copy~/.claude/settings.jsonaside before moving. .claude/hooks/guard.shblocks any Bash command whose text matchesclaude -p/claude --print, including anechoorgrepthat merely contains it. Search with a different token, or prefixCLAUDE_ALLOW_PRINT=1when the call is intended.
Coding Style & Naming Conventions
- Shell/Vim config: prefer POSIX-compatible snippets and two-space indentation to match the existing style; keep per-tool settings in their own rc file and follow the current plugin manager/layout.
- Keep filenames dot-prefixed and aligned with
$HOMEpaths; guard a platform-specific variant with the.gitconfig.osxpattern instead of inventing new suffixes.
Testing & Review
- The verification commands are in the section above:
nix run .#buildafterdarwin/orflake.nix,nix run .#testaftertools/ormerge.jq,nix fmtafter any.nixedit. CI enforces all three on PRs. - Spot-check a representative link after a switch (
ls -l ~/.zshrcmust resolve into this repo). For Vim tweaks, launchvimonce to confirm no startup errors. - Rebase onto
mainbefore opening a PR, and give the description a summary, any new command or env var, and the verification steps actually run. Commit message, branch and PR conventions are the global ones inagents/AGENTS.md.
Security
- Never commit machine-specific secrets or tokens: use env var references or a
.gitconfig.local-style override kept out of version control. Keep a new tool's defaults secure (gpg, SSH) and note any required permissions or key locations in a comment next to the config they affect.