Imported from YASoftwareDev/dotfiles (
AGENTS.md). Install upstream withnpx skills add YASoftwareDev/dotfiles. Copyright stays with the author.
AGENTS.md - dotfiles
This file provides codebase context for AI coding agents (Codex, Copilot, etc.).
Repository purpose
Personal dotfiles for Ubuntu/Debian, with RHEL-family (AlmaLinux/Rocky/Fedora)
support via the user-local binary path: one-command install (install.sh),
managed updates (update.sh), post-install test suite (test.sh), and CI
matrix covering 3 Ubuntu versions × 3 install profiles + no-sudo variants
(auto / forced / nonsudoer) on 3 Ubuntu + 2 AlmaLinux versions, plus the nvim
config on pinned nvim 0.10 and 0.11, the latter both with and without build
tools, and one Ubuntu 16.04 cell whose git 2.7.4 exercises the full-clone
fallback and the unapplied-pins path - 28 cells total.
Key files
| File | Role |
|---|---|
get.sh |
curl-pipe bootstrap; auto-stashes local modifications on existing clones before pulling |
install.sh |
Entry point - profile selection, module orchestration |
update.sh |
Tool updates with --check mode, PATH shadow detection, and plugin auto-heal |
test.sh |
Post-install validation (run after every install and update) |
lib/utils.sh |
Shared helpers: logging, sudo detection (detect_sudo uses sudo -n -v to tell a real sudoer from a user not in sudoers), GitHub release fetching |
modules/ |
Per-concern installers: base, zsh, tmux, neovim, tools |
nvim/.config/nvim/init.lua |
Single-file Neovim config (lazy.nvim) |
Critical rules (never violate)
- Gate every apt code path on
$CAN_APT(sudo AND apt-get present, set bydetect_sudo), never on$CAN_SUDOalone - on RHEL-family systems sudo can be available while apt is not, and a bareapt-getcall dies underset -e. Applies to the install/update flow; the optionalscripts/desktop helpers are Ubuntu-only. - All scripts use
set -euo pipefail. Usecount=$(( count + 1 ))- never(( count++ )). Also avoid[ cond ] && var=true- when the condition is false, the expression exits with 1 and tripsset -e. Useif [ cond ]; then var=true; fiinstead. - Every function variable must be declared
local(orlocal -afor arrays). - Run
bash tests/lint-workflows.shbefore pushing a workflow change. An invalid workflow runs nothing, so CI cannot report the fault, and withCI gaterequired the PR blocks with no visible cause. It catches the two that cost runs on PR #52: a double-quoted string inside an expression (only single quotes are legal), and the expression delimiters written in a comment - GitHub scans comments too, and an empty pair is itself a syntax error. CI gateis the single required status check onmaster, and itneedsevery other job. Add any new job to itsneeds- the gate asserts its own completeness and fails when a job sits outside it, because a job nobody required would lose coverage while CI stayed green.- Never let an apt call abort the install. A package name that does not exist on
an older release makes apt exit 100, and
set -ethen kills the whole run - measured on Ubuntu 18.04, whereripgrep/fd-findare absent. Install a bulk list with a per-package retry, keep an optional tool's apt steps non-fatal (|| { log_warn ...; return; }), and let the_install_*GitHub fallbacks cover whatever apt cannot supply. - Never construct GitHub release asset URLs manually - use
_gh_release_infoor_gh_latest_releasefromlib/utils.sh; asset names change between releases. Exception:releases/latest/download/<name>for an asset whose name carries no version (yazi, cheat, uv, tree-sitter) - no API call, no rate limit. - Never use
command -vat install time to probe binary locations - use direct[ -x /absolute/path ]probes. - Never commit generated protobuf files (
*_pb2.py,*.pb.go, etc.). - Logging:
log_step,log_info,log_ok,log_warn,log_error,die- never bareecho. - Read the glibc version with
_glibc_version, neverldd --version | head -1 ... || echo 0.0: under pipefail ldd can take SIGPIPE and the fallback corrupts the value (measured 52/200 runs on Ubuntu 20.04), which installed nvim builds that cannot run there.
Neovim config
nvim/.config/nvim/init.lua registers mixed-case Ex command aliases at the bottom
of the file so accidental Shift-holding doesn't fail:
W → w Wq/WQ → wq Wqa/WQa/WQA → wqa Q → q Qa/QA → qa
Add new aliases to the pairs({...}) table - one line, no boilerplate.
Mason LSP servers - pyright and bash-language-server are npm-based.
They are wrapped in vim.fn.executable('npm') == 1 so hosts without npm (e.g.
GPU servers) skip them silently. Do not remove this guard or add new npm-dependent
servers outside of it.
fzf_ok guards telescope-fzf-native, a C library: it gates that dependency's
cond and the load_extension('fzf') call on make plus a compiler. Without it
the failed load aborted telescope's whole config, so every telescope key was dead
on an nvim 0.11+ host with no build tools. Telescope's own sorter is the fallback.
Same rule as the npm guard - do not remove it, and keep optional native extensions
behind it.
truecolor_ok gates termguicolors and NOTHING else - it must never switch the
colorscheme. nightfly sets only gui colours, so forcing termguicolors on a chain
that cannot deliver 24-bit colour left nothing readable (#53).
Two rules, both measured 2026-09-18 and both easy to get wrong:
- Judge the chain, not
$TERM. Inside tmux$TERMis always tmux's own (tmux-256color) and says nothing about the client; tmux quantizes whatever nvim emits down to the attached client's palette. So_chain_colors()asks tmux for#{client_termname}and counts that terminal's colours withtput -T. Counting beats name-matching: alacritty and xterm-kitty are truecolor terminals whose names carry no256. - Do not "improve" the fallback by switching scheme. habamax and retrobox set
256-colour greys (
ctermfg=251/ctermbg=234) which BOTH collapse to black when quantized to 8 colours - measured 67% of the screen black-on-black, far worse than the bug. Leaving nightfly withtermguicolorsoff renders in the terminal's own fg/bg: 0.4% unreadable against 9.8% before the fix.
tests/nvim-colour-fallback.sh pins all four arms, including the tmux one.
Version gates - supported hosts run nvim 0.9-0.12. Options and plugins that need a
newer nvim are gated (vim.fn.has('nvim-0.X'), lazy cond), because one invalid
option value aborts the rest of init.lua. Parser installs are gated on nvim 0.12,
a tree-sitter CLI >= 0.26.1, a C compiler ($CC's first word), curl and tar (what
nvim-treesitter needs to build them);
nvim-treesitter itself still loads from 0.10, as on master.
update.sh helpers
_update_plugin NAME PATH [URL]- pulls the plugin atPATH; ifPATHis missing andURLis given, clones it. The zsh-plugins block passes URLs soupdate.sh zsh-pluginscan self-heal machines that missed a plugin install._update_std_tool CMD LABEL REPO GNU_ARM [BINARY] [ASSET_PREFIX]- covers standard single-binary GitHub tarball tools (rg, eza, fd, ...).
Version bump rules
fix:commits -> patch (X.Y.Z+1)feat:commits -> minor (X.Y+1.0)BREAKING CHANGE-> major (X+1.0.0)
Edit VERSION, update CHANGELOG.md, commit as chore: release vX.Y.Z, tag
vX.Y.Z on the merge commit on master.
