Skip to content

chore: consolidate dotfiles and add AI agent configs#8

Open
kylejb wants to merge 14 commits into
mainfrom
chore/consolidate-and-ai-configs
Open

chore: consolidate dotfiles and add AI agent configs#8
kylejb wants to merge 14 commits into
mainfrom
chore/consolidate-and-ai-configs

Conversation

@kylejb

@kylejb kylejb commented May 29, 2026

Copy link
Copy Markdown
Owner

No description provided.

@kylejb kylejb requested a review from Copilot May 29, 2026 16:28
@kylejb kylejb added documentation Improvements or additions to documentation enhancement New feature or request labels May 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.sh and export DOTFILES_PROFILE early from zshenv.
  • Expand dot CLI 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). Also command -v is being used with a file path, which is not a valid way to check/install mise in 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.sh but does not set a fallback for DOTFILES, so it will fail when invoked standalone.
    gnupg/apply.sh:11
  • Likely typo: pinentry-curse doesn’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.sh sources ${DOTFILES}/utils.sh but never sets a default for DOTFILES, so it will fail when invoked standalone (and script/apply runs appliers via sh, so they should be self-contained).
    ssh/apply.sh:18
  • echo in POSIX sh does not reliably interpret \n, so the Include ...\n line is likely to be written with a literal \n in the ssh config. Use printf to emit a real newline.
    ssh/apply.sh:21
  • Same echo/newline issue for the macOS include line — \n may be literal in many sh implementations. Prefer printf for predictable output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread script/apply Outdated
Comment thread script/apply Outdated
Comment thread script/apply Outdated
Comment thread script/update Outdated
Comment thread script/bootstrap
Comment thread macos/set-defaults.sh Outdated
Comment thread linux/install.sh Outdated
Comment thread .github/README.md
Comment thread script/uninstall Outdated
Comment thread script/uninstall Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 $DOTFILES without ensuring it is set, and uses sh -c to run the installer path as a string. Per the repo’s “self-contained scripts” convention, it should set DOTFILES, source utils.sh, and execute installers via sh "$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 echo writes a literal \n into the ssh config (single quotes prevent escape expansion), which will break the Include directive. It also writes tmp_config into the current working directory. Use printf for the newline and write the temp file under src_dir.
    ssh/apply.sh:23
  • Same issue as the Linux branch above: this writes a literal \n and uses a temp file in the current working directory. Use printf and keep the temp file under src_dir.
    script/install:6
  • This runner script uses $DOTFILES without ensuring it is set, and uses sh -c to run the installer path as a string. Per the repo’s “self-contained scripts” convention, it should set DOTFILES, source utils.sh, and execute installers via sh "$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 echo writes a literal \n into the ssh config (single quotes prevent escape expansion), which will break the Include directive. It also writes tmp_config into the current working directory. Use printf for the newline and write the temp file under src_dir.
    ssh/apply.sh:23
  • Same issue as the Linux branch above: this writes a literal \n and uses a temp file in the current working directory. Use printf and keep the temp file under src_dir.

Comment thread ai/apply.sh Outdated
Comment thread script/bootstrap Outdated
Comment thread mise/install.sh Outdated
Comment thread ai/apply.sh Outdated
Comment thread script/bootstrap Outdated
Comment thread mise/install.sh Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/install isn't self-contained (it assumes $DOTFILES is already set) and it runs installers via sh -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.sh relies on command_exists uv/uv ..., but bootstrap runs in a non-interactive shell where ~/.local/bin typically is NOT on $PATH. This can cause false negatives (reinstall loops) or failures right after install. Add ~/.local/bin to PATH for 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_user is empty (unsupported OS) or the code CLI isn't on PATH, the script still calls install_extensions, which will fail under #!/bin/sh -e. Gate extension installation on both a supported OS and command_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

Comment thread script/apply
Comment thread script/update
Comment thread mise/install.sh Outdated
Comment thread mise/install.sh Outdated
Comment thread mise/install.sh
kylejb and others added 7 commits May 31, 2026 12:20
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>
@kylejb kylejb force-pushed the chore/consolidate-and-ai-configs branch from e554a7b to b021204 Compare May 31, 2026 17:39
kylejb and others added 7 commits May 31, 2026 15:06
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants