Guide for AI agents working in this repository. It collects repo-specific gotchas.
- The default shell is zsh (the agent's Bash tool runs under zsh too). Do not
use
statusas a variable name in scripts — in zshstatusis read-only and assigning to it raises aread-only variableerror. Usestor similar. - Using
!=in ajqfilter gets escaped by zsh to\!=and causes a parse error. Write it with the| notpattern, e.g.select(.x | . == null | not).
env.shandenv.d/**/*.shhave no shebang. They are fragments sourced at shell startup and contain bash/zsh syntax (function definitions,[[ ]], etc.), sosh -n(dash) false-flags them. Check these withbash -n.- CI (the lint job in
.github/workflows/ci.yml) enumerates every tracked*.shviagit ls-filesand decides the interpreter from the shebang:*bash*→bash -n,#!...sh→sh -n, no shebang →bash -n(for the sourced fragments above).
- Version pins are centralized in the
VERSION=line of eachsetup.d/ubuntu/00X-*.sh(nodenv=002 / rbenv=003 / pyenv=004 / goenv=005). Change only that file. - CI asserts that the installed result matches this pin (and fails on mismatch).
The expected value is extracted from each setup script's
VERSION=line, so do not hardcode it. - When verifying versions, run from a neutral directory and don't rely on
PATH/shims.
rbenv init -/goenv init -may not prepend shims to PATH, so the runner's system ruby/go can win. Also, *env honors a directory-local pin (e.g..ruby-version) over the global one — and this repo's root has.ruby-version = system. CI therefore runs*env execfrom$HOMEto check the global version that setup installed. - For repo updates, guard with
[ -d ~/.Xenv/.git ]and usegit pull --ff-onlyso a non-git install (package / manual extraction / symlink) doesn't break and no unintended merge commit is created.
~/.gnupgis a symlink torc.d/gnupg.gpg-agent.confis a generated file and is gitignored. Its sources arerc.d/gnupg/gpg-agent.conf.linux/.darwin, whichsetup.d/dotfiles.shcopies into place per platform. Edit the.linux/.darwinsources, not the generated file. The cache TTL is one year (unlock once and it stays cached).- Commits are GPG-signed (key
036459B1). The agent's non-tty shell can't run an interactive pinentry, so a commit would hang. At the start of a session, have the user runecho | gpg --clearsign -u 036459B1 -o /dev/nullonce to cache the passphrase in the agent before doing any commit work.
- Platform-specific files are managed with a
*.darwin/*.linuxsuffix;resolve_os_nameinsetup.d/dotfiles.shresolves them to the real name per OS. - Always push with
git push origin <current-branch>(no bare push).
- Skills are tool-agnostic (a folder with a
SKILL.md, optionalreferences/) and shared across Claude Code, Codex, and Google Antigravity (agy). The single source of truth isrc.d/agents/skills/<name>/— add and edit skills there, never in a tool-specific copy. - Each tool reads the same files through symlinks:
- Claude:
rc.d/claude/skillsis a symlink →../agents/skills(committed, git mode 120000).~/.claude/skills→rc.d/claude/skillsresolves through it. ~/.agents→rc.d/agentsis created automatically by the genericfor f in rc.d/*loop (it's not in the exclusion list), giving the tool-agnostic~/.agents/skills/global location for free.- Codex / Antigravity:
setup.d/dotfiles.shsymlinks each skill individually into~/.codex/skills/<name>and~/.gemini/config/skills/<name>(guarded on each tool's presence). It links per-skill — not the whole dir — so tool-specific siblings (e.g. Codex's.system) and externally-managed skills are left untouched.[ -e ]skips dangling skills (e.g. theccskill-gptimagesymlink that points at an externalsrc/checkout absent on some machines).
- Claude:
- Antigravity (
agy) reads global skills from both~/.gemini/config/skills/and~/.gemini/antigravity-cli/skills/; we useconfig/skills/because it is shared by the CLI, IDE, and 2.0. Workspace skills go in<workspace>/.agents/skills/. rc.d/codex/config.tomlis symlinked to~/.codex/config.toml; the machine-local[projects]trust entries Codex appends through the symlink are kept out of commits by a clean filter (see the clean-filters section below).rc.d/codex/rules/*.rulesare copied once bysetup.d/dotfiles.sh(only when the local file is missing or an old symlink) — Codex appends machine-local command approval rules there and no filter covers them, so do not symlink the rules. Codex project trust entries are exact worktree paths; do not rely on~/src/*-style wildcards there.
- The global instructions for Claude Code, Codex, and Antigravity are a single
file:
rc.d/agents/AGENTS.md— edit it there, never a tool-side copy. - Tool-side files are committed symlinks to it:
rc.d/claude/CLAUDE.md,rc.d/codex/AGENTS.md, andrc.d/gemini/AGENTS.mdall point to../agents/AGENTS.md, so~/.claude/CLAUDE.md,~/.codex/AGENTS.md, and~/.gemini/config/AGENTS.md(a standalone AGENTS.md in an Antigravity customization root is loaded as global rules) resolve through them.~/.agents/AGENTS.mdalso works via the~/.agents→rc.d/agentslink. - Keep the content tool-agnostic; a tool-specific rule belongs in the tool's own section within the shared file, not in a separate per-tool file.
- Some tool configs are symlinked into
~and the tool writes runtime state back through the symlink into the working tree. That state is kept out of commits with git clean filters (defined inrc.d/gitconfig, wired up in.gitattributes, scripts inbin/):rc.d/codex/config.toml→bin/codex-config-cleanstrips[projects.*]trust entries and[tui.model_availability_nux].rc.d/gemini/settings.json→bin/agy-settings-cleanstripstrustedWorkspaces.rc.d/claude/settings.json→bin/claude-settings-cleanstripsautoMode(repo-specific environment blocks written by setup flows such as/auto-mode-setup; they describe private repositories and must never be committed here — they belong in that repo's.claude/settings.local.json).
- The working-tree file keeps the full content (it is what the tool reads);
only the staged/committed blob is cleaned.
git difftherefore stays quiet even though the on-disk file contains machine-local state. rc.d/gemini(likeclaude,codex,gnupg) is in the exclusion list of the genericrc.d/*loop —~/.geminiholds runtime state and must never be replaced wholesale;setup.d/dotfiles.shlinks only the reusable pieces.
CLAUDE.mdis a symlink to thisAGENTS.md. Edit the content on the AGENTS.md side.