chore: consolidate dotfiles and add AI agent configs#8
Conversation
There was a problem hiding this comment.
Pull request overview
This PR modernizes the dotfiles “script architecture” around POSIX sh entrypoints (self-contained DOTFILES + utils.sh sourcing), adds machine profiles (personal vs work) for gating behavior, and introduces shareable AI agent configuration management (Claude + Codex) applied via dot --apply.
Changes:
- Add standardized OS detection + machine profile helpers in
utils.shand exportDOTFILES_PROFILEearly fromzshenv. - Expand
dotCLI and scripts to support--apply,--defaults, and--uninstall, and split “install tools” vs “apply config”. - Add AI agent config syncing (symlinks + Codex config fragment merge) and gitignore rules to avoid syncing runtime/secret state.
Reviewed changes
Copilot reviewed 27 out of 31 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
zsh/zshenv.symlink |
Exports DOTFILES_PROFILE early from an XDG config file for consistent gating across shell/topics. |
vscode/install.sh |
Starts using utils.sh OS predicates; manages VS Code settings symlinks/extensions. |
uv/update.sh |
Switches to sh shebang for portability. |
uv/install.sh |
Switches to sh + set -e; sources utils.sh. |
utils.sh |
Adds POSIX-friendly OS detection helpers + machine profile helpers; refines formatting helpers. |
ssh/apply.sh |
Converts to sh, uses OS predicates, ensures ~/.ssh exists before linking config. |
script/vscode-extensions |
Removed legacy VS Code extension installer script. |
script/update |
Self-contained update runner; gates macOS-only system updates and runs all update.sh. |
script/uninstall |
New script to remove dotfile symlinks that point into $DOTFILES. |
script/post-bootstrap |
Removed legacy post-bootstrap orchestration script. |
script/install |
Shebang updated to sh (installer runner remains as-is). |
script/bootstrap |
Refactors bootstrap flow: base dirs, profile setup, OS-aware tool installs, apply step, macOS defaults. |
script/apply |
New “config-only” path to create symlinks and run all */apply.sh renderers/linkers. |
mise/update.sh |
Switches to sh shebang for portability. |
mise/install.sh |
Switches to sh + set -e; uses OS predicates; tightens curl flags. |
macos/set-defaults.sh |
Fixes utils.sh sourcing path and shellcheck directives. |
linux/install.sh |
Updates shellcheck directives for utils.sh sourcing. |
linux/debian.sh |
Replaces which with command -v for POSIX portability. |
gnupg/apply.sh |
Uses OS predicates; updates error message to uname-based platform string. |
dotfiles.sh |
Removes unused/legacy entrypoint script. |
codespaces/installer.sh |
Switches to sh, improves robustness of shell env steps (e.g., rm -f, command -v). |
bin/dot |
Expands CLI flags (--apply, --defaults, --uninstall) and adds Windows guard. |
ai/codex/README.md |
Documents Codex config structure and the managed-block merge approach for config.toml. |
ai/codex/prompts/.gitkeep |
Adds placeholder for shareable Codex prompts directory. |
ai/codex/config.d/mcp.toml.example |
Example MCP server fragment templates (committed vs local secret-bearing). |
ai/codex/config.d/.gitkeep |
Adds placeholder for Codex config fragments directory. |
ai/claude/settings.json |
Adds shareable Claude settings. |
ai/claude/CLAUDE.md |
Adds shared, tool-agnostic agent standards (symlinked into Claude + Codex). |
ai/apply.sh |
New AI config applier: symlinks trees and merges Codex fragments into managed block. |
.gitignore |
Adds macOS .DS_Store ignores and AI runtime/secret-state ignores; ignores local Codex fragments. |
.github/README.md |
Documents new dot workflow, machine profiles, and self-contained script architecture. |
Comments suppressed due to low confidence (7)
vscode/install.sh:29
- On macOS the symlink destination is hard-coded to
.../settings.json, so if more files are added (keybindings/snippets) the loop would overwrite the same destination repeatedly. Use the per-file destination directory and link to$file(and create the directory first).
if is_macos; then
ln -svf "${DOTFILES}/vscode/$file" "$HOME/Library/Application Support/Code/User/settings.json"
elif is_linux; then
ln -svf "${DOTFILES}/vscode/$file" "$HOME/.config/Code/User/$file"
else
echo 'Unsupported OS detected. Skipping VSCode setup...'
fi
mise/install.sh:18
- After a fresh install, the script exits early (
exit 0) and never runs the rest of the setup (completion + tool installs). Alsocommand -vis being used with a file path, which is not a valid way to check/installmisein POSIX sh.
if command -v "${HOME}/.local/bin/mise"; then
echo 'Successfully installed mise'
exit 0
fi
echo 'Unable to locate mise after installation' 1>&2
exit 1
gnupg/apply.sh:4
- This script sources
${DOTFILES}/utils.shbut does not set a fallback forDOTFILES, so it will fail when invoked standalone.
gnupg/apply.sh:11 - Likely typo:
pinentry-cursedoesn’t match the common binary name (pinentry-curses) and contradicts the comment below. As written, this will point GnuPG at a non-existent pinentry on most Linux installs.
ssh/apply.sh:4 ssh/apply.shsources${DOTFILES}/utils.shbut never sets a default forDOTFILES, so it will fail when invoked standalone (andscript/applyruns appliers viash, so they should be self-contained).
ssh/apply.sh:18echoin POSIX sh does not reliably interpret\n, so theInclude ...\nline is likely to be written with a literal\nin the ssh config. Useprintfto emit a real newline.
ssh/apply.sh:21- Same
echo/newline issue for the macOS include line —\nmay be literal in manyshimplementations. Preferprintffor predictable output.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 31 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (6)
script/install:6
- This runner script uses
$DOTFILESwithout ensuring it is set, and usessh -cto run the installer path as a string. Per the repo’s “self-contained scripts” convention, it should setDOTFILES, sourceutils.sh, and execute installers viash "$installer".
#!/bin/sh
# Run all dotfiles installers.
set -e
find "${DOTFILES}" -name install.sh | while read -r installer; do sh -c "${installer}"; done
ssh/apply.sh:20
- This
echowrites a literal\ninto the ssh config (single quotes prevent escape expansion), which will break theIncludedirective. It also writestmp_configinto the current working directory. Useprintffor the newline and write the temp file undersrc_dir.
ssh/apply.sh:23 - Same issue as the Linux branch above: this writes a literal
\nand uses a temp file in the current working directory. Useprintfand keep the temp file undersrc_dir.
script/install:6 - This runner script uses
$DOTFILESwithout ensuring it is set, and usessh -cto run the installer path as a string. Per the repo’s “self-contained scripts” convention, it should setDOTFILES, sourceutils.sh, and execute installers viash "$installer".
#!/bin/sh
# Run all dotfiles installers.
set -e
find "${DOTFILES}" -name install.sh | while read -r installer; do sh -c "${installer}"; done
ssh/apply.sh:20
- This
echowrites a literal\ninto the ssh config (single quotes prevent escape expansion), which will break theIncludedirective. It also writestmp_configinto the current working directory. Useprintffor the newline and write the temp file undersrc_dir.
ssh/apply.sh:23 - Same issue as the Linux branch above: this writes a literal
\nand uses a temp file in the current working directory. Useprintfand keep the temp file undersrc_dir.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 31 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (3)
script/install:6
script/installisn't self-contained (it assumes$DOTFILESis already set) and it runs installers viash -c, which is unsafe (word-splitting) and also ignores shebang options like#!/bin/sh -e. This breaks running the script standalone and can hide failures in installers that rely on-e.
set -e
find "${DOTFILES}" -name install.sh | while read -r installer; do sh -c "${installer}"; done
uv/install.sh:12
uv/install.shrelies oncommand_exists uv/uv ..., but bootstrap runs in a non-interactive shell where~/.local/bintypically is NOT on$PATH. This can cause false negatives (reinstall loops) or failures right after install. Add~/.local/bintoPATHfor the duration of the script.
# shellcheck disable=SC3046 source=/dev/null
. "${DOTFILES}/utils.sh"
if ! command_exists uv; then
echo 'Installing uv'
curl -LsSf https://astral.sh/uv/install.sh | sh
command_exists uv || { echo 'Unable to locate uv after installation' 1>&2; exit 1; }
vscode/install.sh:46
- Even when
$code_useris empty (unsupported OS) or thecodeCLI isn't on PATH, the script still callsinstall_extensions, which will fail under#!/bin/sh -e. Gate extension installation on both a supported OS andcommand_exists code.
if [ -n "$code_user" ]; then
mkdir -p "$code_user"
# Link each tracked config file to its OWN destination (not all to settings.json).
for name in settings.json keybindings.json; do
src="${DOTFILES}/vscode/$name"
[ -e "$src" ] && ln -sfnv "$src" "$code_user/$name"
done
# Snippets directory, if present.
[ -d "${DOTFILES}/vscode/snippets" ] && ln -sfnv "${DOTFILES}/vscode/snippets" "$code_user/snippets"
fi
install_extensions
Streamline the install/maintenance layer around a single front door (bin/dot) and remove dead/duplicate code. - bin/dot: fix --update bug; add --apply/--defaults/--uninstall; Windows guard - delete dead dotfiles.sh, orphaned post-bootstrap, dead vscode-extensions - wire macos/set-defaults.sh into bootstrap (it never ran before) - fix setup_git to target ~/.config/git/config.local; de-dupe installer - add script/uninstall (symlinks only); document dot verbs in the README
Profile gates tooling, git identity, and secret handling so the work machine (which cannot use 1Password) never invokes op. - utils.sh: get_profile / is_personal / is_work + setup_profile (prompt once) - source of truth: ~/.config/dotfiles/profile (one word, sh- and zsh-readable) - zsh/zshenv.symlink exports DOTFILES_PROFILE early; bootstrap prompts on install
Mechanism to standardize Claude/Codex configs across machines, plus a fast config-only path separate from heavy package installs. - ai/apply.sh: content-driven symlinker; skips real files (no clobber) - .gitignore: never capture auth/sessions/history/caches/memories - script/apply + dot --apply: render+link configs, no package installs - apply.sh (config) vs install.sh (packages) convention; gnupg/ssh/ai reclassified - avoid fragile for-over-find loops (SC2044)
Setup scripts run before zsh/modern-bash exist, so target POSIX sh (validated with dash); interactive layer stays zsh. Also fixes real latent bugs found en route and unifies OS detection. - shebangs -> #!/bin/sh across setup scripts; convert [[ ]], read -rp, echo -e, set -o pipefail, process substitution - utils.sh: source /etc/os-release (was broken under dash); fix a set -u crash in the logging helpers (referenced undefined color vars on non-tty) - standardized OS layer: is_macos/is_linux/is_wsl + get_os/get_distro (no $OSTYPE); migrate all consumers; drop dead 'linux-gnu' checks and DETECTED_OS - mise install curl hardened with -fsSL; init() wired into bootstrap - document why scripts are self-contained (exec drops functions; installer.sh bypasses dot)
Folds the post-review fixes for the consolidation work: - DOTFILES fallback in every script that sources utils.sh (standalone-safe), including script/install (+ IFS-safe loop, run installers via `sh "$x"`) - script/apply setup_symlinks refreshes symlinks (skip only real files); run_installers runs script/install as a subprocess (no set/exit leakage) - vscode/install.sh: explicit per-file OS-aware links (was a broken ls|grep loop that clobbered a single target) - mise/install.sh: run full setup on fresh install (was `exit 0` after the binary); PATH the new binary; guard apt-get with `command -v` + `-y`; `[ -x ]` check; `mktemp` template - explicit `set -e` in gnupg/ai appliers (shebang -e is dropped via `sh "$x"`) - run_updates is resilient and reports failures (no silent abort); `grep -Fqx` + `tee -a >/dev/null` in setup_shell - README: document the `dot --apply` verb + fail-fast vs best-effort convention Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- drop the defined-but-unwired setup_git from bootstrap (per-profile git config.local generation will live in a git/install.sh later) - promote `safe_link` (symlink unless a real file; refresh stale symlinks; mkdir parent) into utils.sh and use it from setup_symlinks, ai/apply.sh, and gnupg/apply.sh — one implementation, and gnupg now refreshes stale links too Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
e554a7b to
b021204
Compare
Replace per-agent doc copies with a single ai/AGENTS.md linked to both ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md. Collapse codex config.d/ fragments into one config.toml (+ gitignored config.local.toml) rendered into a managed block (# >>> dotfiles:codex >>>) in ~/.codex/config.toml, preserving Codex-owned content outside the block. Teach script/uninstall to remove the new links and the managed block, and ignore AI runtime state in .gitignore.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No description provided.