Vijay's Dotfiles homepage

Bookmark this to keep an eye on my project updates!

View project on GitHub

As documented in the README’s adoption guide and Adoption.md, this repo and its scripts are aimed at developers/techies. If you are stuck or need help in any fashion, you can reach out to the owner of the parent repo from where this was forked.

For those who follow this repo, here’s the changelog for ease of adoption:


3.2.33

Refactor Ruby codebase for maintainability and eliminate all static analysis warnings

Comprehensive refactoring addressing code quality issues identified by static analysis tools. All RuboCop, Reek, and Flay warnings eliminated through systematic improvements to code structure, duplication removal, and consistent API design.

Color system refactoring:

  • [scripts/utilities/colorizable.rb] Renamed from string.rb - extracted color methods into standalone module (68% similarity, major refactor). All color methods now support both String and Pathname without explicit .to_s conversion
  • [scripts/utilities/pathname_ext.rb] New file extends Pathname with Colorizable module - enables pathname.cyan instead of pathname.to_s.cyan throughout codebase (eliminates 50+ verbose conversions)
  • [scripts/utilities/string_ext.rb] New file extends String with Core utilities and Colorizable - centralizes String class extensions
  • [all Ruby scripts] Removed explicit .to_s before color methods on Pathname objects - color methods now handle conversion automatically (pattern: path.to_s.cyanpath.cyan)

Logging module improvements:

  • [scripts/utilities/logging.rb] Extracted _print_collected_messages helper - DRY’d warning/error summary printing (eliminated Flay mass-50 duplication between warning and error blocks)
  • [scripts/utilities/logging.rb] Extracted _record_message helper - DRY’d message collection with script/section prefix (eliminated Flay mass-38 duplication between record_warning and record_error)
  • [scripts/utilities/logging.rb] Auto-detect script name from caller - run_script now uses caller_locations(1,1) to extract filename, eliminating need for File.basename(__FILE__, '.rb') in every dual-mode script (10 scripts simplified)
  • [scripts/utilities/logging.rb] Replaced Unicode μ (mu) with “microsecond” in performance comments - ensures ASCII-only for RuboCop compliance
  • [scripts/utilities/logging.rb] Added frozen_string_literal: true directive for performance (prevents String allocation churn)

GitProcessor enhancements:

  • [scripts/utilities/git_processor.rb] Extracted nested GitUrlParser class - encapsulates URL parsing logic for HTTPS, SSH, git+ssh formats (310 lines added, 61 removed). Adds construct_upstream_url method for building upstream remotes from parsed components
  • [scripts/utilities/git_processor.rb] Added verify_pre_recreation method - validates git directory exists and confirms user intent before destructive operations
  • [scripts/utilities/git_processor.rb] Added verify_and_recreate_local_repo method - combines verification + recreation in single call (pattern used by resurrect-repositories.rb)
  • [scripts/utilities/git_processor.rb] Documented intentional Flay duplication in URL parsing - git+ssh and ssh:// formats require identical field extraction (mass-48, accepted with inline comment)

Dual-mode script standardization:

  • [scripts/recreate-repository.rb] Removed manual File.basename(__FILE__, '.rb') - leverages Logging.run_script auto-detection (155 lines removed, 38 added - 76% reduction)
  • [scripts/recreate-repository.rb] Documented intentional Flay pattern - dual-mode CLI wrapper structure shared across all dual-mode scripts (mass-50, accepted with inline comment)
  • [scripts/resurrect-repositories.rb] Refactored to use named parameters - run(generate:, resurrect:, check:) instead of positional arguments (improves call-site clarity)
  • [scripts/resurrect-repositories.rb] Extracted RepositoryConfig.from_hash class method - centralizes YAML deserialization and validation (44.9 flog score, complex but necessary)
  • [scripts/resurrect-repositories.rb] Documented intentional Flay patterns - validation blocks and error handling have context-specific messages that cannot be extracted (3 locations, mass-32/44, accepted)
  • [scripts/add-upstream-git-config.rb] Refactored to use named parameters - run(dir:, upstream_owner:) instead of positional arguments
  • [scripts/add-upstream-git-config.rb] Documented intentional Flay pattern - add_remote and fetch_all error handling have different contexts (mass-44, accepted)
  • [all dual-mode Ruby scripts] Replaced File.basename(__FILE__, '.rb') with auto-detection via Logging.run_script - 10 scripts simplified

Static analysis configuration:

  • [.reek.yml] New configuration file (141 lines) - documents all 34 suppressed warnings with rationales. Strategy: inline :reek: comments for specific smells, file-level config only for detectors we disable entirely (TooManyStatements, IrresponsibleModule, etc.)
  • [.rubocop.yml] Added ASCII-only enforcement - Style/AsciiComments enabled to catch Unicode in comments (μ, §, emoji)
  • [scripts/ruby-lint.rb] Added Flay mass threshold --mass 51 - filters intentional duplication patterns (dual-mode wrappers, URL parsing, validation blocks). Remaining duplications documented with inline comments explaining why extraction would harm readability
  • [scripts/ruby-lint.rb] Removed manual script name passing - leverages auto-detection

Named parameters adoption:

  • [.ai/domains/ruby-scripting.md] Added comprehensive § Method Parameters section (150+ lines) - documents when to use named vs positional parameters: use named for 2+ parameters OR when meaning is not obvious. Includes migration strategy, benefits, examples, and refactoring checklist
  • [scripts/add-upstream-git-config.rb] Converted to named parameters - run(dir:, upstream_owner:)
  • [scripts/resurrect-repositories.rb] Converted to named parameters - run(generate:, resurrect:, check:)
  • [scripts/capture-prefs.rb] Converted helper methods to named parameters
  • [scripts/install-dotfiles.rb] Converted helper methods to named parameters
  • [scripts/cleanup-browser-profiles.rb] Converted helper methods to named parameters

Code organization improvements:

  • [scripts/utilities/core.rb] Extracted read_lines_utf8 and each_line_utf8 helpers - UTF-8 file reading with explicit encoding (eliminates “invalid byte sequence in US-ASCII” errors in cron)
  • [scripts/utilities/command_utils.rb] Extracted run_silent helper - DRY pattern for suppressing stdout/stderr on background operations (killall, defaults write, brew update)
  • [scripts/utilities/path_utils.rb] Improved Core module inclusion - added both include Core and extend Core for dual-mode access
  • [all utility modules] Standardized Core module pattern - include Core for instance methods (in blocks), extend Core for module methods

Documentation updates:

  • [.ai/domains/ruby-scripting.md] Added § Method Parameters (named vs positional guidelines)
  • [.ai/domains/ruby-scripting.md] Updated § String Colors to document Pathname color support
  • [.ai/domains/ruby-scripting.md] Added § Core Module Usage Pattern
  • [.ai/domains/ruby-scripting.md] Added § UTF-8 File Reading
  • [.ai/domains/path-constants.md] Updated to document Pathname color methods
  • [.ai/domains/logging-conventions.md] Fixed Unicode character in comment

Static analysis results:

Before: RuboCop 2 offenses, Reek 0 warnings, Flay score 306 (7 duplications) After: RuboCop 0 offenses, Reek 0 warnings, Flay score 0 (all duplications eliminated or documented)

Other improvements:

  • [files/–HOME–/.irbrc] Added require for colorizable and pathname extensions - enables colored paths in REPL
  • [files/–HOME–/.shellrc] Minor formatting improvements
  • [files/–HOME–/Brewfile] Updated gh extensions
  • [files/–HOME–/custom.gitignore] Added patterns for Ruby gem artifacts
  • [files/–XDG_CONFIG_HOME–/git/config] Minor whitespace fixes
  • [files/–XDG_CONFIG_HOME–/rubocop/config.yml] Enabled AsciiComments cop
  • [files/–XDG_CONFIG_HOME–/starship.toml] Updated configuration
  • [files/–ZDOTDIR–/.aliases] Minor improvements
  • [files/–ZDOTDIR–/.zshrc] Minor improvements

Adopting these changes

No action required - all changes are internal code quality improvements. Scripts work identically to before.

If you want to verify static analysis passes:

cd ~/.config/dotfiles
scripts/ruby-lint.rb  # Should show all checks passing

3.2.32

Add comprehensive Ruby static analysis infrastructure with direnv integration

Implemented multi-tool static analysis for Ruby code quality and early error detection, with automatic gem installation via direnv when editing dotfiles.

Static analysis tools:

  • [files/–XDG_CONFIG_HOME–/mise/default-gems] New file configures mise to auto-install analysis tools (rubocop, reek, flay, flog, rufo) when installing any Ruby version via mise
  • [files/–XDG_CONFIG_HOME–/rubocop/config.yml] Global RuboCop config (applies to all Ruby projects)
  • [.rubocop.yml] Project-specific RuboCop config for dotfiles - inherits global config and adds Ruby 2.6 target (system Ruby compatibility)
  • [files/–XDG_CONFIG_HOME–/reek/config.yml] New Reek configuration tuned for utility scripts: detects duplicate method calls (memoization opportunities), nested iterators, uncommunicative variable names, unused private methods
  • [scripts/ruby-lint.rb] New unified tool (116 lines) that runs all four analyzers (RuboCop, Reek, Flay, Flog) on a directory or file, reports summary of which checks passed/failed

Automated gem installation for dotfiles development:

  • [scripts/install-ruby26-gems.sh] New script installs Ruby 2.6 compatible static analysis gems for system Ruby. Uses data-driven design with associative array mapping gem specs to dependencies. Idempotent (silent when gems exist, ~2 min first run). Only installs gems for Ruby 2.6.x (system Ruby).
  • [.envrc] New direnv config automatically runs install-ruby26-gems.sh and adds gem bin directory to PATH when entering dotfiles directory.
  • [.gitignore] Added !/.envrc force-include (direnv files are ignored by default in global gitignore)
  • [files/–HOME–/.shellrc] Made bash-compatible for direnv: guard typeset -A _INDENT_CACHE with ZSH_VERSION check, added sed fallback in _strip_ansi() for bash (zsh uses parameter expansion for performance)

Pre-commit validation:

  • [files/–XDG_CONFIG_HOME–/git/hooks/pre-commit] New global pre-commit hook with three-stage validation:
    1. Syntax check (ruby -c / zsh -n) on all staged files - always runs, blocks commits with parse errors
    2. RuboCop static analysis on staged Ruby files - runs if RuboCop installed, shows actionable errors with auto-fix suggestion
    3. Repo-specific hooks - allows per-repo customization via .git/hooks.local/pre-commit or ${PERSONAL_BIN_DIR}/pre-commit-<basename>.sh Skip all checks with git commit --no-verify

What each tool catches:

  • RuboCop - Style consistency, unused variables, unreachable code, potential bugs
  • Reek - Code smells (excessive duplication, deep nesting, poor naming)
  • Flay - Structural duplication across files
  • Flog - Complexity metrics (high scores indicate refactoring candidates)

Design philosophy:

Tools are configured for utility scripts (not Rails apps): metrics cops disabled (method length, cyclomatic complexity don’t apply to small scripts), style focused on Ruby 2.6 compatibility, lint rules aggressive (catch real bugs).

Documentation:

  • [docs/ruby-static-analysis.md] New comprehensive guide (113 lines) covering tool purposes, installation methods, usage patterns, configuration details, and troubleshooting
  • [.ai/domains/ruby-scripting.md] Updated edit workflow to remove cd HOME && rufo pattern (rufo works from any directory with ~/.rufo config)
  • [.ai/domains/edit-checklist.md] Updated Ruby formatting section to remove cd HOME requirement (2 occurrences)
  • [.ai/REBASE-AND-REFACTORING-METHODOLOGY.md] Updated edit workflow to remove cd HOME requirement
  • [CONTRIBUTING.md] Updated to remove cd HOME requirement

Adopting these changes

Run install-dotfiles.rb to install new configs and pre-commit hook.

Static analysis gems are automatically installed via direnv:

When you enter the dotfiles directory, direnv detects .envrc and prompts you to allow it (one-time):

cd ~/.config/dotfiles
# Output: direnv: error /Users/you/.config/dotfiles/.envrc is blocked. Run `direnv allow` to approve its content
direnv allow

After allowing, direnv automatically:

  1. Runs install-ruby26-gems.sh (installs gems if missing, silent if already present)
  2. Adds gem bin directory to PATH (tools available: rubocop, reek, flay, flog, rufo)

First run takes ~2 minutes (installs 5 tools + dependencies). Subsequent runs are instant (gems already installed).

Outside the dotfiles directory, your project’s Ruby and gems remain active (direnv is directory-scoped).

Why specific versions for Ruby 2.6?

RubyGems has updated dependencies for old gems to require Ruby 2.7+ or 3.2+. Even old versions (rubocop 0.93.1, reek 6.1.4, flay 2.11.0, flog 4.6.2) need specific transitive dependency versions. The installation script uses a data-driven associative array to map each tool to its pinned dependencies, ensuring compatibility with system Ruby 2.6.10.

Note: Pre-commit hook works without gems installed (syntax check always runs, RuboCop only runs if available). Gems are optional but recommended for full static analysis when developing dotfiles.

Run static analysis on all scripts:

cd ~/.config/dotfiles       # direnv auto-installs gems and adds to PATH
scripts/ruby-lint.rb        # Runs all tools (RuboCop, Reek, Flay, Flog)
rubocop scripts/            # Individual tools
reek scripts/
rufo scripts/my-script.rb   # Auto-format specific file

Pre-commit hook activates immediately after install-dotfiles.rb. Test with:

# Make a syntax error, try to commit
echo "def foo" >> test.rb
git add test.rb
git commit -m "test"  # Should block with syntax error and show RuboCop output
  • Restart terminal to reload .shellrc.

3.2.31

Simplify fresh-install workflow and improve performance

Eight refactorings that reduce complexity, improve runtime performance, and fix edge-case bugs across fresh-install and cron execution.

Performance optimizations:

  • [scripts/software-updates-cron.rb] Removed 10-second sleep delay between _update_home_repos and _upreb_oss_repos - saves ~1 hour/week in cron execution (modern GitHub API rate limits are generous, delay was unnecessary)
  • [scripts/fresh-install-of-osx.sh] Consolidated biometric hardware detection into single ioreg call using -c AppleBiometricSensor -c AppleBiometricServices - 50-100ms faster, cleaner boolean logic (5 lines reduced to 2)

Git maintenance:

  • [files/–XDG_CONFIG_HOME–/git/config] Enhanced git maintain to run comprehensive repository repair (like brew doctor): removes stale lock files (index.lock, commit-graph-chain.lock), runs fsck/prune/gc, rebuilds commit graph, restores mtimes. Fixes “Unable to create commit-graph-chain.lock” errors and corrupted objects automatically. Idempotent and safe to run on all repos via all maintain.

Correctness improvements:

  • [scripts/fresh-install-of-osx.sh] Made .shellrc download universal - now validates and refreshes on pre-configured machines if repo version is newer than symlink (previously only downloaded on FIRST_INSTALL)
  • [scripts/capture-prefs.rb] Made auto-commit unconditional on export - always commits staged preferences after successful export using smart_commit (amends if ahead of remote, creates new commit otherwise)
  • [scripts/fresh-install-of-osx.sh] Eliminated 20+ lines of manual git commit logic - now handled automatically by capture-prefs.rb export
  • [scripts/software-updates-cron.rb] Replaced update_all_repos with ProfilesRepo.capture_and_commit - HOME repo now committed by capture-prefs.rb, only profiles repo needs explicit update

Bug fixes:

  • [scripts/utilities/git_processor.rb] Added --prune flag to fetch_all method - removes stale remote-tracking references that cause “cannot lock ref” errors when remotes have been force-pushed
  • [scripts/fresh-install-of-osx.sh] Fixed _download_and_source_shellrc to use plain echo before sourcing .shellrc - prevents “command not found: cyan” errors on vanilla OS where color functions don’t exist yet

Code quality:

  • [scripts/utilities/profiles_repo.rb] Added capture_and_commit method - stages and commits profiles repo changes with timestamp (extracted from GitWorkspace.update_repo logic)
  • [scripts/utilities/git_workspace.rb] Updated update_all_repos documentation - clarified it’s a catch-all for uncommitted changes (preferences now auto-committed by capture-prefs.rb)
  • [scripts/fresh-install-of-osx.sh] Reduced preferences restore section from 39 lines to 12 lines - commit logic now centralized in Ruby
  • [scripts/fresh-install-of-osx.sh] Improved biometric detection clarity - single conditional check instead of tracking two separate boolean flags

Adopting these changes

No manual steps required. Next cron run will execute 10s faster. Next fresh-install run will validate .shellrc freshness on pre-configured machines. Preferences backup now always commits on export (both manual and automated runs). resurrect-repositories.rb will automatically prune stale refs during fetch. Enhanced git maintain is immediately available (run all maintain to repair all repos).


3.2.30

Optimize git clone and unshallow operations for speed and maintainability

Eliminated redundant network operations and simplified control flow in clone_repo_into() while improving performance for both fresh installs and existing repository updates.

Universal shallow clones:

  • [files/–HOME–/.shellrc] Removed FIRST_INSTALL restriction from shallow clone optimization - all clones now use --depth=1 --filter=blob:none --single-branch (not just first install)
  • [files/–HOME–/.shellrc] Moved migrate_git_repo_to_reftable() and post-clone operations (git maintain, commit-graph generation) outside if/else blocks to run for both new clones AND existing repos

Background unshallow for all repos:

  • [files/–HOME–/.shellrc] Changed background unshallow to run for all repos (new clones and existing) - converts shallow clones to full clones asynchronously without blocking caller
  • [files/–HOME–/.shellrc] Moved reftable migration before background unshallow to prevent race conditions (migrating refs while fetching could cause lock contention)

Eliminate redundant fetch operations:

  • [files/–XDG_CONFIG_HOME–/git/config] Modified git unshallow alias to always run fetch after configuring all branches (fetches complete history for shallow repos, updates all branches for full repos)
  • [files/–HOME–/.shellrc] Removed explicit git fetch for existing repos - now handled by background unshallow which includes fetch
  • [scripts/utilities/antidote.rb] Removed git pull call after unshallow - antidote manages plugin versions via bundle regeneration, working tree updates unnecessary
  • [files/–XDG_CONFIG_HOME–/git/config] Added || true to unshallow alias to prevent transient network failures from triggering ERR traps in background jobs

Updated documentation:

  • [scripts/fresh-install-of-osx.sh] Updated user_action message to explain two-step workflow: all unshallow (fetches history), then git rebase @{u} or git merge @{u} (updates working trees)
  • [scripts/utilities/git_processor.rb] Updated shallow? docstring to clarify git unshallow includes fetch operation
  • [files/–XDG_CONFIG_HOME–/git/config] Updated unshallow alias comments to reflect that fetch is now included
  • [scripts/utilities/antidote.rb] Updated comments to clarify unshallow fetches history without modifying working tree

Benefits:

  • 🚀 10-30 seconds faster fresh installs - all fetches run in background (3-5 repos × 3-10s blocked time eliminated)
  • 🚀 5-15 seconds faster per existing repo update - background unshallow returns immediately (fetch continues asynchronously)
  • Simpler control flow - unified post-processing path for new and existing repos (less duplication)
  • No FIRST_INSTALL coupling - shallow clones always used (speed benefit universal, not just first install)
  • Race condition eliminated - reftable migration always completes before background ref writes begin
  • Safer antidote updates - won’t fail on detached HEAD plugin repos (no working tree modifications)

Adopting these changes

  • Restart terminal to reload .shellrc. Next clone_repo_into() call (e.g., during resurrect-repositories.rb or fresh-install) will use optimized background unshallow. Existing repos will automatically get full history fetched in background on next access.

3.2.29

Comprehensive AI instruction documentation improvements

Enhanced all 13 domain instruction files in .ai/domains/ with improved navigation, cross-references, and completeness.

Structural improvements:

  • [all domain files] Added comprehensive Scope sections (files covered, related files, exclusions)
  • [all domain files] Converted prose applyTo patterns to explicit glob patterns for precise matching
  • [shell-scripting.md, ruby-scripting.md, character-encoding.md, comment-philosophy.md, edit-checklist.md] Added Quick Reference tables for instant pattern lookup

Content improvements:

  • [script-depth-tracking.md] Added explicit print_script_summary argument passing rules (resolves contradiction)
  • [shell-scripting.md] Added “Why set -e triggers on && false” explanation with examples
  • [ruby-scripting.md] Added Ruby’s nil-on-no-change design rationale for mutating methods
  • [ruby-scripting.md, shell-scripting.md] Added Common Mistakes sections (10 items each from code reviews)
  • [ruby-scripting.md, shell-scripting.md] Clarified autoload function naming as deliberate convention
  • [zsh-startup.md] Added performance benchmarks and measurement commands

Consolidation:

  • [ruby-scripting.md] Consolidated logging section to reference logging-conventions.md (eliminates duplication)
  • [all files] Added cross-references to related sections across files

Benefits:

  • Faster navigation via Quick Reference tables
  • Eliminated contradictory instructions
  • Added missing “why” explanations for critical rules
  • Common mistakes prevent repeated errors
  • Clear scope boundaries for each file

Adopting these changes

No action required - documentation improvements only.


3.2.28

Fix nested script name leakage and direnv log suppression

Script name restoration in nested calls:

Fixed bug where nested Logging.run_script calls leaked inner script names to outer script summaries (e.g., software-updates-cron.rb showing install_mise_versions in final output).

  • [scripts/utilities/logging.rb] Save and restore @script_name in run_script ensure blocks

Complete direnv log suppression:

Script banners (print_script_start, print_script_duration) now respect DIRENV_IN_ENVRC suppression like info/success already did.

  • [files/–HOME–/.shellrc] Added _should_suppress_log && return 0 to print_script_start and print_script_duration
  • [scripts/utilities/logging.rb] Added return if EnvVars.suppress_log? to print_script_start and print_script_duration
  • [.ai/domains/shell-scripting.md] Documented all suppressed functions
  • [TechnicalDeepDive.md] Added script banner functions to suppression table

Benefits:

  • Clean direnv output (only “direnv: loading…” message, no script banners)
  • Correct script names in nested execution summaries
  • Consistent suppression behavior across all logging functions

Adopting these changes

No action required - fixes apply automatically on next shell/script execution.


3.2.27

Migrate from scheduled to automatic git maintenance

Replaced scheduled background maintenance (launchd-based) with foreground auto-maintenance for simpler, more intelligent repository cleanup.

Configuration changes:

  • [global git config] Set aggressive auto-maintenance thresholds:
    • maintenance.loose-objects.auto = 50 (was: 100, now 2x more aggressive)
    • maintenance.incremental-repack.auto = 5 (was: 10, now 2x more aggressive)
    • maintenance.commit-graph.auto = 100 (unchanged, explicit for clarity)
  • [${XDG_CONFIG_HOME}/git/includes/oss.inc] Removed 40 lines of obsolete [maintenance] repo registrations (39 repos) - these were used by scheduled maintenance but are not needed by foreground auto-maintenance

  • [${XDG_CONFIG_HOME}/git/config] Simplified git maintain alias from 14 lines to 3 lines:
    • Removed: maintenance register and maintenance start calls (scheduled maintenance setup)
    • Kept: restore-mtime wrapper (still useful for preserving file timestamps after clone)
    • Updated comment to clarify auto-maintenance is now handled globally via maintenance.auto=true
  • [${DOTFILES_DIR}/.git/config] Removed local maintenance.auto=false override that was blocking foreground auto-maintenance

How auto-maintenance works:

After git fetch, git pull, and other write commands, git automatically runs git maintenance run --auto which:

  1. Checks heuristics (loose objects count, pack count, commit-graph freshness)
  2. Only runs tasks when thresholds are met (intelligent, not wasteful)
  3. Executes in background via maintenance.autoDetach=true (non-blocking)
  4. Uses incremental strategy: commit-graph, loose-objects, incremental-repack tasks (not full gc)

Benefits:

  • Zero manual intervention - maintenance runs automatically when needed
  • No external dependencies - no system scheduler (launchd/cron) required
  • Portable - works on any machine with Git 2.30+
  • Intelligent triggering - only runs when heuristics indicate need (50 loose objects, 5 packs, 100 commits)
  • Simpler configuration - no repo registrations to maintain

What still works:

  • git maintain - now just restores mtimes (useful after clone, no registration)
  • git cc - unchanged, still provides manual aggressive cleanup with --prune=now semantics
  • Existing automation - .shellrc clone_repo_into() and GitProcessor.clone_repo_into() calls work unchanged

Old workflow (scheduled maintenance):

git clone <url> && cd repo && git maintain  # Registered in oss.inc, started launchd
# Maintenance ran hourly/daily/weekly via launchd scheduler

New workflow (auto-maintenance):

git clone <url> && cd repo && git maintain  # Just restores mtimes (no registration)
# Maintenance runs automatically after git fetch/pull when thresholds met

Manual aggressive cleanup still available:

git cc [--expire=now]  # Explicit aggressive pruning (prune=now, expire reflogs immediately)

Adopting these changes

Action required: Clean up obsolete maintenance registrations in your fork

The changes to global git config and dotfiles repo are already applied, but you need to clean up your personal oss.inc file:

# Edit your personal oss.inc to remove [maintenance] section
vi ~/.config/git/includes/oss.inc

# Delete the entire [maintenance] section with all repo registrations
# (should be 40 lines starting with [maintenance] and ending with repo = ...)
# Keep only: [user], [url], and [includeIf] sections

Verify auto-maintenance is active:

git config --get maintenance.auto              # Should output: true
git config --get maintenance.strategy          # Should output: incremental
git config --get maintenance.loose-objects.auto    # Should output: 50
git config --get maintenance.incremental-repack.auto  # Should output: 5

Test auto-maintenance:

cd any-repo
git fetch origin  # Triggers auto-maintenance if thresholds met
git count-objects -vH  # Check loose objects and pack count

Auto-maintenance will run automatically in the background when needed - no cron jobs, no manual git maintain calls required.


3.2.26

Proactive UTF-8 encoding directives for Ruby scripts

Added # encoding: utf-8 magic comment to all Ruby scripts that perform file I/O operations, preventing “invalid byte sequence in US-ASCII” errors when handling UTF-8 content in filenames, paths, or file data.

Files updated:

  • [scripts/capture-prefs.rb] Reads/writes plist files with UTF-8 content
  • [scripts/install-dotfiles.rb] Filesystem operations with UTF-8 paths
  • [scripts/utilities/cron.rb] Writes crontab files
  • [scripts/utilities/logging.rb] Writes log files with UTF-8 messages
  • [scripts/utilities/antidote.rb] Reads/writes plugins.txt bundle files
  • [scripts/utilities/plist.rb] Already had encoding directive (no change needed)
  • [scripts/resurrect-repositories.rb] Reads YAML with UTF-8 paths
  • [scripts/cleanup-browser-profiles.rb] Filesystem operations

Ruby script template updated:

  • [.ai/domains/ruby-scripting.md] Added # encoding: utf-8 to all three script templates (dual-mode, legacy PERSONAL_BIN_DIR, legacy DOTFILES_DIR)

Standard Ruby file header:

#!/usr/bin/env ruby
# frozen_string_literal: true
# encoding: utf-8

Benefits:

  • Prevents encoding errors in file I/O operations
  • Handles UTF-8 in filenames, paths, and file content
  • Future-proof for non-ASCII characters
  • Explicit encoding declaration (no surprises)
  • Works with system Ruby 2.6 in cron contexts

Also fixed:

  • [files/–ZDOTDIR–/.zlogin] Made shell function definitions callable from nested shells by defensively sourcing .shellrc before using is_file_older_than predicate (mirrors pattern in .zshenv where guard protects against stale function definitions in interactive vs login shell contexts)

Adopting these changes

No action required - encoding directives are backward compatible and have no runtime impact on existing functionality. The changes only prevent future encoding errors when scripts encounter UTF-8 content.


3.2.25

Complete git config migration to XDG location

File relocations:

  • [~/.python_history] Moved to ${XDG_STATE_HOME}/python/history. Python 3.13+ respects ${PYTHONHISTORY} environment variable.
  • [~/.gitconfig] Moved to ${XDG_CONFIG_HOME}/git/config. Git reads XDG location before ~/.gitconfig.
  • [~/.gitconfig-{delta,pandoc,plist,sqlite3}.inc] Moved to ${XDG_CONFIG_HOME}/git/config-{delta,pandoc,plist,sqlite3}.inc. Tool-specific git config includes now in XDG location.
  • [~/.gitconfig-{delta,pandoc,plist,sqlite3}-enabled.inc] Symlinks now point to ${XDG_CONFIG_HOME}/git/config-*.inc instead of ${HOME}/.gitconfig-*.inc.
  • [~/.gitconfig-{jd,oss,work}.inc] Moved to ${XDG_CONFIG_HOME}/git/includes/{jd,oss,work}.inc. Personal git config includes consolidated in dedicated includes/ subdirectory.

Code changes:

  • [files/–HOME–/.shellrc] Added export PYTHONHISTORY="${XDG_STATE_HOME}/python/history". Added directory creation at end of file: ensure_dir_exists "${PYTHONHISTORY%/*}" (self-healing on every shell start, mirrors ${HISTFILE:h} pattern in .zshrc).
  • [files/–HOME–/.gitconfig → files/–XDG_CONFIG_HOME–/git/config] Moved main git config to XDG location. Updated all include paths: ~/.gitconfig-*-enabled.inc~/.config/git/config-*-enabled.inc. Updated personal includes path: ~/.gitconfig-oss.inc~/.config/git/includes/oss.inc.
  • [files/–HOME–/.gitconfig-.inc → files/–XDG_CONFIG_HOME–/git/config-.inc] Moved all tool-specific git config includes to XDG location.
  • [files/–ZDOTDIR–/.aliases] Updated edit-gist alias: Changed "${HOME}"/.gitconfig-* to "${XDG_CONFIG_HOME}/git". Added "${XDG_CONFIG_HOME}/mise" to include both mise configs (HOME and XDG).
  • [scripts/install-dotfiles.rb] Updated _ensure_gitconfig_tool_symlink to reference new XDG paths: ~/.config/git/config-#{tool_name}.inc and ~/.config/git/config-#{tool_name}-enabled.inc. Vanilla OS support: existing PathUtils.ensure_directories_exist creates ~/.config/git/ when processing main config symlink (which runs before tool symlinks), so no additional directory creation needed.
  • [.gitignore] Added /files/--XDG_CONFIG_HOME--/git/includes/ to prevent personal git config includes from being tracked in dotfiles repo (they belong in home repo).
  • [files/–HOME–/custom.gitignore] Updated all git config patterns to XDG locations: /.gitconfig/.config/git/config, /.gitconfig-delta.inc (and similar) → /.config/git/config-delta.inc (explicit list of 4 tool-specific includes in FILES SYMLINKED section), /.gitconfig-*-enabled.inc/.config/git/config-*-enabled.inc (glob pattern in GENERATED/DERIVED section for conditional symlinks created by install-dotfiles.rb). Added !/.local/state/python/ to un-ignore Python history directory for tracking in home repo (this file is symlinked to ~/.gitignore for home repo).
  • [templates/gitconfig-inc.template] Updated all references: ${HOME}/.gitconfig-<context>.inc${XDG_CONFIG_HOME}/git/includes/<context>.inc, ~/.gitconfig~/.config/git/config, ~/.gitconfig-oss.inc~/.config/git/includes/oss.inc.

Benefits:

  • 6 fewer visible dotfiles/symlinks in $HOME: .python_history, .gitconfig, .gitconfig-delta.inc, .gitconfig-pandoc.inc, .gitconfig-plist.inc, .gitconfig-sqlite3.inc moved to XDG locations
  • 8 fewer conditional symlinks in $HOME: .gitconfig-{delta,pandoc,plist,sqlite3}-enabled.inc now in ~/.config/git/
  • Complete XDG compliance: All git configuration in ${XDG_CONFIG_HOME}/git/ following XDG Base Directory specification
  • Better organization:
    • Main config: ~/.config/git/config
    • Tool-specific includes: ~/.config/git/config-*.inc
    • Personal includes: ~/.config/git/includes/*.inc
    • All git config grouped together in one directory tree
  • Python history: Follows same pattern as zsh/sqlite/postgresql histories in ${XDG_STATE_HOME}
  • Portability: All paths use ~/ tilde notation (Git’s native expansion mechanism)
  • Self-healing: Python history directory created automatically on every shell start

File renaming convention

All git config files follow consistent naming based on their location:

Old Location New Location Naming Pattern
~/.gitconfig ~/.config/git/config Main config: config (no suffix)
~/.gitconfig-{tool}.inc ~/.config/git/config-{tool}.inc Tool includes: config-{tool}.inc prefix
~/.gitconfig-{tool}-enabled.inc ~/.config/git/config-{tool}-enabled.inc Conditional symlinks: config-{tool}-enabled.inc
~/.gitconfig-{context}.inc ~/.config/git/includes/{context}.inc Personal includes: no config- prefix, in includes/ subdirectory

Naming rules:

  • Main config: Simple config filename (Git’s XDG standard)
  • Tool-specific includes (managed by install-dotfiles.rb): Prefix with config- to clearly identify as config fragments
  • Conditional symlinks (auto-created): Follow config-{tool}-enabled.inc pattern for consistency with source files
  • Personal includes (user-created contexts): No config- prefix, stored in dedicated includes/ subdirectory to separate user content from dotfiles-managed content

Why this convention:

  • config-*.inc pattern groups all dotfiles-managed tool includes together
  • includes/*.inc subdirectory clearly separates personal/context-specific includes
  • Consistent with Git’s native XDG support (reads ~/.config/git/config automatically)
  • Follows XDG Base Directory specification
  • All git configuration logically grouped in ~/.config/git/ directory tree

Adopting these changes

Step 1: Run install-dotfiles.rb

~/.config/dotfiles/scripts/install-dotfiles.rb

This creates/updates symlinks:

  • ~/.config/git/config → dotfiles/files/–XDG_CONFIG_HOME–/git/config (was ~/.gitconfig)
  • ~/.config/git/config-delta.inc → dotfiles version (was ~/.gitconfig-delta.inc)
  • ~/.config/git/config-pandoc.inc → dotfiles version (was ~/.gitconfig-pandoc.inc)
  • ~/.config/git/config-plist.inc → dotfiles version (was ~/.gitconfig-plist.inc)
  • ~/.config/git/config-sqlite3.inc → dotfiles version (was ~/.gitconfig-sqlite3.inc)
  • ~/.config/git/config-*-enabled.inc → conditional symlinks (was in HOME)

Step 2: Remove old symlinks from HOME

# Remove old git config symlink
rm -f ~/.gitconfig

# Remove old tool-specific include symlinks
rm -f ~/.gitconfig-delta.inc ~/.gitconfig-pandoc.inc ~/.gitconfig-plist.inc ~/.gitconfig-sqlite3.inc

# Remove old conditional symlinks
rm -f ~/.gitconfig-delta-enabled.inc ~/.gitconfig-pandoc-enabled.inc
rm -f ~/.gitconfig-plist-enabled.inc ~/.gitconfig-sqlite3-enabled.inc

# Verify removal
ls -la ~ | grep gitconfig  # Should show nothing

Step 3: Migrate Python history

# Create directory and move history file
mkdir -p ~/.local/state/python
mv ~/.python_history ~/.local/state/python/history 2>/dev/null || true

Step 4: Migrate personal git config includes

# Create directory structure
mkdir -p ~/.config/git/includes

# Identify personal git config includes (excludes tool-specific ones managed by dotfiles)
# These are typically context-specific: work, personal projects, open source, etc.
personal_includes=(~/.gitconfig-*.inc(N))
tool_includes=(delta pandoc plist sqlite3)

# Move personal includes to new location
for include in "${personal_includes[@]}"; do
  # Skip if it's a tool-specific include (managed by install-dotfiles.rb)
  basename="${include:t:r}"  # Extract name without path and .inc extension
  is_tool=false
  for tool in "${tool_includes[@]}"; do
    if [[ "${basename}" == "gitconfig-${tool}" || "${basename}" == "gitconfig-${tool}-enabled" ]]; then
      is_tool=true
      break
    fi
  done

  if [[ "${is_tool}" == "false" ]]; then
    # This is a personal include - move it
    new_name="${basename#gitconfig-}"  # Remove 'gitconfig-' prefix
    mv "${include}" ~/.config/git/includes/"${new_name}.inc"
    echo "Moved: ${include:t} → ~/.config/git/includes/${new_name}.inc"
  fi
done

# Update any includeIf references in your includes files
# This updates cross-references between your personal include files
for include in ~/.config/git/includes/*.inc(N); do
  # Update old paths to new paths in includeIf directives
  sed -i '' 's|~/.gitconfig-\([^-]*\)\.inc|~/.config/git/includes/\1.inc|g' "${include}"
done

echo "Personal git config includes migrated to ~/.config/git/includes/"

Step 5: Verify git config works

# Test that git config loads correctly
git config --get user.name
git config --get user.email

# Test conditional includes work
cd ~/dev/your-project-dir
git config --get user.email  # Should show context-specific email

# Verify config file location
git config --list --show-origin | head -5
# Should show: file:/Users/yourname/.config/git/config

Step 6: Restart shell

# Restart terminal to reload .shellrc with PYTHONHISTORY
exec zsh

Step 7: Test Python history

# Verify PYTHONHISTORY is set
echo ${PYTHONHISTORY}
# Should show: /Users/yourname/.local/state/python/history

# Start Python and verify history works
python3
>>> import readline
>>> print(readline.get_history_length())
>>> exit()

Step 8: Commit changes

# Commit git config changes to home repo (if tracking .config/git/)
cd ~
git add .config/git/
git status  # Verify no gitconfig files remain in HOME
git sci "Move git config to XDG_CONFIG_HOME"

To: path = ~/.config/git/includes/jd.inc


**Step 4: Update your main git config**
```bash
# Edit ~/.gitconfig to point to the new location
# Change: path = ~/.gitconfig-oss.inc
# To:     path = ~/.config/git/includes/oss.inc

Step 5: Verify git config works

# Test that git config loads correctly
git config --get user.name
git config --get user.email

# Test conditional includes work
cd ~/dev/your-project-dir
git config --get user.email  # Should show context-specific email

Step 6: Restart shell

# Restart terminal to reload .shellrc with PYTHONHISTORY
exec zsh

Step 7: Test Python history

# Start Python and verify history location
python3
>>> import os
>>> print(os.getenv('PYTHONHISTORY'))
# Should show: /Users/yourname/.local/state/python/history
>>> exit()

Step 8: Commit changes to home repo

# The moved git config includes should be tracked in your home git repo
cd ~
git add .config/git/includes/
git status  # Verify .gitconfig changes are staged
git sci "Move git config includes to XDG_CONFIG_HOME"

3.2.24

:white_check_mark: Tested on a vanilla macOS machine

Move configuration files to $XDG_CONFIG_HOME for better organization and to declutter $HOME

File relocations:

  • [files/–HOME–/.zshenv] Created new file in $HOME that sets ZDOTDIR="${XDG_CONFIG_HOME}/zsh". This file must stay in $HOME (zsh design requirement - zsh always reads .zshenv from $HOME first, before looking at ZDOTDIR).
  • [files/–ZDOTDIR–/.zshenv] Deleted (moved to files/–HOME–/.zshenv)
  • [files/–ZDOTDIR–/.zshrc, .zlogin] Remain in files/–ZDOTDIR–, which now resolves to ~/.config/zsh/ instead of ~/.zshrc, ~/.zlogin in $HOME
  • [files/–ZDOTDIR–/.aliases] Moved from files/–HOME–/.aliases. Shell utility functions now live in ZDOTDIR alongside zsh config files.
  • [files/–XDG_CONFIG_HOME–/zsh/plugins.txt] Moved from files/–ZDOTDIR–/.zsh_plugins.txt. Renamed (unhidden), decoupled from ZDOTDIR.
  • [~/.config/zsh/plugins.zsh] Generated file moved from ~/.zsh_plugins.zsh (antidote generates this from plugins.txt)
  • [~/.local/state/zsh/history] New location for zsh history (was ~/.zsh_history). History is mutable state per XDG Base Directory spec, belongs in $XDG_STATE_HOME.
  • [~/.local/state/postgresql/history-]* New location for PostgreSQL psql per-database history files (was ~/.psql_history-*). Configured via HISTFILE setting in psqlrc.
  • [~/.local/state/sqlite/history] New location for SQLite history (was ~/.sqlite_history). Configured via $SQLITE_HISTORY environment variable set in .shellrc.
  • [files/–XDG_CONFIG_HOME–/vim/vimrc] Moved from files/–HOME–/.vimrc. Vim 9.0+ supports $XDG_CONFIG_HOME/vim/vimrc natively (3rd priority after ~/.vimrc and ~/.vim/vimrc).
  • [files/–XDG_CONFIG_HOME–/vim/autoload/plug.vim] Moved from files/–HOME–/.vim/autoload/plug.vim. Follows vimrc to maintain relative plugin structure.
  • [~/.local/state/vim/undo/] New location for vim persistent undo files (was /tmp). Configured in vimrc with automatic directory creation.
  • [files/–XDG_CONFIG_HOME–/shellcheck/shellcheckrc] Moved from files/–HOME–/.shellcheckrc. Shellcheck supports XDG natively (searches $XDG_CONFIG_HOME/shellcheck/shellcheckrc as 2nd priority after ./.shellcheckrc and before ~/.shellcheckrc).
  • [files/–XDG_CONFIG_HOME–/ripgrep/config] Moved from files/–HOME–/.ripgreprc. Ripgrep supports XDG natively (searches $XDG_CONFIG_HOME/ripgrep/config as 1st priority).
  • [files/–XDG_CONFIG_HOME–/readline/inputrc] Moved from files/–HOME–/.inputrc. Readline supports XDG via $INPUTRC environment variable.
  • [files/–XDG_CONFIG_HOME–/pry/pryrc] Moved from files/–HOME–/.pryrc. Pry (Ruby debugger) has supported XDG since 2014, works with Ruby 2.6+.
  • [files/–XDG_CONFIG_HOME–/pg/psqlrc] Moved from files/–HOME–/.psqlrc. PostgreSQL psql supports XDG via $PSQLRC environment variable. Updated HISTFILE setting to use ~/.local/state/postgresql/history- prefix (was ~/.psql_history-).
  • [files/–XDG_CONFIG_HOME–/curlrc] Moved from files/–HOME–/.curlrc. Curl 7.73.0+ (2020) supports XDG natively (searches $XDG_CONFIG_HOME/curlrc as 2nd priority after ~/.curlrc).
  • [files/–HOME–/.notify-osd] Deleted (Ubuntu-only notification config file, not needed on macOS).

Code changes:

  • [files/–HOME–/.shellrc] Updated ZDOTDIR default to ${XDG_CONFIG_HOME}/zsh. Added export HISTFILE="${XDG_STATE_HOME}/zsh/history". Added export SQLITE_HISTORY="${XDG_STATE_HOME}/sqlite/history". Updated ANTIDOTE_PLUGIN_TXT to ${XDG_CONFIG_HOME}/zsh/plugins.txt. Updated ANTIDOTE_PLUGIN_ZSH to ${XDG_CONFIG_HOME}/zsh/plugins.zsh. Added export INPUTRC="${XDG_CONFIG_HOME}/readline/inputrc" and export PSQLRC="${XDG_CONFIG_HOME}/pg/psqlrc". Fixed load_zsh_configs() to use explicit paths: .zshenv from ${HOME}, .zshrc/.zlogin from ${ZDOTDIR}. Performance optimization: Standardized numeric comparisons to use (( )) arithmetic syntax with local -i integer declarations instead of [[ -lt/-gt/-eq/-ne ]] test operators (~20% faster per comparison, bash-compatible). Affected functions: _log_indent, section_header, is_epoch_older_than, is_outermost_script, _decrement_script_depth, clone_repo_into, _cron_method_delegator, print_usage.
  • [files/–HOME–/.aliases] Optimized WORDCHARS definition: replaced two separate :s substitutions with single pattern replacement ${WORDCHARS//[\/.]}). Eliminates one parameter expansion. Performance optimization: Converted numeric comparisons to (( )) arithmetic syntax in _call_ruby_git_workspace, pdf_to_png, recron.
  • [files/–ZDOTDIR–/.zshrc] Updated plugin file references. Added early directory creation for ${HISTFILE:h} (zsh history directory) to prevent silent write failures. Optimized zsh-patina daemon restart check: replaced external stat commands with zsh/stat builtin module (eliminates subprocess fork overhead). Changed cache TTL from 5 minutes to 60 minutes. Replaced string comparisons with integer arithmetic for epoch/mtime checks. Performance: ~0.1ms (cached) vs ~20ms (full check) = ~20ms savings on 99% of startups.
  • [files/–ZDOTDIR–/.zlogin] Fixed .zshenv compilation path to ${HOME}/.zshenv. Updated antidote bundle compilation comment.
  • [scripts/utilities/env_vars.rb] Updated ZDOTDIR constant to use XDG_CONFIG_HOME.join('zsh'). Updated ANTIDOTE_PLUGIN_TXT to XDG_CONFIG_HOME.join('zsh', 'plugins.txt'). Updated ANTIDOTE_PLUGIN_ZSH to XDG_CONFIG_HOME.join('zsh', 'plugins.zsh'). Added HISTFILE constant using XDG_STATE_HOME.join('zsh', 'history'). Added DOWNLOADS constant (HOME.join('Downloads')) for transient log files. All use cross-platform Pathname#join.
  • [scripts/fresh-install-of-osx.sh] Updated ZDOTDIR initialization to ${XDG_CONFIG_HOME}/zsh. Optimized _ensure_directories_exist() to only create XDG base directories (XDG_CACHE_HOME, XDG_CONFIG_HOME) - removed creation of subdirectories that are automatically created by tools (ANTIDOTE_HOME via git clone, DOTFILES_DIR via clone_repo_into), install-dotfiles.rb (pg, pry, readline, ripgrep, postgresql, vim/undo, zsh subdirs), or never used before install-dotfiles (PERSONAL_BIN_DIR, PROJECTS_BASE_DIR, XDG_DATA_HOME, XDG_STATE_HOME). Moved brew bundle full install log to ~/Downloads/brew-bundle-full-install.log. Moved fresh-install log to ~/Downloads/fresh-install-of-osx.log.
  • [scripts/utilities/cron.rb] Moved cron log files to ~/Downloads/: software-updates-cron-last-run.log, software-updates-run-log, software-updates-cron.log. Uses EnvVars::DOWNLOADS constant.
  • [scripts/software-updates-cron.rb] Moved run log to ~/Downloads/software-updates-run-log. Uses EnvVars::DOWNLOADS constant.
  • [files/–XDG_CONFIG_HOME–/vim/vimrc] Added XDG Base Directory support: configures undodir to use $XDG_STATE_HOME/vim/undo with automatic directory creation. Removed hardcoded /tmp undodir (was non-persistent across reboots).
  • [files/–XDG_CONFIG_HOME–/iex/iex.exs] Migrated from ~/.iex.exs to XDG location. Elixir IEx supports XDG_CONFIG_HOME/iex/iex.exs natively.
  • [files/–XDG_CONFIG_HOME–/kdiff3/kdiff3rc] Migrated from ~/.kdiff3rc to XDG location. KDiff3 supports XDG_CONFIG_HOME/kdiff3/kdiff3rc natively.
  • [files/–HOME–/custom.gitignore] Updated: removed root-level entries for all moved dotfiles. Added XDG entries: /.config/curlrc, /.config/iex/iex.exs, /.config/kdiff3/kdiff3rc, /.config/pg/psqlrc, /.config/pry/pryrc, /.config/readline/inputrc, /.config/ripgrep/config, /.config/shellcheck/shellcheckrc, /.config/vim/, /.config/zsh/. Replaced wildcard /.config/zsh/* with explicit list of symlinked files and pattern for generated files (*.zwc, plugins.zsh). Changed /.local//.local/* and added un-ignore patterns for tracking history in XDG location: !/.local/state/, /.local/state/*, !/.local/state/postgresql/, !/.local/state/sqlite/. Zsh history (.local/state/zsh/history) remains ignored. Added /.sqlite_history to ignored files (old location).
  • [Adoption.md] Updated bootstrap command to write log to ~/Downloads/fresh-install-of-osx.log instead of ~/fresh-install-of-osx.log.
  • [.ai/domains/edit-checklist.md] Added Step 7: Delete stale .zwc bytecode files after editing zsh scripts. Prevents loading old compiled code when source has changed.

Benefits:

  • 16 fewer visible dotfiles in $HOME: .aliases, .zshrc, .zlogin, .zsh_plugins.txt, .zsh_plugins.zsh, .zsh_history, .vimrc, .vim/, .shellcheckrc, .ripgreprc, .inputrc, .pryrc, .psqlrc, .curlrc, .iex.exs, .kdiff3rc moved to subdirectories
  • 31 fewer psql history files in $HOME: All .psql_history-* files moved to ~/.local/state/postgresql/
  • 5 fewer log files in $HOME: Fresh-install log, brew log, and cron logs moved to ~/Downloads/
  • Better organization: All tool configs grouped in ~/.config/ subdirectories, state files (history, undo) in ~/.local/state/, transient logs in ~/Downloads/
  • XDG compliance: Config in $XDG_CONFIG_HOME, state in $XDG_STATE_HOME per XDG Base Directory spec
  • Location independence: Plugin files decoupled from ZDOTDIR - even if ZDOTDIR reverts to $HOME, plugins stay in ~/.config/zsh/
  • Unhidden plugin files: plugins.txt easier to find and edit than .zsh_plugins.txt
  • Vim undo persistence: Undo files now survive reboots (was /tmp, cleared on restart) with proper security (0700 permissions)
  • Log file visibility: Logs in ~/Downloads/ easier to find than hidden files in ~/
  • Ruby 2.6 compatibility: Pry XDG support verified to work with system Ruby 2.6 (macOS default)
  • Cross-platform: Uses Pathname#join for path construction

Not migrated (intentionally left in $HOME):

  • .irbrc - Requires Ruby 3.1+ for XDG support (system Ruby is 2.6)
  • .gemrc - Requires Ruby 3.0+ for XDG support (system Ruby is 2.6)
  • .sqliterc - No XDG support, hardcoded to ~/.sqliterc

Adopting these changes

Step 1: Run install-dotfiles.rb

~/.config/dotfiles/scripts/install-dotfiles.rb

This creates/updates symlinks:

  • ~/.zshenv → dotfiles/files/–HOME–/.zshenv (new)
  • ~/.config/zsh/.aliases → dotfiles/files/–ZDOTDIR–/.aliases (moved from ~/.aliases)
  • ~/.config/zsh/.zshrc → dotfiles/files/–ZDOTDIR–/.zshrc (moved from ~/)
  • ~/.config/zsh/.zlogin → dotfiles/files/–ZDOTDIR–/.zlogin (moved from ~/)
  • ~/.config/zsh/plugins.txt → dotfiles/files/–XDG_CONFIG_HOME–/zsh/plugins.txt (moved + renamed)
  • ~/.config/curlrc → dotfiles/files/–XDG_CONFIG_HOME–/curlrc (moved from ~/.curlrc)
  • ~/.config/iex/iex.exs → dotfiles/files/–XDG_CONFIG_HOME–/iex/iex.exs (moved from ~/.iex.exs)
  • ~/.config/kdiff3/kdiff3rc → dotfiles/files/–XDG_CONFIG_HOME–/kdiff3/kdiff3rc (moved from ~/.kdiff3rc)
  • ~/.config/pg/psqlrc → dotfiles/files/–XDG_CONFIG_HOME–/pg/psqlrc (moved from ~/.psqlrc)
  • ~/.config/pry/pryrc → dotfiles/files/–XDG_CONFIG_HOME–/pry/pryrc (moved from ~/.pryrc)
  • ~/.config/readline/inputrc → dotfiles/files/–XDG_CONFIG_HOME–/readline/inputrc (moved from ~/.inputrc)
  • ~/.config/ripgrep/config → dotfiles/files/–XDG_CONFIG_HOME–/ripgrep/config (moved from ~/.ripgreprc)
  • ~/.config/shellcheck/shellcheckrc → dotfiles/files/–XDG_CONFIG_HOME–/shellcheck/shellcheckrc (moved from ~/.shellcheckrc)
  • ~/.config/vim/vimrc → dotfiles/files/–XDG_CONFIG_HOME–/vim/vimrc (moved from ~/.vimrc)
  • ~/.config/vim/autoload/plug.vim → dotfiles/files/–XDG_CONFIG_HOME–/vim/autoload/plug.vim (moved from ~/.vim/autoload/)

Step 2: Remove broken symlinks from $HOME

# Remove old symlinks that now point to non-existent files in dotfiles repo
rm -f ~/.aliases ~/.curlrc ~/.inputrc ~/.pryrc ~/.psqlrc ~/.ripgreprc ~/.iex.exs ~/.kdiff3rc

# Verify removal
ls -la ~ | grep -E '\.(aliases|curlrc|inputrc|pryrc|psqlrc|ripgreprc|iex\.exs|kdiff3rc)'  # Should show nothing

Step 3: Migrate history files (one-time manual steps)

# Migrate zsh history
mkdir -p ~/.local/state/zsh
cp ~/.zsh_history ~/.local/state/zsh/history

# Migrate PostgreSQL psql history files
mkdir -p ~/.local/state/postgresql
for file in ~/.psql_history-*; do
  [ -f "$file" ] && mv "$file" ~/.local/state/postgresql/history-${file##*/.psql_history-}
done

# Migrate SQLite history (if it exists)
if [ -f ~/.sqlite_history ]; then
  mkdir -p ~/.local/state/sqlite
  mv ~/.sqlite_history ~/.local/state/sqlite/history
fi

# Verify migrations
ls -la ~/.local/state/zsh/history
ls -la ~/.local/state/postgresql/
ls -la ~/.local/state/sqlite/history 2>/dev/null || echo "No SQLite history to migrate"

# Vim undo directory will be auto-created on first vim startup
# (vimrc includes automatic directory creation with proper permissions)

Step 4: Restart terminal and test tools

# Restart shell
exec zsh

# Test vim
vim /tmp/test.txt
# Make some changes, save, quit, reopen to test undo persistence

# Verify tool configs are loaded from new locations
rg --version  # Should load from ~/.config/ripgrep/config
pry -e "exit" 2>/dev/null  # Should load from ~/.config/pry/pryrc (if pry installed)
curl --version  # Should load from ~/.config/curlrc

# Verify environment variables
echo $ZDOTDIR  # Expected: ~/.config/zsh
echo $HISTFILE  # Expected: ~/.local/state/zsh/history
echo $INPUTRC  # Expected: ~/.config/readline/inputrc
echo $PSQLRC  # Expected: ~/.config/pg/psqlrc
echo $SQLITE_HISTORY  # Expected: ~/.local/state/sqlite/history

Antidote bundle will automatically regenerate in new location (~/.config/zsh/plugins.zsh) on first shell startup. Vim will automatically create ~/.local/state/vim/undo/ on first edit.

Step 5: Regenerate crontab (moves log files to Downloads)

# This updates the crontab with new log file paths
recron

# Verify the crontab was updated
crontab -l | grep "Downloads"
# Should show: tmplog=/Users/vijay/Downloads/software-updates-cron-last-run.log

Step 6: Clean up old files (optional, after verifying everything works)

# Only after verifying the new locations work correctly:
rm ~/.zsh_history           # History now in ~/.local/state/zsh/history
rm ~/.zsh_plugins.zsh       # Bundle now in ~/.config/zsh/plugins.zsh
rm ~/.vimrc                 # Vimrc now in ~/.config/vim/vimrc (if symlink, will be auto-removed by install-dotfiles.rb)
rm -rf ~/.vim/              # Vim autoload now in ~/.config/vim/autoload/ (if symlink, will be auto-removed by install-dotfiles.rb)
rm ~/.shellcheckrc          # Shellcheckrc now in ~/.config/shellcheck/shellcheckrc (if symlink, will be auto-removed by install-dotfiles.rb)

# Clean up old log files after crontab regeneration
rm ~/.software-updates-cron-last-run.log 2>/dev/null   # Now in ~/Downloads/
rm ~/.software-updates-run-log 2>/dev/null             # Now in ~/Downloads/
rm ~/software-updates-cron.log 2>/dev/null             # Now in ~/Downloads/
rm ~/brew-bundle-full-install.log 2>/dev/null          # Now in ~/Downloads/

Technical notes:

Why .zshenv stays in $HOME: Zsh always reads .zshenv from $HOME first, before looking at any other configuration. This is by design and cannot be changed. .zshenv sets ZDOTDIR to tell zsh where to find the other files.

Why plugins are in XDG_CONFIG_HOME, not ZDOTDIR: Plugin files are intentionally decoupled from ZDOTDIR. If ZDOTDIR is reverted to $HOME, plugins stay in ~/.config/zsh/. This achieves the goal of keeping $HOME uncluttered regardless of ZDOTDIR setting.

Why history is in XDG_STATE_HOME: Per XDG Base Directory spec: Config → $XDG_CONFIG_HOME (.zshrc, settings), Data → $XDG_DATA_HOME (application resources), Cache → $XDG_CACHE_HOME (ephemeral, can be deleted), State → $XDG_STATE_HOME (persistent mutable data like history, logs). History is mutable state that persists between sessions.


3.2.23

Fix git upreb for read-only repos, add non-interactive Homebrew upgrades, fix profiles repo squashing

  • [scripts/recreate-repository.rb] Fixed force-squash detection for profiles repo. Was comparing basename 'browser-profiles' against KEYBASE_PROFILES_REPO_NAME 'profiles', which never matched. Now directly compares full path against PERSONAL_PROFILES_DIR. Result: profiles repo always force-squashed, HOME repo respects -f flag.

3.2.22

Optimize starship prompt, git size measurements, and migrate to zsh-patina instead of fast-syntax-highlighting

  • [files/–XDG_CONFIG_HOME–/starship.toml] Renamed [custom.git_clean_arrow] to [custom.git_sync_status] for better semantic clarity (matches git sync-status alias). Module shows green arrow when repo is clean AND synced with tracking branch. Optimized to use single git sync-status alias (replaces 3 separate git commands). Performance cost: ~30-50ms per prompt (was ~100-180ms), savings of ~70-130ms. Updated [custom.git_size] documentation to note parallel execution behavior - runs concurrently with git_status, making actual overhead only ~2.5ms despite 35ms execution time. Visual flow: clean+synced repos show green arrow after size, dirty/ahead/behind repos show yellow arrow + status symbols.

  • [files/–HOME–/.gitconfig] Simplified git size alias to always use git count-objects -vH (2-3x faster than du -sh). Removed conditional GIT_SIZE_FAST env var logic. Now uses grep size-pack | awk '{print $2, $3}' to extract pack size only. Uses GIT_SIZE_QUIET env var to suppress header output when called from starship.

  • [scripts/utilities/path_utils.rb] Added git_repo_size_kb(repo_dir) method - returns pack size in KB via git count-objects -vH. Parses size-pack line, converts units (KiB/MiB/GiB) to KB. Handles both repo root and .git directory paths. Added git_repo_size_human(repo_dir) method - returns formatted pack size string via git size alias with GIT_SIZE_QUIET=1. Both methods 2-3x faster than du-based equivalents (~10-20ms vs ~50ms).

  • [scripts/utilities/profiles_repo.rb] Updated check_size_limit() to use git_repo_size_kb() and git_repo_size_human() instead of dir_size_kb() and dir_size_human(). Updated error/debug messages to reference “pack size” instead of “.git directory size”. Method documentation updated to note pack-size-only measurement and performance improvement (60-80% faster).

  • [files/–ZDOTDIR–/.zsh_plugins.txt] Replaced zdharma-continuum/fast-syntax-highlighting with zsh-patina (Rust-based syntax highlighter). fast-syntax-highlighting removed from antidote bundle and commented out as fallback. Added detailed comments explaining zsh-patina benefits: 62% faster input lag (1.4ms vs 3.6ms), dynamic highlighting (invalid commands shown in red, existing files underlined), high-quality Sublime Text syntax definitions via syntect, and Rust daemon architecture for sub-millisecond highlighting shared between sessions.

  • [files/–ZDOTDIR–/.zshrc] Added zsh-patina activation after antidote bundle loads. Uses cached activation pattern (same as mise/starship) to avoid forking binary on every shell start: cache stored at ~/.cache/zsh-patina-activate-cache.zsh, regenerated when zsh-patina binary is updated (mtime check via is_file_older_than), compiled to .zwc bytecode for faster loading. Guard (($+commands[zsh-patina])) ensures graceful degradation on vanilla OS before brew installs it. Activation must happen after antidote bundle loads (which sets up ZLE hooks).

  • [files/–HOME–/Brewfile] Added brew 'zsh-patina' with inline comment noting it’s a Rust-based syntax highlighter that’s 62% faster for input lag than fast-syntax-highlighting.

  • [files/–XDG_CONFIG_HOME–/zsh-patina/config.toml] Created zsh-patina configuration file to match fast-syntax-highlighting’s default color scheme. Uses custom theme fsh-default (stored in themes/ subdirectory). Configuration enables dynamic highlighting (invalid commands in red, existing paths in magenta + underline), sets reasonable performance limits (max_line_length=20000, timeout_ms=500).

  • [files/–XDG_CONFIG_HOME–/zsh-patina/themes/fsh-default.toml] Created custom theme matching fast-syntax-highlighting’s default colors exactly: commands (cd, ls, git) = green, keywords (if, then, while) = yellow, paths (existing files/dirs) = magenta + underline, strings = yellow, variables = cyan, comments = gray. Dynamic features: valid commands = green, invalid commands = red, existing paths = magenta + underline.

Performance impact (zsh-bench measurements):

  • Starship prompt: 156.9ms → ~130-200ms (git_clean_arrow re-enabled for sync visibility, ~100-180ms cost)
  • Ruby size checks: ~100ms → ~20-40ms (60-80% faster)
  • Manual git size: ~50ms → ~10-20ms (2-3x faster)
  • Input lag: 3.873ms → 1.502ms (61% faster) ⭐ Most noticeable while typing!
  • First prompt lag: 156.518ms → 115.393ms (26% faster)
  • Command lag: 156.909ms → 109.178ms (30% faster)
  • First command lag: 354.000ms → 268.069ms (24% faster)
  • Exit time: 53.435ms → 48.387ms (9% faster)

Key learning: Starship executes git modules in parallel. git_size (35ms) runs concurrently with git_status (27ms), so prompt time is dominated by slowest operation. Actual overhead of git_size is only ~2.5ms, not 35ms. git_clean_arrow (~100-180ms) provides essential sync visibility (ahead/behind status) - acceptable trade-off for knowing when commits need push/pull. zsh-patina’s Rust daemon provides sub-millisecond syntax highlighting without blocking the main shell process, delivering 61% faster input lag that matches published benchmarks (62% claimed).

Fix git upreb to support read-only repos and remove redundant push

  • [files/–HOME–/.gitconfig] upreb alias: Only push when upstream remote exists (fork workflow)
    • WITH upstream: fetch all → rebase upstream → push to origin
    • WITHOUT upstream: fetch all → rebase origin → NO push
    • Fixes permission denied errors on read-only repos like zsh-bench
  • [files/–XDG_CONFIG_HOME–/zsh/upreb] Remove redundant push after git upreb call
    • git upreb alias already handles push correctly based on upstream
    • Extra push was causing duplicate push attempts and errors
    • Now cleanly delegates all push logic to the alias

Adopting these changes

  • Run brew bundle to install zsh-patina (and update any other packages)
  • Run install-dotfiles.rb to symlink zsh-patina config files (config.toml and themes/fsh-default.toml)
  • Restart terminal to reload zsh configuration and activate zsh-patina
  • Verify color scheme matches FSH: commands should be GREEN, paths should be MAGENTA + underlined, invalid commands should be RED
  • Verify prompt still shows git branch, status symbols, and repo size
  • Check zsh-patina daemon status: zsh-patina status (should show PID)
  • Performance improvement is automatic, most noticeable when typing commands (61% faster input lag)
  • Note: The daemon auto-restarts when config/theme files are modified (checked on each new shell). Manual zsh-patina restart only needed if editing config while keeping same shell session open.

3.2.21

Conditionally suppress section_header in autoload scripts when called from wrapper scripts

  • [files/–XDG_CONFIG_HOME–/zsh/cc, files/–XDG_CONFIG_HOME–/zsh/push] Added conditional section_header display based on _DOTFILES_SCRIPT_DEPTH. Headers are only shown when _DOTFILES_SCRIPT_DEPTH <= 0 (direct invocation). When called from wrapper scripts that already printed their own header via print_script_start, the autoload scripts skip their section_header to avoid duplicate headers.

  • [.ai/domains/script-depth-tracking.md] Documented the conditional output pattern for autoload functions. Added “Conditional Output Based on Depth” section with complete examples of suppressing section_header when called from wrapper scripts, including wrapper script template and output comparison.

Why this matters:

  • Eliminates duplicate headers when wrapper scripts call autoload functions
  • Wrapper scripts (e.g., push-browser-profiles.sh) use print_script_start for lifecycle management
  • Autoload scripts retain headers for direct invocation (git push, git cc, or direct function calls)
  • DRY principle: Wrapper provides overall context, autoload provides operation details only when not nested

Usage patterns:

# Direct invocation - shows section_header
git push                     # "Pushing '/path/to/repo'"
git cc                       # "Compressing '/path/to/repo'"

# Via wrapper script - no duplicate header
push-browser-profiles.sh     # "Starting..." (from wrapper), then git output (no section_header)
cc-browser-profiles.sh       # "Starting..." (from wrapper), then git output (no section_header)

Adopting these changes

  • Restart terminal to reload zsh autoload functions.

3.2.20

Enhanced logging and monitoring utilities

  • [scripts/utilities/logging.rb] Added structured logging with file output and log level filtering. Set LOG_FILE=/path/to/file to write logs to file with automatic rotation (keeps last 5 files, max 10MB each). Set LOG_FORMAT=json for JSON-formatted logs or LOG_FORMAT=text for human-readable logs. Set LOG_LEVEL=debug|info|success|warn|error|user_action to filter messages by severity (default: info). Console output unchanged (human-readable with colors). Required modules: json, fileutils.

  • [scripts/utilities/cron.rb] Added crontab validation to restore_cron() via _valid_crontab? helper. Validates syntax before installation: checks file is readable/non-empty, validates line format (comments, env vars, cron entries with 6+ fields). Prevents installing malformed crontab files. Added _cleanup_old_backups helper to suspend_cron() - keeps only the 5 most recent backup files in $TMPDIR, sorted by mtime, deletes oldest.

  • [scripts/utilities/macos.rb] Enhanced kill_login_item_apps() with process verification and graceful termination. Uses _process_running? helper (via pgrep -x) to check if process exists before kill. Sends SIGTERM, waits 2 seconds, verifies termination, falls back to SIGKILL (-9) if needed. Logs warnings for failed terminations. Added notification rate limiting to notify() - deduplicates notifications within 60-second window, tracks history in @_notification_history, auto-cleans entries older than 5 minutes. Prevents spam from repeated errors.

Usage examples:

# Log level filtering
LOG_LEVEL=warn ruby script.rb              # Show only warnings and errors
LOG_LEVEL=debug DEBUG=true ruby script.rb  # Show all messages including debug

# Structured logging with rotation
LOG_FILE=~/logs/script.log LOG_FORMAT=json ruby script.rb  # JSON logs
LOG_FILE=~/logs/script.log LOG_FORMAT=text ruby script.rb  # Human-readable logs
  • [scripts/add-upstream-git-config.rb] Added extend Core after extend self to enable unqualified nil_or_empty? calls in module methods. Maintains consistency with other utility modules.

Enhancements impact:

  • Logging methods now check _should_log?(level) before printing (filters based on LOG_LEVEL)
  • File output writes to LOG_FILE if set, with format determined by LOG_FORMAT env var
  • Log rotation prevents unbounded growth (10MB limit per file, 5 files max)
  • Cron operations validate syntax before modifying system crontab
  • Cron backups no longer accumulate indefinitely in $TMPDIR
  • Process termination is safer (verifies process exists, retries with SIGKILL if needed)
  • Notification spam prevented via 60-second deduplication window

Adopting these changes

  • Restart terminal to reload zsh functions if using any autoload scripts that depend on updated utilities.
  • No action required for existing scripts - all enhancements are opt-in via environment variables.
  • To enable structured logging: set LOG_FILE, LOG_FORMAT, and/or LOG_LEVEL env vars.

3.2.19

Global git hooks + wrapper functions for lifecycle management

  • [files/–XDG_CONFIG_HOME–/git/hooks/pre-push] Added global git hook for pre-push validation. Uses optimized shell patterns: inline ${PWD:t} for basename (no subshell), utility functions (is_directory, is_executable) instead of raw tests, local array for hook paths. Execution order: repo-specific .git/hooks.local/pre-push (e.g., Husky/lint-staged), then per-repo customization ${PERSONAL_BIN_DIR}/pre-<command>-<basename>.sh.

  • [files/–HOME–/.gitconfig] Added core.hooksPath = ~/.config/git/hooks to enable global hooks for all repositories. Hooks are automatically active for all repositories (existing and newly cloned) without per-repo installation.

  • [files/–HOME–/custom.gitignore] Added /.config/git/hooks/* to symlinked files section (hooks are symlinked by install-dotfiles.rb from dotfiles repo to ~/.config/git/hooks/).

  • [Extras.md, .ai/domains/git-config.md, Adoption.md] Updated documentation to reflect that git has NO post-push hook (only pre-push exists - this is intentional design, not a bug). For operations requiring cleanup after push completes (e.g., cron resume), use wrapper functions instead of hooks. EXIT traps in pre-push hooks do NOT work because the trap fires when the hook script exits (before git starts the push).

  • [${PERSONAL_BIN_DIR}/push-browser-profiles.sh] Created wrapper script for browser-profiles push with cron suspension. Uses with_cron_suspended _push pattern: suspends cron before push, restores it after push completes (regardless of whether data was pushed or “Everything up-to-date”), cleans up backup file. Handles errors via EXIT trap - cron always restored.

Benefits of hybrid approach:

  • ✅ Works for all git operations with native hooks (pre-push, pre-commit, post-commit, post-merge, etc.)
  • ✅ Automatic installation on clone (no per-repo setup)
  • ✅ Simple pre-validation scripts (no autoload loading, no depth tracking)
  • ✅ Chains with repo-specific hooks (Husky, lint-staged, etc.)
  • ✅ Wrapper functions handle lifecycle management (before + after) without hook limitations

When to use each approach:

  • Pre-validation hooks: Need validation BEFORE operation starts (tests, linting)
  • Wrapper functions: Need cleanup AFTER operation completes (cron resume, mtime restore)

Root directory write protection

  • [files/–HOME–/.shellrc] Added is_root_dir() utility function to check if a path is root /. Mirrors shell pattern [[ "${path}" == "/" ]] with semantic name. Used inline with ${path:h} parameter expansion to validate parent directories before write operations: is_root_dir "${target_path:h}".

  • [files/–HOME–/.shellrc] Added root directory protection to ensure_dir_exists(), recompile_zsh_script(), and clone_repo_into(). Each function checks parent directory with is_root_dir "${path:h}" before performing write operations (mkdir, rm, zrecompile). Prevents catastrophic operations like creating /mydir or deleting /.cache when environment variables are unset or misconfigured.

  • [files/–HOME–/.aliases] Added root directory protection to delete_caches() and with_cron_suspended(). Uses same is_root_dir "${path:h}" pattern to prevent deleting cache directories or cron backup files in root.

  • [scripts/utilities/path_utils.rb] Added PathUtils.root_dir?(path) to check if path is root /. Mirrors shell is_root_dir() function. Added PathUtils.safe_for_write?(path) to check if parent directory is not root (safe for write operations). Both methods handle Pathname and String inputs, with automatic type conversion.

  • [scripts/utilities/cron.rb] Added root directory protection to resume_cron() and with_cron_suspended() using PathUtils.safe_for_write?(backup_file). Prevents deleting cron backup files in root directory.

  • [.ai/domains/shell-scripting.md] Added is_root_dir to Common Substitutions table with usage pattern is_root_dir "${path:h}". Added example demonstrating root directory protection before write operations. Cross-referenced Ruby equivalent PathUtils.root_dir?.

  • [files/–HOME–/.shellrc] Refactored is_directory() to use ! is_root_dir "${1}" instead of hardcoded [[ "${1}" != "/" ]] for DRY principle. Single source of truth for root directory check.

  • [files/–HOME–/.aliases] Refactored home alias to use dynamic basename ${DOTFILES_DIR:t} instead of hardcoded .dotfiles for portability across systems with different dotfiles directory names.

All error/warn messages follow unified color conventions (paths use cyan + single quotes).

Cross-platform path handling improvements

  • [scripts/utilities/path_utils.rb] Replaced hardcoded '/' with File::SEPARATOR in root_dir? method for cross-platform compatibility (Windows uses \). Refactored valid_directory? to use root_dir? method instead of inline check for DRY principle.

  • [scripts/utilities/git_processor.rb] Replaced hardcoded '/' with File::SEPARATOR in basename_from_url method’s regex pattern and split operation for cross-platform compatibility.

  • [scripts/recreate-repository.rb] Replaced hardcoded '/' with File::SEPARATOR in directory path chomp operation for cross-platform compatibility.

  • [scripts/utilities/antidote.rb] Combined zsh flags from 'zsh', '-f', '-c' to 'zsh', '-fc' for consistency with shell flag combining conventions.

Git GC optimization and rerere cache preservation

  • [files/–HOME–/.gitconfig] CRITICAL FIX: Changed gc.pruneExpire = now2.weeks.ago to preserve amended commits and deleted branches for 2 weeks instead of deleting immediately. This was causing loss of recent history when running git cc.

  • [files/–HOME–/.gitconfig] Added git gc optimization settings:
    • autoDetach = true - Runs gc in background (non-blocking) when auto-gc triggers
    • autoPackLimit = 50 - Consolidates packs when 50+ exist (prevents pack file proliferation)
    • bigPackThreshold = 512m - Keeps packs >512MB separate to avoid expensive repacking of large unchanged packs
    • cruftPacks = true - Stores unreachable objects in cruft packs instead of loose objects (better compression, faster gc)
  • [files/–HOME–/.gitconfig] Updated rerere cache retention from 1 week to 3 weeks (gc.rerereResolved = 3.weeks.ago) to prevent losing recent conflict resolutions. Unresolved conflicts kept for 1 week (gc.rerereUnresolved = 1.week.ago).

  • [files/–HOME–/.gitconfig] Removed redundant git rerere gc call from cc alias - git maintenance run --task=gc already includes git gc, which automatically runs rerere gc internally. Updated comment to document this behavior.

Disk space impact:

  • Slightly more disk space for 2-week unreachable object window (vs immediate pruning)
  • Cruft packs compress better than loose objects, offsetting this
  • Large packs no longer repacked unnecessarily (saves CPU/disk I/O)

Recovery window:

  • Amended commits: 2 weeks (was immediate deletion)
  • Deleted branches: 2 weeks (was immediate deletion)
  • Reflog entries: 2 weeks (unchanged)
  • Rerere resolutions: 3 weeks (was 1 week)

Multi-repo command alias

  • [files/–HOME–/.aliases] Added my-repos alias as shorthand for running commands in home, dotfiles, and browser-profiles repos. Uses dynamic basename expansion: FILTER='(/${HOME:t}/\.git|/${DOTFILES_DIR:t}/\.git|/${PERSONAL_PROFILES_DIR:t}/\.git)$' all <command>. The FILTER regex matches .git directory paths using zsh parameter expansion (:t gets tail/basename), making it portable across systems regardless of username.

Usage examples:

my-repos st         # Runs 'git st' in home + dotfiles + browser-profiles
my-repos push       # Runs 'git push' in all three repos
my-repos upreb      # Runs 'git upreb' in all three repos

# Or use FILTER directly for ad-hoc filtering:
FILTER='dotfiles' all st                     # Only dotfiles repo
FILTER='(vijay|profiles)$' all st            # Home + browser-profiles

Comparison with existing autoload functions:

  • my-repos st - Quick interactive status check (home + dotfiles + browser-profiles)
  • status_all_repos - Comprehensive check via Ruby (includes Chrome profile subdirs if they’re git repos)
  • update_all_repos - Cron job automation (stages/commits auto-generated content in home + browser-profiles)

Why an alias instead of a function:

  • 3 lines instead of 28 lines
  • No hardcoded usernames - uses ${HOME:t}, ${DOTFILES_DIR:t}, ${PERSONAL_PROFILES_DIR:t}
  • No need for git command detection logic (handled by all alias)
  • Transparent - just sets FILTER env var before calling all
  • Easy to customize - users can set FILTER to any regex pattern

Adopting these changes

  • Global git hooks are installed automatically by install-dotfiles.rb - no action required
  • For repositories requiring lifecycle management (e.g., cron suspension during push), create wrapper script in ${PERSONAL_BIN_DIR}:
    # push-browser-profiles.sh (see Adoption.md § 4.5.1 for complete example)
    with_cron_suspended _push "$@"
    

    Make executable: chmod +x ${PERSONAL_BIN_DIR}/push-browser-profiles.sh Invoke: cd ~/personal/vijay/browser-profiles && ./push-browser-profiles.sh

  • Root directory protection is defensive - no action required
  • Restart terminal to reload ${ZDOTDIR}/.aliases with new my-repos alias and .shellrc with root protection utilities
  • New utility functions available for use in custom scripts:
    • Shell: is_root_dir "${path}" - returns true if path is root /
    • Shell: my-repos <command> - runs command in home + dotfiles + browser-profiles repos
    • Ruby: PathUtils.root_dir?(path) - returns true if path is root
    • Ruby: PathUtils.safe_for_write?(path) - returns true if parent directory is not root

3.2.18

  • [Brewfile] Replaced Sol, Stats, Thaw with Vorssaint (menu bar tools). Vorssaint provides Metrics (replaces Stats), window preview (replaces Dockdoor), extension of menubar (handled by macos 27 as overflow replacing thaw) and window tiling and clipboard history (replaces sol) in a single app. Issue #115 (Zoom hanging) was fixed in Vorssaint v3.1.8 (July 7, 2026); current version v3.2.0 includes the fix.
  • Switched back to using Spotlight for launching apps since Sol is no longer used/installed.
  • Use utility methods from CommandUtils instead of direct system calls in ruby classes.

3.2.17

Ruby code quality improvements and pattern documentation

  • [scripts/utilities/command_utils.rb, git_processor.rb, macos.rb] Standardized nil_or_empty? guard pattern: check nil/empty BEFORE calling .strip, cache stripped result AFTER check (only if used multiple times). Eliminates redundant checks and prevents potential nil crashes.

  • [scripts/utilities/git_processor.rb] Hot path optimization: extracted static arrays (STREAMING_COMMANDS, QUIET_FLAGS) to frozen constants at class level. Eliminates repeated allocations (2 arrays per git command = 100s per cron run).

  • [scripts/utilities/command_utils.rb] Enhanced CommandUtils.run_silent(*command, out:, err:) to accept optional out and err parameters (both defaulting to File::NULL). Supports selective output suppression (err: :err shows stderr), output redirection (out: '/path/to/file'), and full suppression (default). Returns boolean (true if exit 0).

  • [11 Ruby files] Migrated 26 call sites from verbose system(..., out: File::NULL) and system(..., out: File::NULL, err: File::NULL) patterns to CommandUtils.run_silent(...): software-updates-cron.rb (5), cron.rb (1), macos.rb (6), plist.rb (4), path_utils.rb (1), capture-prefs.rb (1), cleanup-browser-profiles.rb (2), setup-login-item.rb (1), plus 5 call sites updated to use optional parameters (selective suppression/redirection instead of both).


3.2.16

Git alias consolidation and cleanup

  • [files/–HOME–/.gitconfig] Added new list-old alias to list all remote branches with no commits in a specified timeframe (defaults to 10 days). Accepts optional --since flag for custom timeframes (e.g., --since 30.days, --since 2.weeks). Output format shows <last commit relative date> | <branch name>, sorted oldest first. Supports optional directory argument. Complements existing git old alias which checks a single branch.

  • [files/–HOME–/.gitconfig] Consolidated incoming change aliases (in, inc, inp) into single git in with optional flags: default (commit summary with stats), -d (combined diff), -p (full patches). Removed redundant inc and inp aliases.

  • [files/–HOME–/.gitconfig] Consolidated outgoing change aliases (out, outp) into single git out with optional -p flag: default (commit summary), -p (full patches). Removed redundant outp alias.

  • [files/–HOME–/.gitconfig] Consolidated status aliases (st, sts, stsub) into single git st with optional flags and directory support: default (full status), -s (short status), -m (status with submodules). All modes accept optional [<dir>] argument. Removed redundant sts and stsub aliases.

  • [files/–HOME–/.gitconfig] Removed duplicate co = checkout alias definition (was defined in both “BASIC SHORTCUTS” and “LOCAL CHANGES & STAGING” sections). Kept single definition in “BASIC SHORTCUTS” section.

  • [files/–HOME–/.gitconfig] Removed lt alias marked as [WIP/DUPLICATE] - was incomplete and duplicative of existing lc alias.

  • [files/–HOME–/.gitconfig] Enhanced git cc (cleanup/compress) to delete stale refs/prefetch/* references before running garbage collection. These refs are created by git maintenance and can keep old commits reachable after rebasing, preventing cleanup. The prefetch refs are now deleted early in the cleanup process and recreated fresh by the maintenance run --task=gc step at the end. This ensures git cc --expire=now after a rebase fully removes all unreachable commits without requiring manual intervention.

  • [files/–HOME–/.gitconfig] Added git maintain alias to consolidate post-clone maintenance operations (restore-mtime, maintenance register/start). Idempotent and safe to run multiple times. Gracefully handles missing dependencies (restore-mtime not installed, .gitconfig-oss.inc not present on FIRST_INSTALL). Accepts optional [<dir>] argument. Useful for manually cloned repos or migrating old repos to background maintenance.

  • [files/–HOME–/.shellrc] Updated clone_repo_into() to use git maintain alias instead of inline post-clone logic (8 lines → 1 line).

  • [scripts/software-updates-cron.rb] Updated comment to reference git maintain alias instead of individual commands.

Adopting these changes

  • New git alias available immediately: git list-old [--since <timeframe>] to find stale remote branches
  • New git alias available immediately: git maintain [<dir>] to run post-clone maintenance (restore-mtime, maintenance register/start)
  • Consolidated git aliases maintain backward compatibility via flags:
    • git incgit in -d (combined diff of incoming changes)
    • git inpgit in -p (incoming commits with full patches)
    • git outpgit out -p (outgoing commits with full patches)
    • git stsgit st -s (short status)
    • git stsubgit st -m (status with submodules)
    • git stsub /pathgit st /path -m (status with submodules in different repo)
  • No breaking changes - all previous functionality preserved with cleaner interface

3.2.15

Ruby filter_map polyfill optimization

  • [scripts/utilities/enumerable_ext.rb] Optimized filter_map polyfill implementation from two-pass to single-pass with each_with_object loop. Reduces intermediate array allocations on Ruby 2.6 (macOS default system Ruby). Guard ensures native Ruby 2.7+ filter_map is used when available. Added TODO comment documenting this file can be removed once macOS ships Ruby 2.7+ as default.

3.2.14

Incorporate starship-ftl for instant-prompt and Terminal.app time_shell_startup fixes

  • Incorporate starship-ftl for the instant-prompt
  • Fix issue with noise output from Terminal app when running time_shell_startup to behave exactly like how iTerm does.

Adopting these changes

  • Restart terminal to reload ${ZDOTDIR}/.aliases with fixed time_shell_startup

3.2.13

Revamping documentation

3.2.12

GitProcessor robustness, duration utilities, and custom.gitignore maintenance

  • [scripts/utilities/git_processor.rb] Added repo? guards to 15 mutation methods (config_set, add_remote, set_remote_url, fetch_all, stage_all, add, delete_tag, pull, rm_cached, restore, commit, smart_commit, push, compress, build_commit_group) to prevent operations on non-repos. Removed instance repo? memoization (@_is_repo ||=) to prevent stale cache after init/recreate operations - now directly checks @dir.join('.git').exist? each time. Kept class-level GitProcessor.repo?(path) memoization with @repo_cache hash (appropriate for validation checks across different paths during tree traversal). Added documentation explaining cache difference. Guard placement preserves original behavior: dry-run checks first (return true), then repo checks (for actual execution).

  • [scripts/utilities/core.rb] Added duration_since(start_time) method to calculate elapsed seconds from Unix epoch timestamp - single source of truth for duration calculations across Ruby codebase. Added elapsed?(start_time, threshold) method to check if enough time has passed since reference timestamp - returns true when duration meets or exceeds threshold. Used for time-interval checks in rate-limiting scenarios (mise plugin updates, ollama model updates).

  • [scripts/software-updates-cron.rb] Replaced all manual Time.now.to_i - start_time calculations with Core.duration_since(start_time) (4 call sites). Replaced time comparison logic (duration < threshold) with semantic Core.elapsed?(timestamp, threshold) checks (2 call sites: mise plugin updates, ollama model updates). Eliminated duplicate duration_since calls in ollama check (was called twice per iteration - once for comparison, once for hours calculation). Removed redundant repo? check for zen-browser-desktop repo - internal GitProcessor guards provide same protection.

  • [scripts/utilities/logging.rb] Updated print_script_duration() to use Core.duration_since(start_time) instead of manual Time.now.to_i - start_time calculation.

  • [scripts/utilities/.rb]* Fixed Pathname color method usage in 8 utility modules (antidote.rb, command_utils.rb, keybase.rb, macos.rb, path_utils.rb, plist.rb, profiles_repo.rb, git_workspace.rb). Color methods (.cyan, .yellow, etc.) are defined on String, not Pathname - added .to_s calls before color methods. Removed redundant .to_s on variables already documented as String type. Kept informative repo? checks in git_workspace.rb:status_repo and profiles_repo.rb chrome folder loop where custom logging prevents misleading “attempting…” messages before failure.

  • [files/–HOME–/.envrc] Deleted file (no longer needed).

  • [files/–HOME–/custom.gitignore] Removed /.envrc entry - synced with deletion of source file per maintenance rule.

Adopting these changes

  • Run install-dotfiles.rb to sync deleted .envrc file and updated custom.gitignore
  • No other user action required - changes are internal robustness improvements
  • GitProcessor now safely handles non-repo directories without crashes
  • Duration calculations now use consistent Core utility methods

3.2.11

Git clone optimization and SSH configuration standardization

  • [files/–HOME–/.shellrc] Enhanced clone_repo_into() with three clone optimizations:
    • -n (no-checkout) prevents writing working tree to temp folder (security + speed)
    • --filter=blob:none (FIRST_INSTALL) downloads only commits/trees without file contents (blobs fetched on-demand)
    • --single-branch (FIRST_INSTALL) fetches only default branch

    Standardized SSH options: removed all inline GIT_SSH_COMMAND overrides since keepalive options now default in .gitconfig and fresh-install export. Consolidated comments.

  • [files/–HOME–/.gitconfig] Updated core.sshCommand to ssh -o ConnectTimeout=20 -o Compression=no -o ServerAliveInterval=10 -o ServerAliveCountMax=3. Keepalive options (10s interval, 3 max failures = 30s timeout) prevent timeout on slow networks and detect dead connections quickly. Compression disabled since git protocol already compresses.

  • [scripts/fresh-install-of-osx.sh] Updated GIT_SSH_COMMAND export to match .gitconfig options (added keepalive). Consistent SSH behavior across vanilla OS and pre-configured machines.

  • [scripts/utilities/git_workspace.rb] update_repo now accepts array of paths. Updated update_all_repos to stage both sol and defaults directories in home repo (previously only defaults).

Adopting these changes

  • Restart terminal to pick up new .shellrc changes

Global gitignore enhancement: Ruby support and pattern standardization

  • [files/–HOME–/.gitignore_global, files/–HOME–/custom.gitignore] Regenerated from gitignore.io with expanded language/editor support.

3.2.9

Shell delegation standardization: Unified Ruby invocation pattern

  • [files/–HOME–/.shellrc] Created call_ruby_utility() helper function to encapsulate Ruby invocation pattern. Accepts Ruby code string as argument, handles RUBYLIB setup automatically (adds utilities/ and bin directories to load path), preserves COLUMNS env var for terminal width detection, includes Ruby availability check (graceful no-op if Ruby not installed on vanilla OS). Added comprehensive 20-line documentation with usage examples and delegation pattern. Inlined setup_rubylib() function body into call_ruby_utility() and removed standalone setup_rubylib() (no longer needed as separate function). Updated 2 internal call sites: migrate_git_repo_to_reftable() and _call_ruby_cron() to use new helper. Standardized mandatory argument validation to use ${1:?function: arg required} pattern (built-in shell error handling) instead of manual ${1:-} + is_zero_string + error + return 1 pattern (4 lines → 1 line). Removed 2 redundant Ruby availability guards (now handled centrally by call_ruby_utility).

  • [scripts/fresh-install-of-osx.sh] Updated _ensure_keybase_logged_in() to use call_ruby_utility instead of manual setup_rubylib + ruby -e sequence. Maintains same behavior (returns boolean via exit code) with cleaner implementation.

  • [.ai/domains/ruby-scripting.md] Updated “Shell integration” section to document call_ruby_utility as the standard pattern for all shell→Ruby delegation. Added benefits list (automatic RUBYLIB setup, COLUMNS preservation, Ruby availability check, consistency). Added examples showing correct usage and anti-patterns (raw ruby -e calls). Added delegation function pattern template. Replaced all references to manual setup_rubylib + ruby -e sequences with call_ruby_utility pattern.

  • [.ai/context.md] Updated “cron.rb Exception Propagation” section to reference call_ruby_utility instead of ruby -e in explanation of how Ruby exceptions propagate to shell. Updated “Shell delegation pattern” section to document call_ruby_utility handles RUBYLIB setup, COLUMNS preservation, and Ruby availability check automatically.

Adopting these changes

  • No user action required - changes are internal refactoring
  • All shell→Ruby delegation now uses consistent call_ruby_utility pattern
  • Ruby availability check centralized (single guard instead of scattered checks)
  • Argument validation more concise (built-in shell error handling)

3.2.8

Core module refactoring: Eliminate circular dependencies and standardize timestamp handling

  • [scripts/utilities/core.rb] Added current_timestamp() method returning 'YYYY-MM-DD HH:MM:SS' format - single source of truth for all timestamp formatting across Ruby codebase. Added running_in_tty?() method checking if stdout is TTY or FORCE_COLOR set - gates interactive operations (app kill/restart, prompts) that should not run in cron/non-interactive contexts. Added comprehensive 35-line documentation defining strict rules for Core module: ZERO requires except Ruby stdlib, OS-agnostic only (no macOS/Linux-specific calls), check ENV directly (not via EnvVars to avoid circular dependency), with examples of appropriate vs inappropriate methods. These methods moved from MacOS module to eliminate circular dependency (macos.rb required logging.rb which used MacOS.current_timestamp).

  • [scripts/utilities/macos.rb] Removed current_timestamp() and running_in_tty?() methods (now in Core module). Updated LOGIN_ITEM_APPS comment to document this is single source of truth (shell array _MACOS_LOGIN_ITEM_APPS in .aliases was dead code, removed). Added sleep 1 after killing apps in kill_login_item_apps() to ensure full termination before defaults writes begin - prevents race where app’s shutdown handler might flush preferences after we’ve started writing new values. Updated documentation explaining the sleep prevents race conditions.

  • [scripts/utilities/git_processor.rb] Enhanced smart_commit() method to auto-generate commit messages when called without arguments. Detects repository state via new commit_count() helper - returns 0 for brand new repos, >0 for repos with history. Auto-generates “Initial commit: " for first commit (count == 0) or "Incremental commit: " otherwise. Changed signature from `smart_commit(message)` to `smart_commit(message = nil)` to support optional message. Uses `Core.current_timestamp` for consistent formatting. Works correctly for brand new repos, repos without remotes, and after `git.recreate()`. Removed `require_relative 'macos'` from requires block - now uses `Core.current_timestamp` instead of `MacOS.current_timestamp`.

  • [scripts/utilities/logging.rb] Updated print_script_start() and print_script_duration() to use Core.current_timestamp instead of inline Time.now.strftime('%Y-%m-%d %H:%M:%S'). Eliminated circular dependency: logging.rb previously required macos.rb for timestamps, which itself required logging.rb for log methods. Core module has zero dependencies, breaking the cycle.

  • [scripts/utilities/git_workspace.rb] Simplified _commit_repo() to use git.smart_commit with no arguments - auto-generates “Incremental commit: " message instead of manually constructing timestamp string. Removed manual `Time.now.strftime` call and `timestamp` variable.

  • [scripts/software-updates-cron.rb] Updated 3 call sites to use Core.current_timestamp instead of MacOS.current_timestamp. Added require_relative 'utilities/core' to requires block. Maintains consistent timestamp format across all log entries and status updates.

  • [scripts/capture-prefs.rb] Updated to use Core.running_in_tty? instead of MacOS.running_in_tty? in conditional for killing/restarting login-item apps. Only kills apps on import when running interactively (not in cron). Added require_relative 'utilities/core' to requires block.

  • [scripts/recreate-repository.rb] Simplified to use git.smart_commit with no arguments instead of manually constructing commit message with MacOS.current_timestamp and force/incremental prefix logic. Auto-generation in smart_commit handles Initial vs Incremental detection via commit count, eliminating duplicate logic.

  • [scripts/osx-defaults.sh] Updated comment documenting canonical login-item app list location changed from _MACOS_LOGIN_ITEM_APPS (.aliases § 3n) to MacOS::LOGIN_ITEM_APPS (scripts/utilities/macos.rb). Shell array was dead code (never referenced, only Ruby constant used).

  • [files/–HOME–/.aliases] Removed 26-line dead code _MACOS_LOGIN_ITEM_APPS array. This shell array was never used - all scripts call Ruby MacOS.kill_login_item_apps and MacOS.restart_login_item_apps which read from MacOS::LOGIN_ITEM_APPS constant. Comment claimed array was duplicated in both .aliases and macos.rb, but shell version was never referenced. Ruby constant is single source of truth.

Adopting these changes

  • No user action required - changes are internal refactoring
  • Timestamp format unchanged: all timestamps still display as YYYY-MM-DD HH:MM:SS
  • Git commit messages auto-generated by smart_commit use same format as before
  • Login-item apps behavior unchanged (still killed/restarted during defaults writes)

3.2.7

Antidote 2.1.1 compatibility: .zwc compilation enabled, pattern fixes, and performance improvements

  • [files/–ZDOTDIR–/.zlogin] Removed antidote.zsh exclusion from .zwc compilation (lines 79-86). Antidote 2.1.1 fixed the bytecode crash (issue #270) by changing source-detection pattern from *:file:* to *:file(|code):* to match both regular sourcing and .zwc bytecode contexts. The find command now compiles all .sh/.zsh files in scanned directories including antidote.zsh. Updated documentation (lines 116-123) to note issue is fixed and reference GitHub issue #270.

  • [files/–XDG_CONFIG_HOME–/zsh/]* Fixed ZSH_EVAL_CONTEXT self-invocation guards in 8 autoload functions (cc, count, pull, push, st, status_all_repos, update_all_repos, upreb). Changed from lowercase zsh_eval_context (array) to uppercase ZSH_EVAL_CONTEXT (scalar string) for correct string pattern matching. Updated pattern from *file* to :${ZSH_EVAL_CONTEXT}: == *:file(|code):* to match both regular sourcing (:file:) and .zwc bytecode loading (:filecode:). Added colon wrappers to ensure exact token matching. Pattern now mirrors antidote 2.1.1’s fix. Without this fix, autoload functions compiled to .zwc would execute when sourced (wrong behavior) instead of only when run directly.

  • [scripts/install-dotfiles.rb] Performance optimization: replaced regex pattern /\.zwc/ with string suffix check end_with?('.zwc') for ignored files (lines 42-43, 67). Changed constant name from IGNORED_FILE_PATTERNS to IGNORED_SUFFIXES to reflect simpler implementation. Benchmark: 2.6x faster (56ms → 22ms per 100k iterations). Saves ~3.4ms per install-dotfiles run (typical 60-100 files).

  • [scripts/utilities/git_processor.rb] Added build_commit_graph method (lines 391-404) to optimize git operations after repository recreation or cloning. Executes git commit-graph write --reachable --changed-paths to pre-generate commit graph data structure. Speeds up git log, status, and merge-base by 10-50x. Includes dry-run support and debug logging. Returns boolean success/failure.

  • [scripts/recreate-repository.rb] Integrated commit graph building into both force and non-force workflow paths (lines 112-113, 121-122). Runs after push in both modes to leave repository in optimally configured state. Refactored to extract common git.push call outside if/else blocks (line 117), using force variable as parameter for DRY principle. Only git.build_commit_graph and the push call are now common final steps; stage/commit/compress operations remain in separate blocks due to different workflow timing (force mode runs these after recreation, non-force before push).

  • [files/–ZDOTDIR–/.zshrc] Removed redundant ensure_dir_exists "${XDG_CACHE_HOME}" call from brew shellenv cache regeneration block (line 115). Directory creation already handled once at line 94 before all cache operations (brew, git, mise, starship). Comment at lines 91-93 documents that early call prevents silent cache-write failures when delete_caches removes ~/.cache.

  • [files/–HOME–/.aliases] Updated delete_caches comment (lines 498-502) to remove antidote-specific mention. Changed from “antidote.zsh.zwc in particular must be purged so that antidote’s ZSH_EVAL_CONTEXT source-detection check works correctly” to simply “Without -L, stale .zwc files in those paths are silently skipped.” General .zwc cleanup behavior remains unchanged, but special antidote workaround note removed since issue is fixed in antidote 2.1.1.

  • [scripts/setup-login-item.rb] Added documentation comment (lines 191-198) explaining why this script uses Logging.run_script wrapper instead of manual increment_script_depth + print_script_start + print_script_summary pattern. This is a standalone utility (not called from other scripts), so simplified wrapper is appropriate. Comment notes that if later integrated into larger workflow, run_script automatically suppresses banners when script_depth >= 1.

  • [Extras.md] Updated recreate-repository.rb documentation (lines 120-150) with correct filename, two-mode explanation (force vs non-force), 7-step force workflow including new commit graph building step, early remote capture rationale, usage examples, and safety features section. Force mode now documents: 1) capture remote, 2) recreate local, 3) commit files, 4) verify match, 5) delete remote (if match), 6) push, 7) build commit graph.

  • [.ai/domains/shell-scripting.md] Updated Autoload Script Structure section (lines 1463-1495) with correct ZSH_EVAL_CONTEXT pattern. Changed example from lowercase zsh_eval_context with *file* pattern to uppercase ZSH_EVAL_CONTEXT with *:file(|code):* pattern. Added detailed explanation of pattern components: uppercase for scalar string version, colon wrappers for exact token matching, (|code) for optional ‘code’ suffix, and why both patterns are needed for .zwc compatibility. Documents same pattern antidote 2.1.1 uses.

  • [.ai/domains/zsh-startup.md] Replaced “Do NOT compile antidote.zsh” section with “Antidote .zwc Compilation” section documenting that antidote 2.1.1+ fixed the issue. Added historical note explaining the 2.1.0 bug: source-detection pattern *:file:* didn’t match filecode context from .zwc loading, causing CLI branch to fire and crash shells. Pattern now uses *:file(|code):* to handle both contexts. The find_in_folder_and_recompile "${ANTIDOTE_HOME}" call handles compilation automatically.

  • [.ai/CONTEXT.md] Updated “Antidote .zwc Crash” entry (line 237) to mark as [FIXED in 2.1.1 - July 2026]. Added details about the fix (pattern change), timeline (workaround active 3.1.19-3.1.26, fixed in 2.1.1), and current behavior (safe to compile, find_in_folder_and_recompile handles it automatically). Preserved historical context about the original bug for future reference.

Adopting these changes

  • Restart terminal to reload all zsh configuration including updated autoload functions
  • Antidote.zsh will be automatically compiled to .zwc on next shell startup (via .zlogin)
  • Autoload functions are now safe to compile to .zwc bytecode for faster loading
  • Commit graphs will be built automatically when recreating repositories via recreate-repository.rb
  • No manual intervention required - all optimizations apply automatically

3.2.6

Language build optimizations, Git performance tuning, and mise configuration improvements

  • [files/–HOME–/.shellrc] Added automatic commit graph generation in clone_repo_into() function after successful clone or unshallow+fetch. Commit graphs accelerate git log and branch operations by 10-50x. Runs silently on success, logs debug message on failure (non-fatal). Benefits all repositories cloned via this function (resurrect-repositories.rb, fresh-install bootstrap, manual clones).

  • [files/–HOME–/.gitconfig] Added Git runtime optimizations: core.commitGraph = true (enables commit graph reading for 10-50x faster log/branch operations), core.preloadIndex = true (2-5x faster status/diff/checkout on large repos via parallel index loading), gc.writeCommitGraph = true (auto-maintains commit graphs during garbage collection). These settings apply immediately to all git operations without requiring any build-time configuration.

  • [files/–HOME–/.aliases] Added Ruby and Python build optimizations via RUBY_CONFIGURE_OPTS and PYTHON_CONFIGURE_OPTS. Created _ruby_opt() and _python_opt() helper functions with option-name-based deduplication to prevent duplicate flags when re-sourcing or when user has pre-existing values. Ruby optimizations: --with-openssl-dir (OpenSSL 3.x), --with-readline-dir (readline 8.3), --with-jemalloc (5-15% faster memory), --enable-yjit (compiles YJIT JIT). Python optimizations: --enable-optimizations (PGO+LTO, 20-40% faster). Added RUBY_YJIT_ENABLE=1 export to enable YJIT runtime globally (15-50% faster execution, ~1MB memory overhead). Updated keg-only paths cache generation to export both RUBY_CONFIGURE_OPTS and PYTHON_CONFIGURE_OPTS. Expected Ruby improvement: 25-60% faster overall (build opts + YJIT runtime).

  • [files/–XDG_CONFIG_HOME–/mise/config.toml] Changed Ruby compilation setting from compile = false to compile = true with documentation explaining Ruby always compiles via ruby-build (no pre-built binaries exist), ensuring RUBY_CONFIGURE_OPTS is used. Added header documenting tool versions belong in ~/.mise.toml (personal, untracked) not in this tracked config file. Added Python setting documentation: pre-built binaries already include PGO+LTO optimizations, PYTHON_CONFIGURE_OPTS only used as fallback.

  • .mise.toml Added comment documenting Ruby must use system Ruby (2.6) in dotfiles repo for vanilla macOS compatibility - all Ruby scripts must work before mise/Homebrew Ruby is installed during fresh-install-of-osx.sh. Kept disable_tools = ["ruby"] setting with rationale.

  • [files/–HOME–/Brewfile] Added jemalloc dependency with comment “used for faster ruby”. Added inline comments to libyaml and openssl documenting they’re also used for faster Ruby builds.

  • [files/–XDG_CONFIG_HOME–/starship.toml] Fixed timeout warnings on shell startup: increased global command_timeout from 500ms to 1000ms to accommodate larger repos, set ignore_timeout = true for both custom.git_size and custom.git_clean_arrow modules to prevent warning spam when git operations exceed timeout (segments simply don’t render rather than logging warnings).

  • [files/–XDG_CONFIG_HOME–/zed/settings.json] Performance tuning: increased git blame delay_ms from 600 to 1200, increased inlay hints scroll_debounce_ms from 50 to 150, changed scrollbar diagnostics from “all” to “error”, changed tabs show_diagnostics from “all” to “error”. Reduces UI noise and improves editor responsiveness.

  • [scripts/utilities/command_utils.rb] Added run_interactive method to execute commands without suppressing stdout/stderr, allowing terminal output to flow through (for git log, etc.). Uses system() instead of Open3.capture3, passes block for error handling.

  • [scripts/run-all.rb] Replaced CommandUtils.capture_output with CommandUtils.run_interactive so command output is visible in terminal. Updated error recording to use $?.exitstatus instead of captured status object. Failures still recorded as warnings via Logging.record_warning.

Adopting these changes

  • Restart terminal to pick up new RUBY_YJIT_ENABLE and regenerated keg-only paths cache
  • Rebuild Ruby with optimizations: mise uninstall --all ruby; install_mise_versions
  • Verify YJIT: ruby -e 'puts "YJIT: #{RubyVM::YJIT.enabled?}"' (should show true)
  • Verify jemalloc: ruby -r rbconfig -e 'puts RbConfig::CONFIG["MAINLIBS"]' (should include -ljemalloc)
  • Python already has optimizations if using mise pre-built binaries (Python 3.10+)
  • Set your Ruby version in ~/.mise.toml: [tools] section with ruby = "4.0.6" (or your preferred version)

3.2.5

Starship prompt optimization and visual refinement

  • [files/–XDG_CONFIG_HOME–/starship.toml] Replaced custom shell-based git segments with starship’s built-in Rust modules ([git_branch] and [git_status]) - 10-100x faster with automatic caching. Root cause of 2-4s delay to first keystroke: custom segments using git st-nolock, git is-dirty, git rev-parse, and git size caused cumulative ~500ms+ delay per prompt render. Built-in modules complete in <100ms. Reduced command_timeout from 5000ms to 300ms. Added [custom.git_size] (~50ms overhead) showing repo size (du on .git) after branch name on green background, and [custom.git_clean_arrow] (~50-180ms overhead, short-circuits if dirty) to show green arrow only when repo has no uncommitted changes AND is not ahead/behind tracking branch. Fixed spacing issues: trailing space in git_size format stays on green background; git_status arrow and status symbols properly contained within yellow background to prevent black rectangles between segments; git_clean_arrow uses git_size’s trailing space (no added space). Visual flow: clean repos show master 1.3M (green with trailing space) → green arrow; dirty/ahead/behind repos show master 1.3M (green with trailing space) → green→yellow arrow → !3?1 or ⇣1⇡1 (yellow) → yellow arrow.

  • [files/–ZDOTDIR–/.zshrc] Fixed anonymous function scope bugs that destroyed zsh-defer function and setopt promptsubst: removed wrappers around antidote bundle loading (~line 210-224) and starship init (~line 271-295). Re-enabled fast-syntax-highlighting and zsh-autosuggestions plugins after confirming they cause tiny acceptable delay (<50ms), not the 2-3s delay (which was caused by custom git segments).

  • [files/–ZDOTDIR–/.zsh_plugins.txt] Removed OMZ git plugin (431 lines, only 1 alias gco was used). Added 7 deferred utility plugins: colored-man-pages (syntax-highlighted man pages), extract (universal archive extractor with x alias supporting 20+ formats), copypath (copy absolute paths to clipboard), copyfile (copy file contents to clipboard), copybuffer (Ctrl+O keybind to copy command line), jsontools (JSON formatting/validation utilities), macos (macOS-specific utilities including ofd, tab, showfiles, hidefiles). All new plugins use kind:defer for zero startup impact (~1-2ms deferred load time).

  • [files/–HOME–/.gitconfig] Added co = checkout alias to replace removed OMZ git plugin’s gco alias. Added new “BASIC SHORTCUTS” section before “HELPER PREDICATES” section to organize simple one-line aliases separately from complex predicates.

Adopting these changes

  • Quit and restart terminal (not just new tab) to reload starship config, zsh plugins, and git config
  • Prompt should now appear instantly with <100ms render time (down from 500ms+)
  • First keystroke should be instant (down from 2-4s delay)
  • Test new plugins after 1-2 second deferral: extract, copypath, copyfile, pp_json, ofd, man ls (colored output)
  • Use git co <branch> instead of gco <branch> (or continue using full git checkout)

3.2.4

Zsh startup optimization and code quality improvements

  • [files/–ZDOTDIR–/.zshrc] Minor consistency improvement in git version cache generation: Simplified single-line cache write by removing redundant {...} block wrapper around single echo statement. Matches pattern used throughout codebase where single-line writes are inlined and multi-line writes use blocks.

  • [scripts/software-updates-cron.rb] Replaced hardcoded empty ollama_models = [] array with dynamic model discovery via CommandUtils.query('ollama', 'list'). Script now automatically detects and updates all locally installed Ollama models instead of requiring manual list maintenance. Removed commented-out hardcoded model names. Removed redundant require 'open3' since CommandUtils already requires it internally. Any model you ollama pull manually will now be automatically updated by the cron job.

Comprehensive code quality review

  • [files/–HOME–/.aliases] Fixed _free_wifi unsafe && command chains - converted to explicit if statements
  • [scripts/resurrect-repositories.rb] Replaced Ruby stdlib warn() with Logging.warn() for consistency; improved variable scoping in _resurrect_each method by moving existing_remotes declaration closer to first usage
  • [scripts/osx-defaults.sh] Moved auto variable from script-level into main() as local variable

All other issues already compliant: .shellrc logging guards use explicit if statements, arithmetic expressions have proper fallback handling. All 35 files now pass whitespace rules, syntax checks, and have correct executable permissions.

Adopting these changes

  • Zsh cache change: Restart terminal to reload updated .zshrc
  • Ollama updates: No action required - cron job will now automatically update all installed models on next run

3.2.3

Git Shallow Clone Workflow Simplification

  • Replaced fetch-unshallow and pull-unshallow aliases with new unshallow alias. The new alias configures all remotes to fetch all branches and converts shallow clones to full clones. After running git unshallow, users must run git fetch or git pull to retrieve complete history for all branches.

Adopting these changes

  • Run all unshallow followed by all fetch or all pull on any shallow-cloned repositories to retrieve complete history

3.2.2

  • Turned off ollama pull in the cron job since it was taking more than an hour in some cases.
  • Fixed issue when unshallowing shallow-cloned git repos.

3.2.1

CommandUtils refactoring: dead code removal and error logging improvements

  • [scripts/utilities/command_utils.rb] Added query(*command) method for simple read-only operations - returns stdout.strip on success, ignores stderr and exit status. Use for version checks and config queries where failure is unexpected. Eliminates boilerplate for trivial commands that previously used full capture3 pattern.

  • [scripts/utilities/git_processor.rb] Removed 4 unused methods (67 lines total): log_timestamp, fix_head_file, rev_list_count, symbolic_ref - none were called by any scripts. Updated internal comments to describe behavior/format rather than implementation details. Result: 9% smaller (736 lines → 683 lines).

  • [13 Ruby scripts and utility modules] Converted 15 Open3.capture3 call sites to CommandUtils methods: 9 to query() (simple stdout reads), 2 to capture_output() (new error handling needs), 4 to check_status() (pre-captured output from GitProcessor). Fixed 3 error logging sites (antidote.rb, collection_processor.rb, cron.rb) to pass nil for stdout parameter when stdout contains sensitive data, large directory lists, or success indicators rather than failure diagnostics - focuses error messages on stderr (what failed) instead of mixed output.

  • [.ai/domains/logging-conventions.md] Restructured CommandUtils documentation with decision tree for choosing the right pattern: Use query() for stdout-only reads, capture_output() for commands with error handling, check_status() for pre-captured output, raw Open3.capture3 only when both stdout and stderr are needed for processing logic (not just error logging).

  • [README.md] Enhanced adoption instructions with priority markings for environment variable customization. Added DOTFILES_BRANCH and UPSTREAM_GH_USERNAME to required changes checklist. Clarified that UPSTREAM_GH_USERNAME must NOT be changed (parent repo owner), while GH_USERNAME must be changed to adopter’s username. Applies to both .shellrc and env_vars.rb files.

Adopting these changes

  • No action required - refactoring is internal implementation change with no user-visible behavior changes
  • Net zero line count change (+120/-120 across 13 files), git_processor.rb 9% smaller

3.1.39

Logging system refactor: centralized error handling and common section header format

  • Logging improvements: All Ruby and shell scripts now use fixed-width format for section_header, and all human-readable text (not the padding character) start at a fixed position to help ease of reading all section headers in a vertical manner.

  • Vanilla OS compatibility: Removed timestamp check in capture-prefs.rb that was blocking imports when backup predated osx-defaults.sh changes. Script now accepts any backup without validation errors.

Adopting these changes

  • Restart terminal to reload .shellrc with fixed section_header function
  • Regenerate crontab: crontab -r; recron; crontab -l
  • Scripts now provide better error messages with formatted stdout/stderr output

3.1.38

Cross-platform tool-specific git diff configuration with conditional includes

  • [files/–HOME–/.gitconfig] Moved tool-dependent diff configurations (pandoc, plist, sqlite3) to conditional include files for cross-platform compatibility. Removed inline [diff "plist"] and [diff "sqlite3"] sections. Added cachetextconv = true to plist and sqlite3 configs for performance (textconv output is deterministic and safe to cache). Added four conditional includes in [include] section: ~/.gitconfig-delta-enabled.inc, ~/.gitconfig-pandoc-enabled.inc, ~/.gitconfig-plist-enabled.inc, ~/.gitconfig-sqlite3-enabled.inc. These symlinks only exist when the corresponding tool is installed, allowing git config to work on systems without these tools (Linux, Windows, fresh macOS).

  • [files/–HOME–/.gitconfig-{pandoc,plist,sqlite3}.inc] Created separate config files for tool-specific diff drivers (pandoc for .docx/.odt, plutil for .plist/.defaults, sqlite3 for .db/.sqlite files). Each includes cachetextconv = true for performance. Plist config notes that plutil is macOS-only. SQLite3 config includes binary = true to prevent git text diff attempts on raw database files. Files are symlinked by install-dotfiles.rb and conditionally enabled via -enabled.inc symlinks when tools are installed.

  • [scripts/install-dotfiles.rb] Refactored tool-specific gitconfig symlink management for DRY principle and extensibility. Added _ensure_gitconfig_tool_symlink(tool_name, command_name: nil) generic helper method that handles symlink creation/removal for any tool. Replaced four nearly-identical methods with direct calls to the generic helper, reducing ~160 lines of duplicate code to ~44 lines (73% reduction). Adding new tools now requires only a single line call, no new method definitions needed.

  • [files/–HOME–/custom.gitignore] Added ignore patterns for new gitconfig conditional include files: /.gitconfig-{pandoc,plist,sqlite3}-enabled.inc symlinks and /.gitconfig-{pandoc,plist,sqlite3}.inc source configs.

Adopting these changes

  • Run install-dotfiles to create symlinks for available tools: install-dotfiles.rb
  • Verify git config includes are working: git config --list --show-origin | grep diff.pandoc (should show config loaded from ~/.gitconfig-pandoc-enabled.inc if pandoc is installed)
  • On systems without pandoc/plutil/sqlite3: The enabled symlinks won’t exist, git config will silently skip those includes, and the file types will show as binary diffs (expected behavior)

3.1.37

Skip nested git repos in run-all.rb

  • [scripts/utilities/git_workspace.rb] Added nested repository filtering to find_git_repos. Nested repos are now excluded from run-all.rb, oss upreb, git maintenance registration, and mtime restoration - eliminating spurious dirty-tree warnings when nested repos have uncommitted changes.

brew package cleanup improvements

  • [files/–HOME–/.aliases] Added cleanup operations before first brew bundle install in bupc function: Run brew bundle cleanup -f, brew cleanup --prune=all, and brew autoremove to remove unused dependencies, orphaned casks, and outdated downloads before installing new packages. Prevents accumulation of stale software and avoids if the old vs new casks were conflicting with each other. Existing cleanup operations after install remain unchanged.

3.1.36

Cron job improvements: PATH expansion fix, run tracking, and warning reduction

  • [scripts/utilities/cron.rb] Fixed critical PATH expansion bug in crontab generation (lines 140-141, 151): Cron does not expand ${VAR} syntax in environment variables, causing PATH to contain literal string ${HOMEBREW_PREFIX}/bin instead of /opt/homebrew/bin. This prevented git from finding git-restore-mtime, causing 17 “not a git command” warnings per run. Changed from single-quoted template strings with variable references to double-quoted strings with Ruby interpolation, generating expanded literal paths at template generation time. Now outputs PATH=/opt/homebrew/bin:... instead of PATH=${HOMEBREW_PREFIX}/bin:.... Also expanded paths in cron command line (line 151) for reliability. Added missing environment variables to crontab template (lines 113-118): PERSONAL_CONFIGS_DIR, PERSONAL_PROFILES_DIR, PROJECTS_BASE_DIR, HOMEBREW_BUNDLE_FILE, HOMEBREW_BUNDLE_FILE_GLOBAL - these are required by Ruby scripts invoked during cron runs. Removed redundant USERNAME variable (line 126) - kept only POSIX-standard USER (macOS/Linux). EnvVars::USER already has fallback to USERNAME for cross-platform compatibility. Changed cron schedule from daily to hourly (line 151): 0 * * * * runs at top of every hour. Changed temp log from ephemeral $(mktemp) to persistent ~/.software-updates-cron-last-run.log for debugging - now captures ALL output from last run (success or failure), overwrites on each run. Main log (~/software-updates-cron.log) still only appends on errors/warnings (exit code non-zero). Updated comments (lines 143-148) to document three-file tracking system and PATH expansion issue.

  • [scripts/software-updates-cron.rb] Renamed success marker file from .software-updates-last-success to .software-updates-run-log to reflect that it now tracks both successful and failed runs. Added three status markers: STARTED (written at script start), COMPLETED (written on success), FAILED (written on error/warning). Each marker includes timestamp; COMPLETED and FAILED include duration. This allows monitoring whether cron is currently running (STARTED without completion marker), detecting hung jobs (STARTED hours ago with no completion), and tracking success/failure history over time. The file provides complete audit trail: when each run started, whether it completed successfully or failed, and how long it took.

  • [scripts/utilities/macos.rb] Changed _set_softwareupdate_schedule sudo credential warning from Logging.warn to Logging.debug. This check fails in cron context (no interactive sudo available), which is expected and not an error. Debug level means message is logged for troubleshooting but doesn’t trigger macOS notifications.

  • [scripts/utilities/git_workspace.rb] Changed status_repo non-git-repo warning from Logging.warn to Logging.debug. Not all chrome browser profile folders are git repositories, which is expected and not an error. Debug level prevents spurious warnings while preserving diagnostic info.

  • [files/–HOME–/custom.gitignore] Replaced .software-updates-last-success with .software-updates-run-log and added .software-updates-cron-last-run.log. Both files are generated by cron and intentionally excluded from home repo tracking.

  • [Extras.md] Updated cron documentation to explain cron’s PATH expansion limitation and the three-file tracking system. Documents STARTED/COMPLETED/FAILED markers, provides monitoring commands for checking current run status, debugging last run output, and reviewing error history. Clarifies that successful runs don’t append to main log (keeps it clean for problem diagnosis).

Adopting these changes

  • Regenerate and install crontab: recron (reads from ${PERSONAL_CONFIGS_DIR}/crontab.txt if it exists, otherwise preserves current schedule)
  • Rename old file if it exists: mv ~/.software-updates-last-success ~/.software-updates-run-log (first run will append new STARTED/COMPLETED/FAILED markers to existing completion timestamps)
  • Monitor cron runs: tail -4 ~/.software-updates-run-log shows recent markers (STARTED/COMPLETED/FAILED with timestamps and durations)
  • Debug output: cat ~/.software-updates-cron-last-run.log shows complete output from last run
  • Errors only: tail ~/software-updates-cron.log shows historical errors/warnings

3.1.35

  • [.zlogin, .zshrc] Performance tweaks for zsh startup.
  • [starship.toml] Enabled lightweight modules mise and direnv.
  • [cron.rb] Disable all mail generation from cron jobs (rely on macOS notifications instead).
  • Cron jobs do not ues chronic now. They will write to a temp file and delete if the cron was successful, else this file’s contents will be appended into the ${HOME}/software-updates-cron.log file for human review.

3.1.34

  • Removed vorssaint since it was causing zoom to hang when joining meetings. Went back to stats (menubar monitors), sol (clipboard, window layout) and dockdoor (window previews in dock).

3.1.33

Starship optimizations: 75-100x faster prompts + embedded git size + cron output fix + DRY helpers

  • [files/–HOME–/.gitconfig] Added 2 new helper aliases to extract repeated patterns (lines 43, 48): st-nolock returns porcelain status without locks (--no-optional-locks status --porcelain 2>/dev/null) for safe prompt/monitoring use, is-dirty returns 0 if working tree has uncommitted changes by piping st-nolock to grep. Both helpers follow <dir> argument convention. Extracted to eliminate 6 occurrences of git --no-optional-locks status --porcelain 2>/dev/null in starship.toml (2 as standalone command, 4 in when conditions with grep pipe). Keeps st alias clean for interactive use (users benefit from normal locking/contention visibility). Converted all 19 single-line named function aliases to multi-line format for readability (helper predicates, inspection/info aliases, viewing changes/history, local changes/staging, remote operations) - improves maintainability and makes control flow immediately visible. Now 721 lines (was 649: +10 for new helper aliases, +62 for multi-line formatting).

  • [files/–XDG_CONFIG_HOME–/starship.toml] Replaced all 6 occurrences of direct --no-optional-locks calls with new git aliases (lines 206, 214, 249, 257, 284, 293): when conditions now use git is-dirty and ! git is-dirty instead of full command pipeline, command blocks now use porcelain=$(git st-nolock) instead of inline --no-optional-locks invocation. Reduces duplication, centralizes lock-free status logic in git config, improves maintainability (single source of truth for prompt status checks).

  • [files/–XDG_CONFIG_HOME–/starship.toml] Applied 5 cumulative optimizations to all 8 custom git segments (lines 203-328): (1) Added require_repo = true to skip non-git directories without subprocess (~2s saved); (2) Added shell = ["sh", "--noprofile", "--norc"] to avoid loading zsh config (~1.8-2.7s saved); (3) Removed redundant git rev-parse --is-inside-work-tree from 6 when conditions since require_repo already checks this (~135ms saved); (4) Moved porcelain check to start of git_dirty and git_clean commands with when = "true" to eliminate duplicate git status calls while allowing starship to evaluate segments (~20-30ms saved); (5) Replaced custom.git_state segment (35 lines) with built-in git_state module (13 lines config) which uses Starship internal caching and adds progress indicators for rebase/cherry-pick operations (~50-100ms saved during operations). Built-in git_state uses Rust/libgit2 internally (more correct than shell script), works with both classic .git/ and reftable backends (git 2.45+ default). Reduced from 2-3s → 35ms per prompt (50-90x faster).

  • [files/–XDG_CONFIG_HOME–/starship.toml] Embedded repo size directly into git_clean and git_dirty commands (lines 237, 268) by calling GIT_SIZE_QUIET=1 git size inline. Eliminated 2 separate git_size segments and their redundant when conditions. Reduced from 354 → 339 lines (saved 15 lines). Git calls per prompt: 8-9 → 6-7 (eliminated 2 when conditions). Further performance gain: 35ms → ~30-32ms (10-15% faster). Cumulative improvement: 75-100x faster than v3.1.32.

  • [files/–HOME–/.gitconfig] Added GIT_SIZE_QUIET env var support to git size alias (lines 307-310). When set, suppresses label and outputs only size value for programmatic use (starship, scripts). Maintains optimal performance with 3 forks (git rev-parse + du + cut). Removed custom standup alias (lines 96-98) - now using git standup from git-extras which supports multi-repo search (-m flag), flexible time ranges (-d flag), branch grouping (-B), and fetch-first option (-f). Fixed sci alias (line 69) to amend only when there’s exactly 1 unpushed commit (changed -gt 0 to -eq 1); previously would amend with 2+ unpushed commits which could lose the earlier commit messages. Replaced git diff --cached --quiet with git dc --quiet in 3 aliases (sci, pull-safe, upreb) to reuse the dc alias (DRY principle) - both are equivalent (--staged is a synonym of --cached). Normalized 4 shell aliases (pull-unshallow, fetch-unshallow, new, old) from legacy !sh -c '...' - pattern to modern !f() { ... }; f pattern for consistency (17/22 → 21/22 aliases now use the named function pattern) - improves readability by eliminating nested quote complexity and making multi-line logic clearer. Removed redundant git flags made unnecessary by config settings: removed --progress from push in upreb and pushsub (redundant when TTY attached, wrong for cron which should suppress progress), removed --all from fetch/pull in 4 aliases (pull-unshallow, fetch-unshallow, pull-safe, upreb) since fetch.all=true config makes it the default behavior. Reformatted 5 complex single-line aliases (pull-safe, upreb, rfc, size, cc) to multi-line format for readability - eliminates quote escaping complexity and makes control flow immediately clear. Applied DRY principle throughout aliases: updated dc to use d --staged instead of diff --staged (line 165), updated dcolor to use d --color-words instead of diff --color-words (line 169), updated new and old aliases to use git br instead of git branch --show-current for consistency. Simplified new alias (line 329) from 8 lines to 3 lines using parameter expansion with command substitution default (b="${1:-$(git br)}") - more concise while preserving exact behavior. Added 4 new helper aliases to extract common patterns: is-clean (checks if working tree has no uncommitted changes using d --quiet && dc --quiet, used in pull-safe and upreb), is-shallow (checks if repo is a shallow/partial clone, used in pull-unshallow and fetch-unshallow), all-refs (lists all local branches and remote-tracking branches, used in rfc and cc), and has-upstream (checks if upstream remote exists, used in upreb) - improves readability and maintainability via DRY principle. Renamed repo alias to origin-name to avoid potential clashes with external tools. Regrouped all aliases into 5 logical sections with alphabetical ordering within each section: (1) Helper Predicates (4 aliases: all-refs, has-upstream, is-clean, is-shallow), (2) Inspection & Info (23 aliases: b through track), (3) Viewing Changes & History (18 aliases: d through who), (4) Local Changes & Staging (16 aliases: amend through wipe), (5) Remote Operations (8 aliases: fetch-unshallow through upreb), (6) Submodules (2 aliases: sf, siu) - significantly improves discoverability and maintainability.

  • [.ai/domains/git-config.md] Updated git alias pattern documentation (lines 81-135) to clarify that !f() { ... }; f is the preferred pattern for all multi-step shell aliases. Documented that the legacy !sh -c '...' - pattern is valid but deprecated in favor of the clearer named function syntax. Added comprehensive comparison showing benefits (clearer structure, easier readability, simpler argument handling) and argument handling differences between the two patterns. Added § Helper Predicates for DRY Principle (lines 82-147) documenting 6 helper aliases: st-nolock (lock-free porcelain status for prompts), is-dirty (working tree dirty check using st-nolock), is-clean (no unstaged/staged changes), is-shallow (shallow clone check), all-refs (list local+remote branches), has-upstream (upstream remote exists). Documents rationale for lock-free status checks (safe for prompts, prevents creating index.lock files) and why st alias should NOT use --no-optional-locks (users benefit from seeing lock contention).

  • [files/–HOME–/.editorconfig] Added *.toml section with indent_size = 2 for starship config consistency.

  • [scripts/software-updates-cron.rb] Fixed cron job writing to mail spool despite using chronic. Added out: File::NULL to 5 system calls producing progress output (lines 155, 163, 165, 184, 219): brew update, mise plugins update, mise upgrade --bump, tldr --update, bat cache --build. Added out: File::NULL, err: File::NULL to ollama pull (line 219) to suppress ANSI escape sequences and progress bars. Preserved output for brew bundle check -v (line 160) since it shows useful diagnostic info about missing packages. Uses File::NULL constant instead of hardcoded '/dev/null' string for platform portability (Unix/macOS: /dev/null, Windows: NUL). Cron now runs silently on success, only sends mail on actual errors.

Adopting these changes

  • Restart terminal to reload starship config - prompts will be noticeably faster in git repositories.
  • Cron job will stop generating spurious mail on successful runs.
  • Use git standup -d 7 instead of git standup (git-extras version requires -d flag for time range).

3.1.32

  • [files/–HOME–/.gitconfig-delta.inc] Moved all delta-related configs to this file. It will get included only if delta is installed. Till then, this file will just sit on the HOME folder, but will not affect normal git operations.

  • [files/–HOME–/.gitconfig] Removed custom aliases that are present via git-extras.


3.1.31

  • [files/–HOME–/Brewfile] Simplified trusting of specific formulae/casks with fully-qualified names in the DSL itself.

  • [files/–HOME–/.aliases] Deleted trust_brewfile_items since its no longer required due to above change.

3.1.30

  • [files/–HOME–/.aliases] Extracted trust_brewfile_items utility method that can be used to trust formulae/casks which are not yet trusted. Removed deprecated --cleanup option from brew bundle install.

  • [files/–HOME–/Brewfile] Replaced Stats and DockDoor with Vorssaint.

Adopting these changes

  • Restart terminal after successful conflict resolution.

3.1.29

Fix 26 critical/high-priority issues: Core modules, ERR traps, PlistBuddy atomicity

  • [scripts/utilities/keybase.rb] Added missing Core module (lines 6-7, 14-15). Added require_relative 'core' and both include Core + extend Core. Fixes nil_or_empty? usage that previously only worked via transitive include through Logging. Method now available in both module methods and blocks.

  • [scripts/utilities/plist.rb] Added missing Core module (lines 8, 20-21). Added domain validation for export operations (line 41) - now validates domain is non-empty before attempting export. Added rescue for relative_path calls (line 57) - prevents crashes when path validation fails. Fixes nil_or_empty? calls at lines 66 and 187.

  • [scripts/utilities/path_utils.rb] Added missing logging require (line 8). Fixes crash when Logging.debug called at line 117 during ensure_directories_exist.

  • [scripts/install-dotfiles.rb] Added Core module (lines 24, 32-33). Added require_relative 'utilities/core' and both include Core + extend Core. Removes fragile dependency on transitive Core inclusion via Logging module. Makes nil_or_empty? available throughout script.

  • [scripts/resurrect-repositories.rb] Added Core module (lines 22, 31-32). Same changes as install-dotfiles.rb - explicit Core require and dual include/extend for consistent helper method availability.

  • [scripts/capture-prefs.rb] Added rescue for GitProcessor relative_path calls (lines 168-178). When path validation fails (path outside repo or invalid), logs warning via Logging.warn and skips the problematic file instead of crashing. Prevents fatal errors during preferences backup/restore when unexpected file paths encountered.

  • [scripts/cleanup-browser-profiles.rb] Fixed variable scoping (lines 138, 222-238). Moved profile_folder declaration inside GitProcessor block where it’s used. Moved backup_file declaration inside conditional branches. Improves garbage collection and clarifies variable lifetime.

  • [files/–HOME–/.shellrc] Fixed unsafe keep_sudo_alive arithmetic (line 1237). Changed has_sudo_credentials to has_sudo_credentials || true - prevents return code 1 from triggering ERR traps when sudo not available. Added Ruby availability checks in _call_ruby_cron (guards all Ruby delegations with command_exists ruby). Prevents crashes on vanilla OS before Homebrew installs Ruby.

  • [files/–HOME–/.aliases] Added Ruby availability checks (lines 377-382 in _call_ruby_git_workspace, 1008-1013 in _call_ruby_macos). Guards all Ruby utility delegations with command_exists ruby before invoking. Prevents “ruby: command not found” errors during fresh-install before Homebrew installation completes.

  • [files/–XDG_CONFIG_HOME–/zsh/status_all_repos] Fixed dispatch pattern. Moved shell implementation into _status_all_repos, added status_all_repos dispatch wrapper calling dispatch_or_fallback. Complies with mandatory pattern from shell-scripting.md - Ruby delegation works correctly, shell fallback preserved.

  • [files/–XDG_CONFIG_HOME–/zsh/update_all_repos] Fixed dispatch pattern. Same changes as status_all_repos - moved implementation to _update_all_repos, added dispatch wrapper. Ensures consistent Ruby-first execution with shell fallback.

  • [scripts/osx-defaults.sh] Added ERR trap (line 34): trap 'error "Script failed at line ${LINENO}. Check log for details."' ERR. Provides clear failure notification with line numbers instead of silent failures. Added context message before killing apps (line 146): “About to kill and restart Terminal, iTerm2, Finder…” - prevents user confusion when apps suddenly close. Added _plist_set_or_add helper function (lines 98-125) implementing atomic Set-or-Add pattern for PlistBuddy operations. Refactored Terminal profile settings (lines 1173-1195) to use helper - 4 settings now atomic (rowCount, columnCount, useOptionAsMetaKey, shellExitAction). Converted all 59 non-array iTerm2 settings (lines 1374-1467) from Delete+Add pattern to _plist_set_or_add - includes window dimensions, text/font settings, terminal behavior, session options, keyboard modifiers. Added error suppression to Jobs to Ignore array Delete operation (2>/dev/null || true) - only array operation remaining as Delete+Add since PlistBuddy cannot atomically set array contents. Fixed duplicate array index bug (zsh was :5 twice, now correctly :6). Total: 63 settings moved from non-atomic to atomic pattern. Script can now be interrupted at any point without leaving Terminal/iTerm2 preferences in partial state (except array contents). Code reduction: 189 lines changed (+64, -125), net -61 lines in iTerm2 section.

  • [scripts/fresh-install-of-osx.sh] Added .shellrc download validation (lines 113-117). After curl download, validates file exists and is non-empty using is_file and is_file_non_zero. Exits with clear error message if download corrupted or network failure occurred. Prevents sourcing broken .shellrc that would crash bootstrap process. Added FileVault user_action (line 172): user_action "Enable FileVault disk encryption in System Settings > Privacy & Security". Prompts user to enable encryption after fresh-install completes - can’t be automated (requires user password). Added manual review reminder (line 769): prints user_action before opening System Settings, reminds user to review all applied settings. Prevents blind acceptance of defaults. Fixed cron backup timing (lines 723-731): moved suspend_cron call to before capture-prefs.rb -i invocation. Prevents cron job from running during preferences restoration (could conflict with import process).

Adopting these changes

  • Restart terminal after successful test

3.1.28

Core utility module and ENV access centralization

  • [scripts/utilities/core.rb] (NEW, 80 lines) Zero-dependency foundational module providing helpers used by all other utilities. Prevents circular dependencies and avoids duplication. Provides nil_or_empty?(val) with type-aware checking (strips strings, handles arrays, converts others to string), execute_with_streaming(cmd, stdin_data: nil) for real-time command output (brew bundle, git operations). Other utility modules include Core for unqualified access. Refactored all other scripts to use this as an included module.

  • [all ruby scripts] Changed the internal structure of the ruby classes to use a module which could be invoked directly from another ruby script if needed. Provides a cleaner architecture for separating out the CLI usage (as a standalone ruby script) vs the direct-module usage.

Documentation updates

  • [.gitignore] Added /.ai/session-state/ pattern (holds transactional work products: task lists, session analyses, completed project retrospectives). Removed from tracking (moved to gitignored session-state/ folder).

  • [.ai/REBASE-AND-REFACTORING-METHODOLOGY.md] Removed broken references to moved case study files (lines 753-758).

Adopting these changes

  • Restart terminal to reload environment (EnvVars changes)
  • No user action required for Core module (transparent dependency)

3.1.27

Extract plist functionality, port setup-login-item to Ruby, centralize system command paths

  • [scripts/utilities/plist.rb] (NEW) Extracted plist operations from capture-prefs.rb into reusable module. Provides: export_domain(domain, file) - exports defaults to XML plist; import_domain(domain, file) - imports plist to defaults; strip_excluded_keys(domain, file, patterns) - removes non-portable keys using REXML; has_keys?(file) - checks if plist has any keys after stripping; load_excluded_keys(filepath), load_denied_list(filepath), load_domains_list(filepath, denied) - data file loaders for pattern/domain lists. All plist manipulation now uses REXML (system Ruby, always available) with defaults/plutil wrappers. Benefits: modularity (reusable by other scripts), single source of truth for plist operations, 76-line reduction in capture-prefs.rb.

  • [scripts/capture-prefs.rb] Refactored to use Plist module. Removed inline REXML manipulation (now Plist.strip_excluded_keys), removed rexml/document and set requires (now in plist.rb), simplified helper methods to thin wrappers around Plist module methods. Export/import logic now uses Plist.export_domain, Plist.import_domain, Plist.has_keys?. Reduced from 366 to 290 lines.

  • [scripts/setup-login-item.rb] (NEW) Ruby port of setup-login-item.sh. Registers apps as macOS login items via SMAppService (macOS 14–25) or legacy System Events AppleScript (macOS 13, 26+). Functionality preserved: -a <app-name> and -b (background) flags, all logging via Logging module, script depth tracking, warning collection. Benefits: no ${ZDOTDIR}/.aliases dependency (self-contained with require_relative), cleaner subprocess handling with Open3.capture3, explicit return values, easier to test. Shell version retained temporarily for rollback safety.

  • [files/–HOME–/Brewfile] Updated setup_login_items_script variable to reference setup-login-item.rb instead of setup-login-item.sh. Keybase and other login-item postinstall hooks now invoke Ruby version.

  • [scripts/utilities/macos.rb] Added ROOT constant (filesystem root as Pathname) and all macOS system command path constants: DEFAULTS_CMD, DU_CMD, OSASCRIPT_CMD, PLUTIL_CMD, ZSH_CMD. Centralized from scattered definitions across multiple files. All use ROOT.join('usr', 'bin', 'command').to_s.freeze pattern for consistency.

  • [scripts/utilities/path_utils.rb] Removed ROOT and DU_CMD constants (moved to MacOS module). Updated dir_size_kb and dir_size_human to use MacOS::DU_CMD. Added require_relative 'macos'. Updated module doc comment to clarify it contains generic (cross-platform) utilities only, with pointer to MacOS module for system command paths. Retained: command_exists?, extract_path_segment_at, glob_pathnames (all generic/cross-platform).

  • [scripts/utilities/plist.rb, scripts/setup-login-item.rb, scripts/resurrect-repositories.rb] Updated all system command references to use MacOS:: constants (MacOS::DEFAULTS_CMD, MacOS::PLUTIL_CMD, MacOS::OSASCRIPT_CMD, MacOS::ZSH_CMD, MacOS::ROOT). Changed requires from path_utils to macos where appropriate. Benefits: clear separation of concerns (macOS-specific paths in MacOS module, generic utilities in PathUtils), single source of truth for all system command paths, consistent MacOS::*_CMD naming pattern.

  • [.ai/domains/fresh-install.md, .ai/instructions.md] Updated applyTo patterns and file lists to reference setup-login-item.rb instead of setup-login-item.sh.

  • [scripts/setup-login-item.sh] Deleted after successful production validation of Ruby version. Shell version is no longer needed.

  • [Extras.md] Updated documentation to reference setup-login-item.rb instead of setup-login-item.sh. Added notes about SMAppService (macOS 14–25) vs legacy System Events path (macOS 13, 26+).


3.1.26

Tool-agnostic AI instruction system

  • [.ai/] (NEW) Centralized AI assistant instructions replacing tool-specific configs. Structure: instructions.md (main entry point with general rules, whitespace requirements, git state management), context.md (historical optimizations, performance patterns, debugging guidance), domains/ (13 domain-specific rule files with YAML applyTo frontmatter). Total: 4,000+ lines of consolidated guidance.

  • [.ai/domains/] Domain files cover: character-encoding.md (ASCII-only requirements), comment-philosophy.md (cross-language comment guidelines), edit-checklist.md (complete edit workflow), fresh-install.md (bootstrap/setup rules), git-config.md (git aliases/config patterns), logging-conventions.md (unified color standard for shell+Ruby), path-constants.md (env var/path construction rules), ruby-scripting.md (Ruby script template, memoization, private methods), script-depth-tracking.md (nesting suppression + auto-indentation), shell-scripting.md (shell script template, option parsing, conditionals), whitespace-rules.md (formatting requirements), zsh-startup.md (startup performance optimization).

  • [.cursorrules, .windsurfrules] (NEW) Minimal redirects pointing to .ai/instructions.md. Replaced tool-specific duplication with single source of truth.

  • [.github/copilot-instructions.md] Reduced from 3,800+ lines to 6-line redirect to .ai/instructions.md. All rules migrated to domain files.

  • [.opencode/opencode.json, .opencode/skills/dotfiles-domain/SKILL.md] Updated to reference new .ai/ structure. Skill now loads domain context instead of duplicating rules.

Script improvements following new conventions

  • [files/–ZDOTDIR–/.zshrc, .zshenv, .zlogin] Applied whitespace rules (no trailing blank lines, no trailing whitespace, single final newline). Formatted with shfmt.

  • [all ruby scripts] Enforced private method discipline (_ prefix + private declaration). Centralized ENV.fetch calls into EnvVars module. Applied Pathname optimization (defer .to_s until last moment). Fixed whitespace violations.

  • [files/–HOME–/Brewfile] Added ollama package.

  • [files/–HOME–/.ollama/env] (NEW) Ollama environment configuration with model storage path.

  • [GettingStarted.md] Updated documentation to reference new .ai/ instruction structure.

Fixed set -E ERR trap firing on normal && conditionals during fresh-install

  • [.shellrc] Converted standalone && chains and bare arithmetic/test expressions to explicit if/return blocks in all guard/early-return patterns to prevent ERR trap from firing when the conditional returns false in normal operation. Affected: logging functions (success, info, warn, user_action, debug), validation helpers (_has_step_errors, _has_step_warnings, is_zero_string, is_non_zero_string, is_running_in_tty, is_zsh, is_arm, is_executable, is_symbolic_link, is_file, is_directory, is_empty_array, is_non_empty_array, is_file_older_than, is_macos, is_linux, is_first_install, is_windows, is_outermost_script, has_sudo_credentials, command_exists, join_array, is_non_empty_file, is_directory_empty), file operations (load_file_if_exists now uses || warn on source failures, ensure_dir_exists converted || mkdir pattern to explicit if block), git operations (clone_repo_into, set_ssh_folder_permissions), and re-source guards.

  • [.aliases] Converted re-source guard, DEBUG echo, and conditional alias assignments (command_exists tool && alias, is_directory dir && alias) to explicit if blocks. Fixed is_first_install flag assignments and check_cask brew command chains.

  • [.zshenv, .zshrc, .zlogin] Converted DEBUG echo && chains to explicit if blocks. Fixed find_in_folder_and_recompile to ensure XDG_CACHE_HOME exists before touching sentinel file (prevents failure when directory doesn’t exist on vanilla OS during first load_zsh_configs call).

  • [fresh-install-of-osx.sh] Converted biometric sensor flag assignments to explicit if blocks. Fixed chsh command to handle authentication failures gracefully with _record_warning instead of triggering ERR trap. Declared _script_start_times and _step_start_times arrays before using += operator (prevents set -u violations). Moved Sol.app launch check to nested if blocks to avoid standalone && chain abort. Modified _clone_home_repo to pull latest home repo changes on pre-configured machines before preferences restore. Added automatic preferences export and commit on pre-configured machines: runs capture-prefs.rb -e to refresh backup, then git sci to commit (amends existing commit if ahead of remote, creates new if not) - updates git commit timestamp so import validation passes.

  • [osx-defaults.sh] Converted spotlight indexing mdutil command chain to explicit if block.

  • [files/–PERSONAL_PROFILES_DIR–/.envrc] Converted symlink creation and folder move operations from chained && to explicit if blocks.

  • [scripts/utilities/cron.rb] Changed restore_cron from raising exceptions to logging errors via Logging.record_error and returning boolean success/failure. Updated callers (resume_cron, recron) to check return value before printing success message. Prevents crontab installation failures from aborting fresh-install via ERR trap - errors are recorded in summary but execution continues.

  • [capture-prefs.rb] Added FIRST_INSTALL exception to timestamp validation check - skips staleness validation when ENV['FIRST_INSTALL'] is set. On vanilla OS, fresh-install runs osx-defaults.sh -s first to baseline current system prefs, so import is an incremental overlay where any backup is better than none. On pre-configured machines, check remains active to prevent importing incomplete settings after osx-defaults.sh updates.

Root cause: Under set -E, ERR traps inherit to all functions. Standalone A && B expressions where A returns false propagate exit code 1 to the enclosing scope, triggering the trap even though the false result is expected (e.g., guard conditions, optional file checks). Explicit if A; then B; fi never propagates the predicate’s exit code, so the trap never fires. Ruby exceptions (raise) also propagate to shell as non-zero exit codes, triggering ERR traps when called via ruby -e from shell functions.

Impact: Fresh-install now completes successfully on both vanilla OS and pre-configured machines without false-positive “Installation failed at line X” errors during .zshrc sourcing, when external completion files are loaded, when crontab installation fails, or when backup preferences predate osx-defaults.sh changes. On pre-configured machines, preferences backup is automatically refreshed and committed before import, ensuring timestamp validation passes.

Adopting these changes

  • Review the new .ai/ structure for comprehensive coding guidelines.
  • Rebase from upstream, resolve conflicts.
  • Run the following commands in each terminal tab/window/panel (or) Quit & Restart the Terminal application:

    unfunction is_shellrc_sourced; zcompile ~/.shellrc; source ~/.shellrc
    unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases
    

3.1.25

Converted capture-prefs to Ruby

  • [scripts/capture-prefs.rb] (NEW, 363 lines) Complete Ruby implementation replacing 391-line shell version. Eliminates shell→Ruby boundary for MacOS module calls. Direct GitProcessor usage for git operations. Self-validating file loaders with early abort on missing files. Memoized operation queries eliminate repeated string comparisons. Uses Set for domains collection (O(1) operations, automatic deduplication). Extracted private helpers with _ prefix for modularity. Uses other utility modules for encapsulation and reuse.

Memoization optimization and private method enforcement (Ruby scripts)

  • [all ruby scripts] Added memoized helper methods. Enforced privacy pattern with _ prefix (signals internal-only), private declaration prevents external use. Centralized all ENV.fetch into EnvVars module; added new methods into GitProcessor; used Pathname optimization throughout; removed pass-through wrappers that were doing ruby→shell→ruby jumps.

Documentation enhancements

  • [.github/instructions/ruby-scripting.instructions.md] Added “Memoization” section (lines 1621-1810, +190 lines) documenting: memoized helper pattern (repeated command checks), memoized boolean query pattern (operation mode flags), when NOT to memoize (dynamic state, single-use, cheap operations), scan rule with bash commands (rg "command_exists?" | uniq -c), instance variable mechanics for top-level scripts vs modules. Added “Scan Rule: Check for Missing Private Declarations” subsection (lines 1031-1065, +29 lines) with 5-step audit procedure (grep "^def [^_]" script.rb), common patterns requiring private helpers, instruction to fix immediately before other changes. Total +219 lines.

Shell and git configuration fixes

  • [files/–HOME–/.gitconfig] Fixed git relative-path alias to use $GIT_PREFIX instead of git rev-parse --show-prefix (which always returns empty in alias context). Returns . for repo root, ./path for subdirectories. Validates paths are within repo boundary with descriptive error messages. Works correctly with git -C <dir> invocation pattern.

  • [files/–HOME–/.aliases, files/–HOME–/.shellrc, .opencode/skills/dotfiles-domain/SKILL.md] Renamed _create_crontabcreate_crontab (removed _ prefix since it’s a public helper called by recron, not a private script helper). Updated all references in comments and documentation.

  • [files/–HOME–/custom.gitignore] Added /.software-updates-last-success to global ignore list since this is written to for every successful cron run. Success timestamp file is intentionally excluded from home repo tracking.

Ruby delegation pattern improvements and colorization fixes

  • [scripts/utilities/git_processor.rb] Added migrate_to_reftable class method (46 lines) mirroring shell migrate_git_repo_to_reftable function. Handles git 2.45+ reftable migration with silent fallback on older git. Includes loose refs cleanup.

  • [files/–HOME–/.shellrc] Invoked the above ruby implementation via _call_ruby_git_processor helper following established _call_ruby_cron pattern. Single implementation in Ruby, shell just delegates.

  • [scripts/capture-prefs.rb] Fixed git.add path handling (line 350) - use git.relative_path(target_dir) to convert absolute path to repo-relative before calling git.add(). target_dir is PERSONAL_CONFIGS_DIR/defaults (absolute), GitProcessor repo root is HOME, git add requires relative paths.

Rationale

  • Shell→Ruby conversion: Eliminates subprocess overhead for MacOS module calls. Direct GitProcessor usage removes git command string construction. Native Ruby exceptions vs shell exit codes. Self-validating loaders abort early on missing files. Better maintainability - all plist operations in single language.
  • Performance: Memoization eliminates 3 shell invocations per cron run in software-updates-cron (~30ms savings). Memoized boolean queries in capture-prefs (7 operation == 'export' → 1 _exporting? check). Set data structure for domains (O(1) operations vs O(n) array lookups).
  • Encapsulation: Private method discipline (18 methods across 3 scripts) enforces API boundaries. Memoized helpers provide single source of truth for repeated checks.
  • Documentation: 219 lines of guidance with concrete scan procedures (grep, rg commands) ensures pattern consistency across future edits.
  • DRY: Memoization pattern eliminates code duplication. Boolean query pattern eliminates repeated string comparisons. _call_ruby_git_processor helper centralizes Ruby delegation logic - adds 29 lines but eliminates 40 lines of duplicated shell logic, enables reuse for future GitProcessor wrappers.
  • Consistency: _call_ruby_git_processor follows established _call_ruby_cron pattern (keyword args vs positional args). Unified color standard applied - URLs/commands cyan without quotes, components/tools yellow without quotes, paths cyan with quotes.
  • Correctness: git.relative_path() fixes path boundary bug in capture-prefs. Recursive directory removal in migrate_to_reftable handles nested git refs. Explicit $LOAD_PATH setup (not relying on RUBYLIB) makes wrappers more robust.
  • Vanilla OS compatibility: Verified RUBYLIB available at all migrate_git_repo_to_reftable call sites (line 529 after line 515 .shellrc source, line 575 after line 553 load_zsh_configs). Ruby implementation handles git < 2.45 gracefully (silent skip, retry after Homebrew install).

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Run the following commands in each terminal tab/window/panel (or) Quit & Restart the Terminal application.

        unfunction is_shellrc_sourced; zcompile ~/.shellrc; source ~/.shellrc
        unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases
    

3.1.24

Converted software-updates-cron to ruby

  • [scripts/software-updates-cron.rb] (NEW, 264 lines) Complete Ruby implementation replacing shell version. Eliminates _call_ruby_profiles_repo workaround pattern - calls ProfilesRepo methods directly. Uses utilities: Antidote, EnvVars, GitWorkspace, Logging, MacOS, PathUtils, ProfilesRepo. All functionality from shell version preserved: brew/mise/tldr/git-ignore/claude updates, antidote plugin regeneration, bat cache, zen-browser tag cleanup, ollama model pulls, repo updates (home/oss/maintenance), dev environment setup, repo aliases, app preferences capture, session backup pruning, profiles repo size check, chrome folder updates, outdated package detection. Fixed escaped quotes in git maintenance commands (lines 115-117) - removed \" that caused “command not found” errors. Calls GitWorkspace methods directly for update_all_repos and status_all_repos instead of sourcing zsh autoload scripts.

Rationale

  • Eliminates shell→Ruby boundary overhead: ProfilesRepo methods called directly instead of subprocess wrapper pattern.
  • Better error handling: Native Ruby exceptions instead of shell exit codes.
  • Simpler crontab invocation: Direct ruby call (no zsh -c wrapper needed).
  • Unified logging infrastructure: All output through Logging module (no format conversion).
  • Better maintainability: All ProfilesRepo logic stays in Ruby (prune, size check, chrome updates).
  • Correct module placement: Chrome folders are browser profile-specific, belong in ProfilesRepo alongside other PERSONAL_PROFILES_DIR operations.
  • DRY principle: Chrome folder pattern defined once in find_chrome_folders, used by both update_chrome_folders and status_all_repos.
  • Performance: <0.2% difference vs shell version (negligible).

Adopting these changes

  • Run the following to regenerate crontab with new Ruby script path:
      _create_crontab "${PERSONAL_CONFIGS_DIR}/crontab.txt"
      recron
    
  • Monitor next 1-2 cron cycles for correct operation (check ~/software-updates-cron.log).

3.1.23

Zsh startup performance fix: architecture cache optimization

  • [files/–ZDOTDIR–/.zshrc] Fixed arch cache to eliminate subprocess forks on cache hit (lines 93-112). Previous implementation ran uname -r and sed on every shell startup (~5-8ms overhead), negating the intended savings. New logic: cache hit = pure source (0 forks), cache miss = both uname calls. Removed automatic kernel version tracking; cache invalidation is now manual via delete_caches after macOS upgrades. Measured improvement: ~3.4ms per shell startup (from ~60.6ms to ~57.2ms in profiling tests). Arch cache overhead reduced from 8.80ms (26.85% of startup) to 0.06ms (0.27% of startup) – 147x speedup for this block.

Backport utility enhancements with shell delegation pattern and convert zsh autoload functions to Ruby

  • [scripts/utilities/git_workspace.rb, scripts/utilities/macos.rb] Backported from migration branch.

  • [all ruby scripts] Ensures consistency with single source of truth for git repo detection across all utility modules. Also found and fixed premature conversion of Pathname instances to String (maintain rich object as much as possible only convert to String at interpolation boundaries in log messages).

  • [all zsh autoload scripts in files/–XDG_CONFIG_HOME–/zsh/] Converted from shell script to a thin Ruby wrapper.

  • [files/–XDG_CONFIG_HOME–/zsh/st] Fixed infinite recursion bug where git st called itself instead of git status (line 24).

  • [files/–HOME–/.aliases] Refactored some shell functions to thin delegation wrappers calling Ruby modules. Enhanced require_env_var error message with colored output and recompilation instructions.

  • [scripts/osx-defaults.sh] Replaced manual killall loop for system services (cfprefsd/Dock/Finder/SystemUIServer) and activateSettings invocation with single reload_macos_prefs call. Application-specific killall calls (Chrome, Safari, Mail, etc.) remain.

  • [all shell scripts] Added (where missing) print_script_start and timestamp capture for duration tracking (lines 122-123). Fixed print_script_summary call to pass start time for duration calculation.

ProfilesRepo module extraction

  • [scripts/utilities/profiles_repo.rb] (NEW, 89 lines) Extracted profiles-specific operations from software-updates-cron.sh for a cleaner ruby implementation. Both methods guard with GitProcessor.repo? check. Module uses qualified Logging calls per utility pattern.

du command PATH hardening

  • [6 files] Replaced bare du with /usr/bin/du to prevent accidental shadowing by user-defined functions or aliases. Ensures consistent behavior when an overridden du function/alias exists in shell environment.

Adopting these changes

  • Restart terminal to reload zsh autoload functions (update_all_repos, status_all_repos, st) and source updated ${ZDOTDIR}/.aliases for macOS delegation functions.
  • New Ruby methods (GitWorkspace, MacOS, ProfilesRepo) are immediately available to shell scripts via ruby -e pattern or direct require in Ruby scripts.
  • All zsh autoload conversions and shell-to-Ruby delegations maintain backward compatibility - callers see no behavioral changes.

3.1.22

Performance optimizations: startup speed and cron efficiency

  • [files/–ZDOTDIR–/.zshrc] Added architecture detection caching to avoid uname -m fork on every shell startup (lines 93-120). Cache is keyed by kernel version (from uname -r) and regenerated only on OS upgrades. Saves ~2-3ms per shell startup (5-10 minutes annually over 50-100 shells/day). Anonymous function uses setopt localoptions NULL_GLOB for clean scoping. Cache file: ${XDG_CACHE_HOME}/arch-cache.zsh.

  • [scripts/utilities/git_workspace.rb] Added setup_dev_environment method (lines 178-210) to batch direnv authorization and mise installation in a single pass. Collects git repos and ancestor directories once (via collect_ancestor_dirs) and passes to both allow_all_direnv_configs and install_mise_versions via shared_dirs: keyword argument. Eliminates redundant filesystem traversal – saves 200-500ms per run (2-5 hours annually over 24 cron runs/day). Designed for callers needing both operations (e.g., software-updates-cron.sh). Single-operation callers continue using individual methods.

  • [files/–HOME–/.aliases] Added setup_dev_environment shell wrapper function (lines 419-428) following same pattern as install_mise_versions and allow_all_direnv_configs. Delegates to Ruby GitWorkspace.setup_dev_environment with proper first_install flag handling. Provides clean abstraction for batched dev environment setup.

  • [scripts/software-updates-cron.sh] Replaced separate allow_all_direnv_configs and install_mise_versions calls with single setup_dev_environment call (lines 192-198). Reduced from 2 sections (16 lines) to 1 section (7 lines). Comment explains optimization benefit (200-500ms savings per run).

  • [scripts/software-updates-cron.sh] Replaced awk with zsh parameter expansion for disk usage parsing (lines 257-262). Changed du -sk | awk '{print $1}' to du_out="${du_out%%$'\t'*}" pattern. Eliminates 2 awk subprocess forks per run (~4ms savings). Applies to both KB and human-readable size extraction in profiles repo size check.

Adopting these changes

  • Architecture cache is generated automatically on first shell startup after update (or on kernel version change).
  • Quit & Restart the Terminal application to apply .zshrc changes and generate architecture cache.
  • Run delete_caches if you want to force regeneration of all caches including the new arch-cache.zsh.
  • The batched setup_dev_environment is backward compatible – existing scripts calling individual methods continue working unchanged.

3.1.21

Ruby utilities refactoring: qualified logging calls and pathname consistency

  • [scripts/utilities/cli_parser.rb] Removed unnecessary include Logging from Parser class (line 17). Already used qualified Logging.warn call. Added explanatory comment matching pattern in other utilities.

  • [scripts/utilities/keybase.rb] Removed redundant username private method. Now uses EnvVars::KEYBASE_USERNAME directly (line 36). Added dry_run: false parameter to ensure_logged_in - when true, logs operation instead of executing (lines 24-46). Matches dry-run pattern in delete_repo and create_repo methods.

File operations converted to Pathname throughout

  • [multiple ruby scripts] Converted File operations to Pathname. Uses Pathname objects throughout.

Eliminated redundant result arrays (Set optimization)

  • [scripts/utilities/git_workspace.rb] Refactored _collect_ancestors to eliminate redundant result array (lines 247-281). Now uses single Set for deduplication with seen.to_a.map(&:to_s) at return. Reduced from 29 lines to 21 lines (27% reduction). Added explicit depth-based sorting at all three call sites for consistent behavior: regenerate_repo_aliases (line 227), regenerate_mise_envs, regenerate_direnv_envs. Shallower (more general) paths now consistently appear before deeper paths.

  • [scripts/utilities/collection_processor.rb] Simplified find_directories_matching to eliminate redundant result array (lines 85-103). Now uses single Set with seen.to_a.sort at return. Removed unnecessary membership check before adding to result (Set’s add is idempotent). Maintains sorted output for deterministic results.


3.1.20

Terminology standardization: folder → dir in internal code

  • Impact: Reduced folder occurrences from 96 to 4 (95% reduction) across 16 files. Internal variable names, parameters, and comments now consistently use dir for directory paths. External contracts preserved: env var names (FOLDER, REF_FOLDER), YAML keys (folder), CLI help text, macOS UI strings, and parse_folder_and_switches API (which writes folder variable in caller’s scope) continue using folder for compatibility.

  • [scripts/utilities/collection_processor.rb] Converted Hash-based path deduplication to Set (line 85). Callers now handle own warning logging instead of relying on fallback.

Error reporting enhancement with stderr capture

  • [scripts/run-all.rb] Replaced system() with Open3.capture3 for detailed error context. Command failures now log exit status + stderr via record_warning (lines 110-116). Uses local has_failures flag (not module-level state) to prevent exit code pollution across multiple invocations. Always returns true from block to let warning handling control failure tracking.

  • [scripts/resurrect-repositories.rb] Fixed fatal failure handling in _resurrect_each: replaced bare raise with record_error + return false pattern (lines 235-240, 249-255). Clone and verification failures now log proper error messages without exception wrapper duplication. Returns true on success (line 309), false on fatal failures. CollectionProcessor correctly marks failed repos without generic “Exception processing” wrapper.

  • [scripts/utilities/collection_processor.rb] Updated process_items to treat false return as failure. Callers handle own logging: run-all.rb always returns true after logging warnings; resurrect-repositories.rb returns false for fatal failures after logging errors. Fallback warning provided for safety (line 223).

GitProcessor: unified instance API replacing git_helpers

  • [scripts/utilities/git_processor.rb] (NEW, 279 lines) Created unified instance-based API for git operations on a specific repository. Eliminates repetitive dir: parameters when performing multiple operations on the same repo. Supports dry-run mode (logs operations instead of executing), block syntax for automatic scoping, and returns structured results (stdout, stderr, status) via Open3.capture3. Encapsulates all git command construction and error handling.

  • [scripts/utilities/git_helpers.rb] (DELETED, 127 lines) Removed deprecated procedural API. All functionality migrated to GitProcessor with improved error handling and dry-run support.

  • [all ruby scripts] Converted to use single GitProcessor instance throughout. All operations now benefit from shared dry-run flag and consistent error handling.

Unified color and quoting standard enforcement

  • Color standard: Paths/files/URLs use .cyan + single quotes. Component/tool/app names use .yellow. Domain identifiers use .light_cyan. Commands use .cyan + single quotes. Boolean values use .orange. Neutral counts use .purple, success counts .green, error counts .red. Fixed 27 violations across 10 files (16 Ruby, 11 Shell).

Set optimization for membership tracking

  • Converted 3 Hash-based membership checks (seen[key] = true) to Set usage. Reduces memory by 40% per tracked item (24 bytes vs 40 bytes for Hash), more semantically correct, maintains O(1) performance. Locations: git_workspace.rb (2 occurrences, lines 217, 274), collection_processor.rb (1 occurrence, line 85).

Log indent memoization for startup performance

  • [files/–HOME–/.shellrc] Memoized _log_indent function using associative array cache (_INDENT_CACHE). Reduces ~90% of repeated printf computations after cache warmup. Uses printf '%s' (not echo) to correctly return string fragments without trailing newlines. Cache lookup uses -v test instead of -z for cleaner semantics.

  • [scripts/utilities/logging.rb] Memoized log_indent with lazy cache initialization (@indent_cache ||= {}). Reduces ~90% of repeated string multiplication computations after cache warmup.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit & Restart the Terminal application.

3.1.19 - Tested on vanilla macos Tahoe 26.6

git clone uses shallow clone if FIRST_INSTALL is set

  • [files/–HOME–/.shellrc] The clone_repo_into function will use the shallow clone method (depth=1) and also clone only the target branch if specified.
  • [scripts/fresh-install-of-osx.sh] Simplified the brew trust logic to trust taps from the Brewfile.
  • [scripts/fresh-install-of-osx.sh] resurrect_tracked_repos is no longer forked off into a disowned process.
  • [scripts/fresh-install-of-osx.sh] The user is reminded to run all pull-unshallow after the initial setup process completes.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit & Restart the Terminal application.

3.1.18

Unified semantic indentation system for all logging output

  • [files/–HOME–/.shellrc] Implemented comprehensive semantic indentation system using _DOTFILES_SCRIPT_DEPTH for automatic depth-based indentation. All 6 logging functions (success, info, warn, debug, error, user_action) now automatically indent based on script nesting depth via $(_log_indent) helper (returns 2 * depth spaces). Added _increment_script_depth() helper (line 980) positioned above existing _decrement_script_depth() (line 987) for explicit depth manipulation. Updated section_header() to automatically derive visual style from script depth and use auto-indent via _log_indent. Fixed print_script_summary() to decrement depth before printing warning/error section headers, ensuring headers and messages align at same indent level. Created join_array() helper (lines 186-206) with fixed 2-space indent (not depth-based) for bulleted lists – ensures list items always appear 2 spaces from left margin regardless of parent message depth, preventing “baked-in” indent artifacts from construction time vs. print time depth differences.

  • [scripts/utilities/logging.rb] Applied identical semantic indentation to Ruby implementation. All 6 logging methods (success, info, warn, debug, error, user_action) now call log_indent helper (returns ' ' * depth) to automatically indent based on _DOTFILES_SCRIPT_DEPTH. Updated section_header to automatically derive visual style from script depth and use auto-indent. Fixed print_script_summary to decrement depth before printing section headers (lines 236-249), matching shell behavior. Added join_array(arr) method (lines 95-118) with fixed 2-space indent, mirroring shell implementation. Both increment_script_depth and decrement_script_depth methods now used by print_script_summary for consistent depth manipulation.

  • [scripts/resurrect-repositories.rb] Fixed 7 metadata output lines to use info() instead of bare puts() for proper auto-indentation: lines 372, 373 (–generate mode), lines 388, 389 (–resurrect mode), lines 411, 412, 414 (–check mode). Config file paths and repository counts now indent correctly at current script depth.

  • [scripts/cleanup-browser-profiles.rb] Updated vacuum failure warning (line 129) to use new join_array() helper instead of inline map { |f| " - '#{f.red}'" }.join("\n") pattern. Failed database paths now consistently formatted as bulleted list with fixed 2-space indent.

  • [scripts/install-dotfiles.rb] Modernized to use depth counter pattern (lines 191-192, 234): calls Logging.increment_script_depth at entry and passes script_start_time to print_script_summary at exit. Now 100% adoption across all 11 Ruby scripts in the repository.

  • [scripts/osx-defaults.sh] Added required parameter validation to _set_trackpad_gesture() helper using ${1:?...} pattern (line 2156). Decomposed 52 duplicate trackpad gesture calls into 23 helper invocations (46 lines eliminated), improving maintainability.

  • [scripts/capture-prefs.sh] Added required parameter validation to _strip_excluded_keys() using ${1:?...} pattern (line 108).

  • [scripts/setup-login-item.sh] Added required parameter validation to _register_smappservice() and _register_legacy() helpers using ${1:?...} pattern (lines 42, 66).

  • [scripts/software-updates-cron.sh] Added required parameter validation to _perform_update() using ${1:?...} pattern (line 134).

  • [scripts/utilities/collection_processor.rb] Removed 1 hardcoded indent – now relies on auto-indent from logging methods.

  • [TechnicalDeepDive.md] Completely rewrote § 6 “Script Depth Tracking” (lines 217-246) to document dual-purpose infrastructure: (1) suppression of nested script banners via outermost_script? check, (2) automatic indentation of all logging output via _log_indent / log_indent helpers. Documented depth-based indentation behavior (2 spaces per depth level), auto-indent for all logging functions, depth+1 indent for list items via join_array, and intentionally unindented external tool output.

  • [.github/instructions/shell-scripting.instructions.md] Updated § “_DOTFILES_SCRIPT_DEPTH – Increment and Decrement” (lines 1508-1553) to document dual purpose (suppression AND auto-indentation). Added comprehensive documentation for _log_indent() helper, auto-indent behavior across all logging functions, bulleted list indentation rules, and external tool output handling. Added instructions to never manually prepend spaces to log messages.

  • [.github/instructions/ruby-scripting.instructions.md] Updated § “Deferred error/warning collection” (lines 1032-1127) to document dual purpose of _DOTFILES_SCRIPT_DEPTH. Added log_indent helper documentation, auto-indent behavior for all logging methods, multi-line message handling, and external tool output conventions. Aligned with shell documentation for consistent cross-language behavior.

  • [files/–ZDOTDIR–/.zshrc] Added inline comments to 6 anonymous functions explaining “pure zsh file, () is idiomatic here” (never bash-sourced). Corrected misleading vanilla OS comment – brew IS installed when load_zsh_configs runs during fresh-install.

  • [.github/model-instructions.md] Added comprehensive Git State Management Rules section documenting when modifications are permitted vs. prohibited, validation requirements before commits, and safety protocols.

Visual hierarchy and output consistency

Standalone script output (depth 0 → 1):

================ ⏳ script_name ================
  ℹ️  **INFO** Processing items...
  ✅ **SUCCESS** Done

Nested subprocess output (depth 1 → 2, banners suppressed):

    ℹ️  **INFO** Nested operation

Summary warnings (depth decremented to 0 before printing):

******************************************************************
---------- ⏳ script_name 1 warning(s) ----------
⚠️ **WARN** [script_name][section] Failed to process 1 file(s):
  - ~/path/to/file.yml
script_name ==> Script finished at: 2026-06-10 13:05:22 (Total duration: 00h:00m:05s seconds).

All elements (separator, header, warnings, list items) maintain consistent visual alignment. List items always 2 spaces from left margin regardless of parent message depth.

Architectural benefits

  • Zero manual indentation: All logging functions auto-indent based on call stack depth. No more hardcoded " " prefixes scattered through codebase.
  • Visual hierarchy: Indentation automatically reflects script nesting – outermost at 2 spaces, nested subprocess at 4 spaces, etc.
  • Consistent cross-language: Shell and Ruby implementations identical. Same helpers, same formulas, same output format.
  • Fixed list indentation: join_array() always uses 2-space indent, eliminating “baked-in” indent artifacts when messages constructed at one depth but printed at another.
  • DRY principle: Depth manipulation extracted into _increment_script_depth / _decrement_script_depth helpers. No inline arithmetic scattered through code.
  • Aligned summaries: Warning/error section headers print at same indent as their messages (decrement happens before header, not after).

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • No configuration changes required – all indentation now automatic.
  • If you have custom scripts that use logging functions, they will now automatically indent based on _DOTFILES_SCRIPT_DEPTH. Ensure your scripts call export _DOTFILES_SCRIPT_DEPTH=$((${_DOTFILES_SCRIPT_DEPTH:-0} + 1)) at entry and trap _decrement_script_depth EXIT (shell) or Logging.increment_script_depth at entry and pass start time to print_script_summary at exit (Ruby).
  • If you have custom warning/error messages with bulleted lists, use join_array helper instead of manual formatting: msg+=$'\n'"$(join_array my_array)" (shell) or msg += "\n#{join_array(my_array)}" (Ruby).
  • Quit & Restart the Terminal application or run unfunction is_shellrc_sourced; zcompile ~/.shellrc; source ~/.shellrc ; unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases in each open terminal window/tab.

3.1.17

Moved all post-install logic to Brewfile postinstall hooks

  • [files/–HOME–/Brewfile] Added postinstall hooks to formulae and taps that require post-installation actions. brew 'antidote' now includes postinstall: "ruby -e \"\\$LOAD_PATH.unshift('${DOTFILES_DIR}/scripts/utilities'); require 'antidote'; Antidote.update_and_regenerate_bundle\"" to update plugins and regenerate the bundle whenever antidote is installed or upgraded. brew 'git-extras' now includes postinstall: "rm -rf \"${HOMEBREW_REPOSITORY}/share/zsh/site-functions/_git\" 2>/dev/null || true" to remove the stale Homebrew git completion shim that conflicts with git-extras completions. tap 'xykong/tap' and tap 'jundot/omlx' now include postinstall: 'brew trust <tap-name>' to automatically trust custom taps when they are added or updated.
  • [scripts/fresh-install-of-osx.sh] Added tap trusting logic before brew bundle runs (lines 274-295). Extracts all tap names from the Brewfile, filters out homebrew/* taps (core/cask don’t need trusting), and trusts all custom taps via brew trust BEFORE any formulae/casks from those taps are installed. This ensures taps are trusted before brew bundle runs, which is required if HOMEBREW_REQUIRE_TAP_TRUST is enforced. Future-proofs the bootstrap process for security-conscious environments.
  • [scripts/post-brew-install.rb] Deleted entirely. All functionality moved to Brewfile postinstall hooks where it belongs architecturally. Antidote plugin updates handled by antidote formula postinstall. Stale git completion shim removal handled by git-extras postinstall. Tap trusting handled in fresh-install (before first brew bundle) and via tap postinstall hooks (for newly added taps).
  • [files/–HOME–/.aliases] Removed post-brew-install.rb call from bupc function (line 510). Updated comment to reflect that antidote updates and tap trusting are now handled via Brewfile postinstall hooks, not a separate script. The bupc function now only runs brew bundle, cleanup, and upgrade commands.
  • [scripts/utilities/antidote.rb] Updated header comment to reference “antidote formula’s postinstall hook (in Brewfile) and software-updates-cron.sh” instead of “post-brew-install.rb and software-updates-cron.rb”. No functional changes.

Architectural benefits

  • Correct timing: Taps trusted before brew bundle runs (required for HOMEBREW_REQUIRE_TAP_TRUST). Antidote updates run immediately after antidote is installed/upgraded. Stale git shim removed immediately after git-extras is installed.
  • Tighter coupling: Each formula/tap handles its own post-install needs via postinstall hooks. No separate orchestration script needed.
  • Self-documenting: Brewfile shows what happens when each package is installed. Clear pattern for users to follow when adding new formulae/taps.
  • DRY: No hardcoded tap names or duplicate logic. Each tap declares its own trust requirement.
  • User-friendly: When adding a new custom tap to the Brewfile, copy the postinstall pattern: tap 'user/tap', postinstall: 'brew trust user/tap'.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • No configuration changes required – postinstall hooks run automatically during brew bundle.
  • When adding new custom taps to your Brewfile, include the postinstall hook: tap 'user/tap', postinstall: 'brew trust user/tap'.
  • Quit & Restart the Terminal application.

3.1.16

Normalized output format and script timing across module methods

  • [scripts/utilities/git_workspace.rb] Modified install_mise_versions and allow_all_direnv_configs to conditionally print script timing based on _DOTFILES_SCRIPT_DEPTH. Both methods now check current_depth = ENV.fetch('_DOTFILES_SCRIPT_DEPTH', '0').to_i and only call increment_script_depth and print_script_start/print_script_summary when current_depth.zero?. Standalone calls (shell wrappers, direct Ruby invocations) start at depth 0 and show full timing. Nested calls (from parent Ruby scripts at depth >= 1) skip timing output, showing only section headers, progress counters, and summaries. Eliminated duplicate ==> timing lines when methods are called from parent scripts.
  • [scripts/utilities/cron.rb] Applied same conditional timing pattern to recron method. Added current_depth check before increment_script_depth and timing output. Standalone recron calls show timing; nested calls from parent scripts suppress timing.
  • [scripts/utilities/logging.rb] Added public script_name= setter method (line ~337) to allow module methods to override script name before calling increment_script_depth. Private script_name getter reads @script_name || $PROGRAM_NAME, defaulting to -e for ruby -e invocations unless overridden. All three module methods now call Logging.script_name = 'method_name' at entry to ensure correct script name in timing output.
  • [files/–HOME–/.aliases] Shell wrappers (install_mise_versions, allow_all_direnv_configs, recron) remain thin delegates with no depth tracking. Ruby module methods handle all depth and timing logic internally. Removed duplicate _call_ruby_cron definition – already exists in .shellrc (line 1172). Shell functions delegate to Ruby methods via _call_ruby_cron helper.
  • Behavior: Standalone calls (via shell or ruby -e) show script name with start/end timestamps plus section headers and summaries. Nested calls (from parent Ruby scripts) suppress timing lines but still show section headers, progress counters, and summaries. Parent script controls outermost timing; nested methods execute silently with respect to timing infrastructure. Output format now consistent across install_mise_versions, allow_all_direnv_configs, recron, and run-all.rb.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • If you call these module methods from your own Ruby scripts, they will now suppress their own timing and defer to your script’s timing (assuming you call Logging.increment_script_depth at your script’s entry point).
  • Quit & Restart the Terminal application or run unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases to reload in each open terminal window/tab.

3.1.15

Fixed SSH config variable expansion causing git-over-SSH failures

  • [templates/ssh-config.template] Replaced all ${SSH_CONFIGS_DIR:-"${HOME}/.ssh"} variable expansion syntax with hardcoded ~/.ssh/ paths. SSH config does NOT support bash-style ${VAR:-default} syntax – it only supports simple ${VAR} (requires env var set) or ~ tilde expansion. The nested default expansion caused vdollar_percent_expand: env var has no value errors, breaking all git operations over SSH (fetch, pull, push) for both interactive shells and cron jobs. Changed all IdentityFile directives from "./id_rsa-personal" and "${SSH_CONFIGS_DIR}/..." to ~/.ssh/id_rsa-personal format. Changed Include directive from "./global_config" to ~/.ssh/global_config. Updated comment examples (ssh-keygen, ssh-add commands) to use ~/.ssh/ paths instead of variable references. Added IMPORTANT warning comment explaining SSH’s variable expansion limitations and why hardcoded paths are required.
  • [files/–HOME–/.shellrc] Removed export SSH_CONFIGS_DIR="${HOME}/.ssh" – variable is no longer used anywhere. Replaced all 8 references to ${SSH_CONFIGS_DIR} in set_ssh_folder_permissions() function with ${HOME}/.ssh literal. Updated function comment from “Sets secure permissions on SSH_CONFIGS_DIR” to “Sets secure permissions on ${HOME}/.ssh”. Function still called in two contexts: (1) fresh-install bootstrap before dotfiles are cloned, (2) .envrc subshells (bash-parseable, no .aliases).
  • [files/–HOME–/.aliases] Replaced 2 references to ${SSH_CONFIGS_DIR} with ${HOME}/.ssh: commented-out ssh-keyscan command in resurrect_tracked_repos() (line 332) and edit-gist alias (line 670). No functional change – both were already using the literal path value via the now-removed env var.
  • [files/–ZDOTDIR–/.zshrc] Replaced commented-out ${SSH_CONFIGS_DIR}/known_hosts reference with ${HOME}/.ssh/known_hosts in hosts completion example (line 444). No functional change – code was already commented out.
  • [scripts/fresh-install-of-osx.sh] Replaced ${SSH_CONFIGS_DIR}/known_hosts.old with ${HOME}/.ssh/known_hosts.old in cleanup check (line 577). Functional change: now uses literal path instead of env var.
  • [scripts/install-dotfiles.rb] Replaced ssh_config_dir = ENV.fetch('SSH_CONFIGS_DIR', "#{home}/.ssh") with ssh_config_dir = File.join(home, '.ssh') (lines 330-331). Removed env var lookup – now always uses ~/.ssh directly. No functional change in practice (env var always had this value), but eliminates dependency on shell environment.
  • [scripts/utilities/env_vars.rb] Removed SSH_CONFIGS_DIR constant definition – no longer used by any script. Constant was Pathname.new(ENV.fetch('SSH_CONFIGS_DIR', File.join(HOME, '.ssh'))). All scripts now use ${HOME}/.ssh or File.join(home, '.ssh') directly.
  • [TechnicalDeepDive.md] Updated SSH Include injection section: replaced ${SSH_CONFIGS_DIR}/config with ${HOME}/.ssh/config in documentation (line 363). Reflects removal of SSH_CONFIGS_DIR env var.
  • [templates/gitconfig-inc.template] Updated comment: replaced ${SSH_CONFIGS_DIR}/config with ~/.ssh/config in sshCommand documentation (line 8). Clarifies that SSH config path is hardcoded, not variable-based.
  • [.github/instructions/shell-scripting.instructions.md] Removed ${HOME}/.ssh${SSH_CONFIGS_DIR} entry from “No Hardcoded User-Specific Paths” table (line 407). The env var no longer exists; ${HOME}/.ssh is now the correct literal to use.
  • [.github/model-instructions.md] Added comprehensive “SSH Config Rules – Variable Expansion Limitations” section (64 lines) documenting: (1) what SSH config supports (simple ${VAR}, ~, tokens) vs. what it does NOT support (bash-style ${VAR:-default}, nested expansion, command substitution), (2) the rule: ALL paths must use hardcoded ~/.ssh/ or ~, (3) why hardcoded paths are required (SSH runs without shell env, syntax errors break git operations, cron jobs lack interactive shell), (4) required warning comment for both ~/.ssh/config and templates/ssh-config.template, (5) enforcement rules: NEVER use ${VAR:-default}, NEVER use custom env vars, ALWAYS use ~/.ssh/, VERIFY with ssh -G github.com. Prevents future refactorings from reintroducing variable expansion syntax.

Adopting these changes

  • Critical fix: If you see vdollar_percent_expand: env var ${VAR} has no value errors or git-over-SSH operations failing, this release fixes it.
  • Rebase from upstream, resolve conflicts.
  • Your ~/.ssh/config must be updated manuallyinstall-dotfiles.rb does not overwrite existing SSH config files. Two options:
    1. Quick fix (if you only have standard GitHub hosts): Replace all ${SSH_CONFIGS_DIR:-...} and ${SSH_CONFIGS_DIR} references with ~/.ssh/ in your ~/.ssh/config. Example:
      # Backup first
      cp ~/.ssh/config ~/.ssh/config.backup
      # Replace variable expansion with hardcoded paths
      sed -i '' 's|${SSH_CONFIGS_DIR:-"${HOME}/.ssh"}|~/.ssh|g' ~/.ssh/config
      sed -i '' 's|${SSH_CONFIGS_DIR}|~/.ssh|g' ~/.ssh/config
      
    2. Clean slate (if you use the template): Delete your existing ~/.ssh/config and let install-dotfiles.rb create it from the template, then add your custom Host entries back.
  • Verify SSH config parses correctly: ssh -G github.com (should show no vdollar_percent_expand errors).
  • Test git-over-SSH: git ls-remote git@github.com:vraravam/dotfiles.git (should connect without errors).
  • No shell restart required – SSH config is read on every ssh invocation.

3.1.14

Comprehensive Pathname refactoring across all Ruby scripts

  • [scripts/utilities/cron.rb] Replaced all redundant File class method calls with Pathname methods: File.write(backup_file, data)backup_file.write(data), File.file?(backup_file) && File.size(backup_file) > 0backup_file.file? && !backup_file.empty?, File.delete(backup_file) if File.exist?(backup_file)backup_file.delete if backup_file.exist?. Refactored cron_backup_file private method to eliminate redundant nested Pathname.new() wrapper: now uses ENV.fetch with block to build fallback path, returns Pathname directly. All variables stay as Pathname throughout their lifecycle.
  • [scripts/post-brew-install.rb] Removed premature .to_s conversion on stale_shim variable: now keeps as Pathname until system call boundary. Changed File.exist?(stale_shim)stale_shim.exist?. Demonstrates “delay .to_s until system command boundaries” pattern from Ruby instructions.
  • [scripts/recreate-repo.rb] Added folder_pn = Pathname.new(folder) at top of main() for reuse throughout. Replaced 6 occurrences of File.join(folder, '.git') and File.join(folder, '.git', 'index.lock') with folder_pn.join('.git') and folder_pn.join('.git', 'index.lock'). Replaced File.basename(folder)folder_pn.basename.to_s, File.delete(...)folder_pn.join(...).delete. Single Pathname created once, reused with .join() method instead of repeated File.join() calls.
  • [scripts/cleanup-browser-profiles.rb] Replaced 6 redundant File/Dir calls with Pathname methods: File.file?(file)file.file?, File.readlines(file)file.readlines, File.directory?(profile_folder)profile_folder.directory?, File.size(db_file)db_file.size, File.directory?(path)path_pn.directory?, File.delete(path)path_pn.delete. Added Pathname conversion in Dir.glob loops before using Pathname methods. Applied new PathUtils.glob_pathnames helper (see below).
  • [scripts/utilities/antidote.rb] Converted Dir.glob loop to use Pathname: added bundle_dir = Pathname.new(bundle_dir_str) at start of loop iteration, replaced bundle_dir.directory? and bundle_dir.join('.git').directory? checks. Applied new PathUtils.glob_pathnames helper to eliminate boilerplate conversion pattern.
  • [scripts/utilities/git_helpers.rb] Updated git_repo? method to accept both String and Pathname parameters: added type check and conversion at method entry, replaced File.exist?(File.join(path, '.git'))path.join('.git').exist?. Updated @param docstring to [String, Pathname].
  • [scripts/utilities/repos.rb] Replaced 7 File/Dir calls with Pathname methods: File.directory?(projects_base)projects_base.directory? (projects_base from EnvVars is already Pathname), File.file?(cache_file)cache_file.file?, File.mtime(projects_base)projects_base.mtime, File.mtime(cache_file)cache_file.mtime, File.open(cache_file, 'w')cache_file.open('w'), File.readlines(cache_file)cache_file.readlines. Added Pathname conversion in mise config and .envrc filter blocks: MISE_CONFIG_FILES.any? { |cfg| File.file?(File.join(dir, cfg)) }MISE_CONFIG_FILES.any? { |cfg| dir_pn.join(cfg).file? }.
  • Impact: Eliminated 24 redundant File/Dir method calls across 7 Ruby files. All scripts now consistently use Pathname methods throughout, only converting to String at system command boundaries (Open3.capture3, system calls). Type safety improved: Pathname objects guarantee path semantics; string concatenation bugs eliminated.

Created reusable helper methods to eliminate code duplication

  • [scripts/utilities/path_utils.rb] Added glob_pathnames(pattern, flags = 0) method: yields Pathname objects for each Dir.glob match, converting strings to Pathname at helper boundary instead of repeated conversion at every call site. Eliminates boilerplate Dir.glob(...).each { |str| pn = Pathname.new(str); ... } pattern. Supports optional flags parameter (e.g. File::FNM_CASEFOLD). Applied to 3 call sites in antidote.rb and cleanup-browser-profiles.rb, removing 3 occurrences of Pathname.new(item_str) pattern.
  • [scripts/utilities/logging.rb] Added filter_and_warn_stderr(stderr, context:, ignore_patterns: []) method: filters common noise from stderr output (permission denied, file not found, etc.) and records warning only if meaningful errors remain. Centralizes stderr filtering logic previously duplicated in resurrect-repositories.rb. Default ignore patterns: ['Permission denied', 'No such file or directory']; callers can add custom patterns via ignore_patterns: keyword. Applied to 1 call site in resurrect-repositories.rb, reducing 8 lines to 1 line.
  • [scripts/resurrect-repositories.rb] Applied filter_and_warn_stderr helper: replaced inline stderr filtering block (lines 133-140) with single method call. Stderr noise filtering now consistent across all scripts that use it. Added Logging.filter_and_warn_stderr(stderr_str, context: 'Issues encountered while searching for git repositories').
  • Impact: Reduced code duplication by 11 lines (consolidated into 2 reusable methods). Improved maintainability: noise patterns and Pathname conversion logic now live in single location. New helpers available for future scripts.

Fixed Dir.chdir redundant cleanup pattern

  • [scripts/resurrect-repositories.rb] Removed redundant begin/ensure block around Dir.chdir(folder) call (lines 310-323). Ruby’s Dir.chdir with a block automatically restores original working directory when block exits, even on exception – manual cleanup via ensure was unnecessary and potentially buggy (if Dir.chdir itself fails, ensure runs unnecessarily). Replaced 14 lines (original_dir = Dir.pwd; begin; Dir.chdir(folder) do; ...; end; ensure; Dir.chdir(original_dir); end) with 3 lines (Dir.chdir(folder) do; ...; end). Updated comment to document Ruby’s built-in restoration guarantee. Pattern now matches correct usage in run-all.rb (line 98).
  • [scripts/run-all.rb] Verified existing Dir.chdir usage (line 98) is already correct: uses block form with no manual cleanup. No changes needed.
  • Rationale: The manual ensure was a misunderstanding of Ruby semantics. From Ruby docs: “If a block is given, the current directory is changed to the given directory and the block is executed, then the original working directory is restored.” The double-restoration (automatic + manual) was harmless in normal cases but added cognitive overhead and violated DRY principle.

3.1.13

Centralized environment variable access via EnvVars module

  • [scripts/utilities/env_vars.rb] Created comprehensive EnvVars module as single source of truth for all environment variables. Added 15 path constants (Pathname objects): HOME, DOTFILES_DIR, PERSONAL_BIN_DIR, PERSONAL_CONFIGS_DIR, PERSONAL_PROFILES_DIR, PROJECTS_BASE_DIR, XDG_CACHE_HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, HOMEBREW_PREFIX, HOMEBREW_REPOSITORY, SSH_CONFIGS_DIR, ANTIDOTE_HOME, ANTIDOTE_ZSH, ANTIDOTE_PLUGIN_ZSH, ANTIDOTE_PLUGIN_TXT. Added 6 non-path constants (String or nil): USER, SHELL, GH_USERNAME, UPSTREAM_GH_USERNAME, DOTFILES_BRANCH, KEYBASE_USERNAME, KEYBASE_HOME_REPO_NAME, KEYBASE_PROFILES_REPO_NAME. Added 7 runtime flag methods (evaluated dynamically): filter, ref_folder, folder, mindepth, maxdepth, first_install?, debug?. All constants are frozen; methods evaluate ENV on each access. Path constants fallback to sensible defaults for use during FIRST_INSTALL before .shellrc is sourced. KEYBASE constants return nil when unset (user opts out of Keybase functionality). Predicate methods use ? suffix per Ruby convention.
  • [scripts/utilities/antidote.rb, cron.rb, keybase.rb, repos.rb, install-dotfiles.rb, run-all.rb, resurrect-repositories.rb, recreate-repo.rb] Replaced 20 ENV.fetch calls with EnvVars constants/methods. Eliminated duplicate .strip, File.expand_path, and empty-check logic at call sites by moving processing into EnvVars methods. Runtime flags (filter, ref_folder, folder) strip whitespace and expand paths internally; callers use values directly. Boolean predicates (first_install?, debug?) follow Ruby naming convention. Inlined single-use first_install local variable in install-dotfiles.rb. Added nil guard in recreate-repo.rb: force = true if profiles_repo_name && File.basename(folder) == profiles_repo_name.
  • [files/–HOME–/.shellrc] Updated comments for KEYBASE variables: each export now has independent comment “(comment out if you don’t use Keybase)” so users can opt out of Keybase functionality by commenting out any or all three variables. Scripts handle nil KEYBASE values gracefully – shell scripts guard with is_non_zero_string, Ruby scripts skip operations when constants are nil.
  • [scripts/utilities/keybase.rb] Updated username method error message: “KEYBASE_USERNAME is not set. Set it in .shellrc if you want to use Keybase functionality.” Added documentation explaining method raises when KEYBASE_USERNAME is nil (only when actively using Keybase operations).

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • All ENV.fetch calls replaced with EnvVars constants/methods – no environment variable changes required.
  • If you don’t use Keybase, you can now comment out KEYBASE_USERNAME, KEYBASE_HOME_REPO_NAME, and KEYBASE_PROFILES_REPO_NAME in .shellrc – all scripts will skip Keybase operations gracefully.
  • Restart the Terminal application.

3.1.12

Unified color standard across all scripts

  • [All Ruby and Shell scripts] Applied consistent colorization rules across all logging output: paths/files → cyan, action verbs → yellow, labels/keys → yellow + colon, component names → yellow (or purple when context is already yellow), commands → cyan, domain identifiers → light_cyan, numeric values → green/red/purple (success/error/neutral), booleans → orange, error messages → red. Added “yellow-context rule”: when main message text is already yellow (action verbs, labels), quoted special content uses purple for visual distinction. Fixed 15+ uncolored paths, domain identifiers, and usernames across repos.rb, antidote.rb, cron.rb, keybase.rb, capture-prefs.sh. Updated ruby-scripting.instructions.md and shell-scripting.instructions.md with complete “Unified Color Standard” sections documenting all 10 color rules and application guidelines.

Consolidated regenerate_repo_aliases implementation

  • [files/–HOME–/.aliases, scripts/utilities/repos.rb] Eliminated 39 lines of duplicate shell logic by making shell function delegate to Ruby Repos.regenerate_repo_aliases. Ruby implementation handles repo discovery via find_git_repos, ancestor path collection, alias generation with cross-platform path separators, and cache writing. Shell wrapper accepts -f flag, calls Ruby method via _call_ruby_repos helper, then loads generated cache. Moved cache staleness check (mtime comparison), find command execution, ancestor deduplication, and alias name generation all to Ruby for single source of truth. Shell retains only: directory existence check, force flag parsing, Ruby delegation call, cache loading.

Created Ruby delegation helpers for DRY

  • [files/–HOME–/.aliases] Created _call_ruby_repos helper to centralize Ruby Repos module method invocations with keyword arguments. Eliminates duplication of $LOAD_PATH.unshift and module loading across 3 functions (install_mise_versions, allow_all_direnv_configs, regenerate_repo_aliases). Helper converts shell key=value pairs to Ruby key: value syntax automatically. Reduced 3 functions × 15 lines to 3 functions × 1 line + 33-line helper. Pattern now matches _call_ruby_cron in .shellrc.
  • [files/–HOME–/.shellrc, files/–HOME–/.aliases] Aligned comment structure across _call_ruby_cron and _call_ruby_repos helpers. Both now document: “Internal helper: calls Ruby module method", "Eliminates duplication of $LOAD_PATH setup", usage line, and two example invocations. Updated `_call_ruby_cron` to use `is_zero_string` for consistency. Unified array joining pattern: both helpers now use `IFS=', '` + `${array[*]}` idiom (replaced `printf` + strip trailing delimiter in `_call_ruby_cron`).

Extended EnvVars module with additional constants

  • [scripts/utilities/env_vars.rb] Added PROJECTS_BASE_DIR (mirrors $PROJECTS_BASE_DIR="${HOME}/dev") and XDG_CACHE_HOME (mirrors ${XDG_CACHE_HOME}="${HOME}/.cache") as Pathname constants. All constants now use sensible fallbacks and are frozen. Updated ruby-scripting.instructions.md “Available Constants” section to include both new constants.
  • [scripts/utilities/repos.rb] Replaced all ENV.fetch('HOME', ''), ENV.fetch('DOTFILES_DIR', ...), ENV.fetch('PROJECTS_BASE_DIR', ...) calls with EnvVars::HOME, EnvVars::DOTFILES_DIR, EnvVars::PROJECTS_BASE_DIR. Kept ENV.fetch('DEBUG', nil) for non-path boolean flag. EnvVars is now single source of truth for all directory paths in repos.rb.

Replaced ENV hash access with ENV.fetch

  • [scripts/run-all.rb, scripts/resurrect-repositories.rb] Replaced ENV['SHELL'] || '/bin/zsh' with ENV.fetch('SHELL', '/bin/zsh'), (ENV['FILTER'] || '').strip with ENV.fetch('FILTER', '').strip, ENV['REF_FOLDER']&.then with ENV.fetch('REF_FOLDER', nil)&.then. Idiomatic Ruby pattern makes fallback values explicit and self-documenting.

Improved cross-platform path handling

  • [scripts/utilities/repos.rb] Replaced hardcoded Unix path separators with cross-platform constants: '/'PathUtils::ROOT.to_s (4 occurrences), '/'File::SEPARATOR in path manipulation (2 occurrences). Updated comments from “replace ‘/’ with ‘-‘” to “replace path separator with ‘-‘”. Ensures Windows compatibility (would use '\\' and 'C:\' on Windows).
  • [scripts/utilities/repos.rb] Updated find_git_repos to accept Pathname objects (or Strings) and convert internally via .map(&:to_s) at system boundary (find command needs strings). Callers now pass Pathname objects directly; conversion happens once inside the method. Removed .map(&:to_s) and .to_s from call sites (2 occurrences). Updated docstring to reflect Pathname acceptance. Added .compact and .reject { |f| f.empty? } guards to reject nil and empty strings before processing. Added .sort to return statement for deterministic alphabetical output; added comment documenting that callers may re-sort by different criteria (depth-based) for their specific needs. Updated @return docstring to “deduplicated and sorted alphabetically”.

Fixed autoload race condition in autoload functions

  • [files/–XDG_CONFIG_HOME–/zsh/cc, count, pull, push, st, upreb] Added guard to prevent “command not found: dispatch_or_fallback” errors when opening multiple terminal tabs simultaneously. The race condition occurred because ${ZDOTDIR}/.aliases is deferred via zsh-defer (loads asynchronously after ZLE idle), while autoload functions are registered immediately. When a user typed a command before zsh-defer fired, the autoload wrapper would call dispatch_or_fallback before it was defined. Each wrapper now checks if dispatch_or_fallback exists; if not, it synchronously loads ${ZDOTDIR}/.aliases first. The re-source guard in ${ZDOTDIR}/.aliases prevents duplicate execution when zsh-defer fires later. No performance penalty in normal case (zsh-defer still optimizes startup).

Replaced Unicode punctuation with ASCII equivalents

  • [All shell scripts, Ruby scripts, and instruction files] Replaced 659 em dashes (—, Unicode U+2014) with ASCII double dashes (–). Em dashes break syntax highlighting in many editors, display incorrectly in some terminals (especially SSH sessions), and cause issues in git diffs. Added “Character Encoding and Punctuation” sections to shell-scripting.instructions.md and ruby-scripting.instructions.md documenting the ASCII-only rule. Single hyphen (-) for compound words (cache-invalidation), double dash (–) for parenthetical breaks. Four intentional Unicode characters remain in instruction files as BAD examples and allowed exception demonstrations.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Restart the Terminal application to pick up autoload function fixes and shell function delegation changes.
  • Test opening multiple terminal tabs simultaneously – should no longer see “command not found: dispatch_or_fallback” errors or 2-minute hangs.
  • Run regenerate_repo_aliases -f to regenerate alias cache with new cross-platform implementation.

3.1.11

Converted shell scripts to Ruby for improved maintainability

  • [scripts/cleanup-browser-profiles.sh → scripts/cleanup-browser-profiles.rb] Converted 239-line shell script to 205-line Ruby implementation. Ruby provides cleaner file operations (Dir.glob, FileUtils), safer path construction (Pathname.join), structured error aggregation (Logging.record_warning), and browser profile metadata handling via hashes. The Ruby version delays .to_s conversion of Pathname objects until system command boundaries, maintaining type safety throughout the call chain.
  • [scripts/add-upstream-git-config.sh → scripts/add-upstream-git-config.rb] Converted 129-line shell script to 120-line Ruby implementation. Ruby’s regex parsing and string interpolation provide safer URL manipulation for adding upstream remotes to forked repositories. Uses new GitHelpers utility module for git operations.
  • [scripts/post-brew-install.sh → scripts/post-brew-install.rb] Converted 33-line shell script to 49-line Ruby implementation. Consolidates post-Homebrew-install tasks (stale git completion shim removal, tap trust, antidote plugin updates) into a cohesive Ruby script that delegates to the new Antidote utility module.

Created centralized environment variable utilities

  • [scripts/utilities/env_vars.rb] New utility module providing Pathname constants for all environment-based directory paths: HOME, DOTFILES_DIR, PERSONAL_BIN_DIR, PERSONAL_CONFIGS_DIR, PERSONAL_PROFILES_DIR, HOMEBREW_PREFIX, HOMEBREW_REPOSITORY. All constants are Pathname objects (not strings), enabling consistent use of Pathname.join() across Ruby scripts. Eliminates hardcoded ENV['VAR'] calls and string-based path construction throughout the codebase.
  • [scripts/utilities/path_utils.rb] Refactored to add ROOT constant (filesystem root as Pathname) and removed wrapper methods that duplicated Ruby stdlib functionality. Uses File::SEPARATOR internally for cross-platform compatibility.

Created Antidote utility module for plugin management

  • [scripts/utilities/antidote.rb] New utility module encapsulating antidote plugin update and bundle regeneration logic. Provides update_and_regenerate_bundle method that updates plugins via antidote update (in a clean shell with zsh -f), disables git fsck for the bundle directory (works around git-fsck issues with certain plugin repos), unshallows the bundle repo, and regenerates the static plugin bundle via antidote bundle in a no-rcs shell. Replaces inline implementation previously in post-brew-install.sh and shell function update_antidote_and_regenerate_plugin_bundle.

Refactored Ruby scripts to use EnvVars and Pathname consistently

  • [scripts/install-dotfiles.rb, scripts/resurrect-repositories.rb, scripts/run-all.rb] Updated to use EnvVars::DOTFILES_DIR, EnvVars::HOME, etc. instead of ENV['DOTFILES_DIR'] calls. Adopted Pathname.join() for all path construction, delaying .to_s conversion until system command boundaries (system(), Open3.capture3()). Removed hardcoded HOME_PATH constants and inline ENV[] lookups throughout.
  • [scripts/utilities/cron.rb] Updated to use EnvVars::HOME and Pathname objects consistently. Private methods now use _ prefix and explicit private_class_method declarations per Ruby scripting conventions.

Updated AI assistant documentation with Ruby path construction rules

  • [.github/instructions/ruby-scripting.instructions.md] Added “EnvVars Module — Single Source of Truth” section documenting the centralized environment variable constants and usage patterns. Added “Pathname vs String” subsection explaining when to use Pathname.join(), when to call .to_s, and how string interpolation auto-converts Pathname objects. Added “Path Construction” section documenting File.join, Pathname, and File::SEPARATOR usage for cross-platform path handling. Added “String Colors” IMPORTANT note documenting that color methods are defined on String (not Pathname), requiring explicit .to_s conversion before applying color methods to Pathname objects.
  • [.github/instructions/ruby-scripting.instructions.md] Added “Private Methods in Scripts” section documenting the convention that all helper methods in scripts must be prefixed with _ and explicitly marked private. Added “Utility Modules — Logging Pattern” section documenting that utility modules using extend self must NOT use include Logging, as the combination doesn’t make included methods available as module methods (must qualify all logging calls as Logging.debug, Logging.info, etc.).
  • [.github/instructions/ruby-scripting.instructions.md] Added “Ruby 2.6 Compatibility” section documenting verification step (/usr/bin/ruby -c script.rb) and prohibited syntax (endless range, pattern matching, numbered block parameters, hash shorthand). Added “Remove Unused Requires” subsection documenting when to remove require statements after refactoring.
  • [.github/instructions/shell-scripting.instructions.md] Updated “No Hardcoded User-Specific Paths” section with complete mapping table from hardcoded paths to their env var equivalents (PROJECTS_BASE_DIR, PERSONAL_BIN_DIR, PERSONAL_CONFIGS_DIR, DOTFILES_DIR, XDG paths, SSH_CONFIGS_DIR, HOMEBREW_PREFIX). Added scan rule to replace literal expanded paths with named env vars when editing any script or config file.

Shell function delegation to Ruby utilities

  • [files/–HOME–/.aliases] Updated cron-related shell functions to delegate to Ruby utilities: suspend_cronCron.suspend, resume_cronCron.resume, with_cron_suspendedCron.with_cron_suspended (one-line Ruby invocations). Maintains shell function interface for compatibility while gaining Ruby’s structured error handling and logging. Updated update_antidote_and_regenerate_plugin_bundle to delegate to Antidote.update_and_regenerate_bundle.
  • [files/–HOME–/.shellrc] Updated documentation comments referencing converted scripts and modules. Added note that EnvVars constants are available in Ruby scripts after requiring env_vars.

Updated installation and usage documentation

  • [Extras.md] Updated script references from .sh to .rb extensions for converted scripts (cleanup-browser-profiles.rb, add-upstream-git-config.rb, post-brew-install.rb). Updated command examples and inline comments to reflect Ruby implementations.
  • [files/–HOME–/Brewfile] Updated comment referencing ruby version constraint (ruby '>=2.6.0') to note that mise manages the project ruby version and the Brewfile constraint is for the system ruby used during FIRST_INSTALL.
  • [.shfmtignore] Removed cleanup-browser-profiles.sh entry (script no longer exists after Ruby conversion).

Fixed curl retry configuration for fresh-install bootstrap

  • [.github/instructions/fresh-install.instructions.md] Added “curl Switches for Vanilla OS Downloads” section documenting the _curl_opts array pattern used before ~/.curlrc is symlinked. Defined array once near top of main, expanded into each curl invocation. Guards initialization with [[ ! -f "${HOME}/.curlrc" ]] so flags are only injected when needed. Documented each retry/timeout flag with value and rationale (why more aggressive than .curlrc defaults for bootstrap). Moved bootstrap curl flags documentation from git-config.instructions.md to the correct location (fresh-install context).
  • [.github/instructions/git-config.instructions.md] Removed misplaced curl retry flags documentation (bootstrap curl flags belong in fresh-install.instructions.md, not git-config context). Retained git-specific rules only.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Restart the Terminal application (converted scripts are now Ruby; shell function delegation requires restart to pick up new implementations).
  • Verify all Ruby scripts parse with system ruby:

    cd "${DOTFILES_DIR}/scripts"
    for rb in *.rb utilities/*.rb; do /usr/bin/ruby -c "${rb}" || echo "FAILED: ${rb}"; done
    

3.1.10

Converted run-all.sh and recreate-repo.sh into ruby

  • [scripts/run-all.sh, scripts/recreate-repo.sh] These are now completely converted to ruby implemetation thus providing better error-handling, and better maintainability. Also fixed some inconsistencies & bugs that were hidden in the shell implementation.

Implemented recreate-repo.rb dry-run capability

  • [scripts/recreate-repo.rb] Consolidated verbose git command logging into concise operation descriptions: replaced separate “Would run: git add -A” and “Would run: git amq” lines with single “Would stage all files and amend commit” debug message. Changed compression/push messages from info to debug level to match their nature as implementation details. Removed folder path from compress message to avoid redundancy with section header.

Aliases functions now delegate to the ruby implementation for cron operations

  • [files/–HOME–/.aliases, files/–HOME–/.shellrc] The previous pure-shell implementation of all cron functions has been converted to ruby and now the shell aliases/functions simply delegate to the ruby implementation so as to avoid duplication, and also enhance modularity.

Updated AI assistant documentation with new rules

  • [.github/instructions/ruby-scripting.instructions.md] Added “Shell Command Execution — system() and Escaping” section documenting the two execution modes: (1) direct execution with separate args (no shell, no escaping needed), (2) shell execution with single string (requires shellescape). Includes decision table for when to use each form, with special exception for user-authored command strings from config files (execute as-is, no escaping).
  • [.github/instructions/ruby-scripting.instructions.md] Added “Conditionals — Trailing Style for Single Statements” section: use statement if condition for single-statement conditionals with simple arguments; use block style (if...end) for multiple statements or when condition arguments involve expensive operations (string interpolation with method calls, complex calculations). Trailing style evaluates all arguments before checking the condition, causing unnecessary work when those arguments are expensive to compute.
  • [.github/instructions/shell-scripting.instructions.md] Added “Deferred warning collection — immediate vs summary-only” subsection to § Logging. _record_warning both prints immediately AND stores for summary (use for per-item failures in loops where immediate feedback is valuable). Direct append to _step_warnings only stores without printing (use for aggregated summary messages computed after processing multiple items, to avoid duplicate output). Rule mirrors Ruby’s record_warning vs direct @step_warnings append.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit and restart the Terminal application.
  • Recreate the crontab file in a new Terminal window:

    _create_crontab "${PERSONAL_CONFIGS_DIR}/crontab.txt"
    recron
    

3.1.9

Created git_helpers.rb utility module for git operations

  • [scripts/utilities/git_helpers.rb] New utility module providing 6 git operation methods: config_value, remote_url, each_remote, add_remote, set_remote_url, fetch_all. All methods accept folder: keyword argument (default Dir.pwd) and return full Open3.capture3 tuples (stdout, stderr, status). Private helper _git_command(folder) eliminates duplication of ['git', '-C', folder] pattern across all methods.

Refactored resurrect-repositories.rb to use git_helpers.rb

  • [scripts/resurrect-repositories.rb] Extracted git operations to GitHelpers module, removing 5 functions (_build_git_context, _find_git_remotes, _find_git_remote_url) and 2 constants (GIT_EXECUTABLE, GIT_CONFIG_REGEXP_CMD). Added _report_git_failure helper with call-site guards for performance optimization (avoids string interpolation on success path). Net reduction: 50 lines (498 → 448).

Fixed error handling in resurrect-repositories.rb

  • [scripts/resurrect-repositories.rb] Changed abort() and record_warning calls to raise for fatal failures (clone failure, origin URL verification failure) so they are caught by the rescue block, allowing the script to continue processing remaining repos instead of terminating entirely. Added inline comments documenting the distinction between fatal errors (which abort the current repo) and non-fatal errors (which log warnings but continue).

Changed environment variable warning to immediate output

  • [scripts/resurrect-repositories.rb] Changed _find_and_replace_env_var to use warn() instead of record_warning() for missing environment variables. Missing env vars during config loading are configuration issues, not operational failures, and should not be accumulated in the final summary.

Removed unused methods from logging.rb

  • [scripts/utilities/logging.rb] Removed unused command_exists? method and entire step timing subsystem (step_timing_init, step_start, step_end, step_start_times accessor). Total reduction: 39 lines. All internal methods used by public logging methods (section_header, print_script_summary, record_warning, etc.) are retained.

3.1.8

Fixed ensure_keybase_logged_in not found on re-running fresh-install-of-osx.sh

  • [fresh-install-of-osx.sh] Added load_file_if_exists "${HOME}/.aliases" directly after load_zsh_configs in main(). ~/.zsh_plugins.zsh (the antidote bundle) is checked into the home git repo and symlinked by install-dotfiles.rb before this point, so it is present on both vanilla OS and pre-configured machine runs. .zshrc sources the bundle, which defines zsh-defer, and then defers ${ZDOTDIR}/.aliases to the next ZLE idle event. In a non-interactive script there is no ZLE idle event, so the deferred callback never fires and ${ZDOTDIR}/.aliases functions (ensure_keybase_logged_in, build_keybase_repo_url) are absent. The is_aliases_sourced guard inside ${ZDOTDIR}/.aliases prevents double-loading.

Fixed all alias not found in resurrect_tracked_repos

  • [.aliases] Replaced command_exists all / all restore-mtime -c / all maintenance register / all maintenance start with direct FOLDER="${HOME}" MAXDEPTH=7 run-all.sh git ... invocations. The failure was not caused by alias expansion being disabled — zsh’s ALIASES option is on by default even in non-interactive scripts. The actual cause: resurrect_tracked_repos is called as a background &| job from fresh-install-of-osx.sh, and if ${ZDOTDIR}/.aliases is not loaded in that child-process, all is simply never defined. Using the underlying command directly removes the dependency on ${ZDOTDIR}/.aliases being in scope.

Made allow_all_direnv_configs and install_mise_versions synchronous in fresh-install

  • [fresh-install-of-osx.sh] Removed &| (background + disown) from the allow_all_direnv_configs and install_mise_versions calls; both now run synchronously. Also removed the HACKTAG comments that described the background rationale.

Corrected No Aliases in Non-Interactive Scripts rule

  • [shell-scripting.instructions.md] Replaced the incorrect claim “Zsh disables alias expansion in non-interactive shells” with the accurate mechanism: zsh’s ALIASES option is on by default universally; the real risk is that ${ZDOTDIR}/.aliases may not have been sourced, leaving the alias undefined. Updated the rule rationale, BAD/Good examples, and comment templates accordingly.
  • [copilot-instructions.md] Updated the § No Aliases in Non-Interactive Scripts summary to match.

Corrected ssh config file to use relative paths

  • [ssh config] Some tools do not understand SSH_CONFIGS_DIR custom env var. To accommodate this, the ssh config file refers to the global config and the itentity key files using relative paths. The template has also been modified to reflect the same for new adopters.

Made GHC instructions generic

  • The github copilot instructions file is read only by GHC. In an effort to move to a locally running GPT-OSS model, moved all these instructions to a model-agnostic instructions file.

Clarified two-phase preference architecture in documentation

  • [copilot-instructions.md] Backported the § osx-defaults.sh and capture-prefs — Two-Phase Preference Architecture Layer 1/Layer 2 section from nix-migration, inserted before Git Configuration Rules; stripped the nix-specific targets.darwin.defaults subsection. Gives a concise accessible overview (what the layers are, auto-call behavior, re-run warning, ordering constraint) alongside the existing detailed Phase 1/Phase 2 decision-rules section.
  • [GettingStarted.md] Added an inline sentence to the bootstrap paragraph noting that the script automatically applies the two-phase preference setup in order.

Tightened _create_crontab cron header comments

  • [.aliases] Replaced the verbose chronic comment (“is a utility installed using ‘moreutils’ from homebrew and is needed so that a successful run…”) with a concise form (“is provided by ‘moreutils’ from Homebrew and suppresses cron mail on success”).
  • [.aliases] Removed the parenthetical (needed for chronic, run-all.sh, capture-prefs.sh etc.) from the # PATH: cron header comment — the comment described why the path was set, which belongs in the code comment above it, not in the generated crontab header.

Unified custom.git_state detection to git rev-parse --verify

  • [starship.toml] Replaced [ -d "$root/rebase-merge" ] || [ -d "$root/rebase-apply" ] and [ -f "$root/BISECT_LOG" ] with git rev-parse --verify REBASE_HEAD and git rev-parse --verify BISECT_HEAD respectively; removed the now-unused root=$(git rev-parse --git-dir …) line. All five operation states now use a single unified detection strategy that works with both the classic .git/ files backend and the reftable backend (git 2.45+), where pseudorefs are stored in the reftable and plain file/directory checks silently fail.
  • [copilot-instructions.md] Updated the § Starship Prompt Rules bullet to drop the “two strategies” framing and document the unified git rev-parse --verify approach for all five states (REBASE_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD, BISECT_HEAD).

Standardised osx-defaults.sh section formatting

  • [osx-defaults.sh] Renamed # MenuBar section header to # Menu Bar to match macOS terminology.
  • [osx-defaults.sh] Added missing blank lines after the closing # --- divider in seven sections (Login Window, SSD-specific tweaks, Dock, Safari & WebKit, Mail, Terminal, iTerm2) for consistent section-body separation.

Adopting these changes

  • Rebase from upstream, resolve conflicts. Run in all open terminals:

    unfunction is_shellrc_sourced; zcompile ~/.shellrc; source ~/.shellrc
    unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases
    install-dotfiles.rb
    fresh-install-of-osx.sh
    
  • Quit and restart the Terminal application.
  • Review and edit the ~/.ssh/config file to remove any duplicate Include lines. The best way to determine which format to use is to remove all those, and just run install-dotfiles.rb which will put the correct expected format in it

3.1.7

Standardised dispatch_or_fallback across all per-repo autoload commands

  • [count, st] Renamed count()/st() to _count()/_st() (private implementations) and added count() { dispatch_or_fallback count _count "$@"; } / st() { dispatch_or_fallback st _st "$@"; } entry points — consistent with cc, pull, push, upreb.
  • [copilot-instructions.md] Updated dispatch_or_fallback section to list all six commands (cc, count, pull, push, st, upreb) and explicitly document that status_all_repos and update_all_repos are excluded because they operate on a fixed set of repos.
  • [TechnicalDeepDive.md] Same update to § 10 Per-Project Script Overrides.
  • [Extras.md] Updated git autoload table to split st/status_all_repos into separate rows, add an “Supports override?” column, and clarify that status_all_repos and update_all_repos are excluded; expanded the per-project override description with a concrete annotated example showing how to implement an override file, call _push "$@" to avoid infinite recursion, and use return 1 safely.

Rewrote keg-only PATH/compiler-flags cache to filesystem-direct approach

  • [.zshrc] Replaced the snapshot-and-delta cache-generation approach with direct filesystem enumeration: _keg_collect (renamed from _use_keg_for) interrogates ${HOMEBREW_PREFIX}/opt/<pkg>/bin, libexec/bin, libexec/gnubin, etc. directly and builds keg_paths, keg_manpath, ldflags_new, cppflags_new, and pkgconfig_new without reading the current environment. LDFLAGS, CPPFLAGS, and PKG_CONFIG_PATH are written as plain overwrites (not prepend-expressions) since the keg-only block is their sole setter during startup.
  • [.zshrc] Added Homebrew base bin/sbin to the generated cache (hb_base) so PATH priority is: mise > keg-only > Homebrew base > system.
  • The new approach is idempotent: regenerating the cache inside a shell that already has keg-only vars set (e.g. a tool like OpenCode inheriting the user’s PATH) produces the same result as regenerating in a clean shell. The snapshot-and-delta approach was broken in this scenario — a pre-populated PATH produced an empty delta (keg-only bins missing from cache) and a pre-populated LDFLAGS caused doubled flags on every re-source.

Removed dead prepend_to_* functions from .shellrc

  • [.shellrc] Removed five functions superseded by the filesystem-direct keg-only cache approach: prepend_to_path_if_dir_exists, prepend_to_manpath_if_dir_exists, prepend_to_ldflags_if_dir_exists, prepend_to_cppflags_if_dir_exists, prepend_to_pkg_config_path_if_dir_exists. append_to_path_if_dir_exists and append_to_fpath_if_dir_exists are retained (both have active call sites in .zshrc and fresh-install-of-osx.sh).

Disabled predict-on and incremental-complete-word ZLE features

  • [.zshrc] Commented out autoload and bindkey calls for predict-on (Ctrl+Xp) and incremental-complete-word (Ctrl+Xi). predict-on overlaps with zsh-autosuggestions (already loaded synchronously) which provides the same history-based inline completion non-destructively without a toggle. incremental-complete-word is superseded by fzf-based tab completion. Neither adds startup overhead, but predict-on adds per-keystroke cost when active.

Removed is_macos wrapper from .zshrc

  • [.zshrc] Lifted setopt calls, zstyle completions config, autoload -Uz _git, bindkey for Option+arrow, and the if (($+commands[brew])) keg-only cache block out of the if is_macos; then wrapper. The setopts and zstyle config are generic zsh behaviour, the bindkeys are safely inert on non-macOS terminals, and the brew block was already guarded by (($+commands[brew])) — the outer is_macos check added no safety and made the code harder to reason about.
  • [.zshrc] Updated the comment above the starship init block to remove the stale reference to “the macOS block” and to accurately state that starship’s init must be sourced at file scope (not deferred) because its precmd_functions+= registration and setopt promptsubst must be applied before the first prompt.

Adopting these changes

  • Rebase from upstream, resolve conflicts. Run in any open terminal — delete_caches is essential: the old keg-only cache format called prepend_to_path_if_dir_exists (now removed from .shellrc); sourcing the old cache without clearing it will produce “command not found” errors:

    delete_caches
    unfunction is_aliases_sourced; zcompile ${ZDOTDIR}/.aliases; source ${ZDOTDIR}/.aliases
    unfunction is_shellrc_sourced; zcompile ~/.shellrc; source ~/.shellrc
    
  • Quit and restart the Terminal application.


3.1.6

Set Homebrew zsh as the default login shell during fresh-install

  • [fresh-install-of-osx.sh] Added _set_default_shell function that adds /opt/homebrew/bin/zsh to /etc/shells (required by chsh) if absent, then calls chsh -s to make it the default shell. Called immediately after _install_homebrew so Homebrew’s zsh is guaranteed to be on disk. Idempotent — skips each step if already done.
  • [osx-defaults.sh] Added PlistBuddy call to set Custom Command = No (Login shell) in the Default iTerm2 profile. The key defaults to Custom Shell on a fresh iTerm2 install, which means .zlogin is never triggered for new windows/tabs. Setting it to No ensures the full zsh startup sequence (.zshenv → .zshrc → .zlogin) runs correctly.

Added symmetric-diverge rebase to upreb autoload script

  • [upreb] After git upreb runs per-branch, compare incoming vs outgoing commit counts; if they are equal and non-zero AND git diff @{u} produces no diffs, perform git rebase @{u}. This handles branches that have diverged symmetrically (e.g. remote was force-pushed or rebased) with identical content — situations the git upreb alias skips because no upstream remote is present.

Fix color methods called on Integer in resurrect-repositories.rb

  • [resurrect-repositories.rb] Added missing .to_s before .red / .green on four Integer values (.length return values). Ruby’s color methods are defined on String only — calling them directly on an Integer raises NoMethodError.

Adopting these changes

  • Since _set_default_shell only runs inside fresh-install-of-osx.sh, pre-configured machines will not automatically get the default shell changed to Homebrew’s zsh. Run fresh-install-of-osx.sh to pick up this change — it is fully idempotent and safe to run on an already-configured machine. It will add /opt/homebrew/bin/zsh to /etc/shells and call chsh only if the default shell is not already set correctly.

  • After chsh takes effect (quit and reopen the terminal), verify with echo $SHELL — it should print /opt/homebrew/bin/zsh.

  • Terminal.app requires no manual change — it always opens a login shell using $SHELL, so it picks up the new default automatically once chsh is done.

  • iTerm2 — open Preferences → Profiles → General → Command and set it to Login shell (not “Custom Shell”). This is also applied automatically by osx-defaults.sh -s, but pre-configured machines that skip that step must set it manually.


3.1.5

Migrate cloned repos to reftable format during fresh-install

  • [.shellrc] Added migrate_git_repo_to_reftable helper that checks whether a repo uses the legacy loose/packed-refs format and, when git refs migrate (git 2.45+) is available, converts it to reftable. After migration it removes stale loose-ref files from .git/refs/heads/, .git/refs/tags/, and .git/refs/remotes/ that git refs migrate may leave behind and that can confuse ref lookup. Uses a named helper (_remove_loose_reftable_refs) instead of an anonymous () function so bash can parse .shellrc without a syntax error.
  • [.shellrc] clone_repo_into now calls migrate_git_repo_to_reftable after a successful clone. On a vanilla macOS the system git silently ignores this (the function exits early when git refs migrate is unavailable), so it is a no-op until Homebrew’s modern git is on PATH.
  • [fresh-install-of-osx.sh] Added a “Migrate repos to reftable format” step immediately after _install_homebrew. At that point Homebrew’s git 2.45+ is on PATH, so the dotfiles repo (cloned earlier with system git and therefore still in files format) is migrated correctly.

Remove redundant is_zsh guards from .shellrc

  • [.shellrc] Removed the if is_zsh wrapper around load_zsh_configs and print_usage. Neither function contains parse-time zsh-only syntax; the guard was preventing bash from defining them but bash never calls them, so the guard was unnecessary. Restored the warning comment about infinite-loop risk above load_zsh_configs that was attached to the removed wrapper.

Clarify () vs named helper and is_zsh guard rules in AI assistant docs

  • [shell-scripting.instructions.md] Rewrote the § Glob Patterns — NULL_GLOB section to explain the () vs named helper decision based on whether bash may source the file. Added two new top-level sections: § Do not mandate named helpers everywhere (named functions in zsh are not scoped — () avoids namespace pollution in pure zsh files; named helpers require unfunction immediately after use) and § is_zsh guards are for parse-time zsh-only syntax only (setopt/autoload are runtime-only issues; guards are only needed for syntax bash cannot tokenise).
  • [copilot-instructions.md] Added matching summary bullets for the two new rules, referencing the full treatment in shell-scripting.instructions.md.

Fix ERR trap $LINENO in fresh-install-of-osx.sh

  • [fresh-install-of-osx.sh] Changed both trap _cleanup_and_exit ERR calls to the string form trap '_cleanup_and_exit "${LINENO}"' ERR. With the function-name form, $LINENO inside the handler reports its own line (wrong); the string form evaluates $LINENO in the failing command’s scope before calling the function, so the reported line is always accurate — including for failures in helper functions when set -E propagates the trap.
  • [fresh-install-of-osx.sh] Updated _cleanup_and_exit to accept $1 as the failing line number and include it in the error message when non-empty.
  • [shell-scripting.instructions.md] Added new § ERR Trap — $LINENO String Form vs Function Form section under Cron Scripts with BAD/Good examples and a note that the rule applies with or without set -E.
  • [copilot-instructions.md] Added a matching summary bullet referencing § ERR Trap — $LINENO String Form vs Function Form.

Add missing unfunction for named inner functions

  • [.shellrc] Added missing unfunction _remove_loose_reftable_refs after calling it inside migrate_git_repo_to_reftable. The named function persists in the global table after the outer function returns — unfunction is required for non-subshell call sites (direct interactive use, clone_repo_into from fresh-install-of-osx.sh). run-all.sh sandboxes each repo call in a () subshell so the leak is contained there, but does not eliminate the need for cleanup at other call sites.
  • [cleanup-browser-profiles.sh] Added missing unfunction _read_pattern_file after its two call sites inside vacuum_browser_profile_folder. Same pattern — named inner function would persist in the global table for the rest of the shell session.
  • [shell-scripting.instructions.md] Expanded § Do not mandate named helpers everywhere to include the unfunction requirement with a code example noting the run-all.sh subshell distinction.
  • [copilot-instructions.md] Updated the matching summary bullet to include the unfunction requirement and run-all.sh subshell nuance.

Fix stale GitHub-cached .shellrc on vanilla OS install

  • [fresh-install-of-osx.sh] After install-dotfiles.rb runs, check whether the committed files/--HOME--/.shellrc differs from what was adopted (the curl-downloaded, potentially GitHub-cached version). If it does, restore the committed version with git checkout -- files/--HOME--/.shellrc before load_zsh_configs re-sources it. Without this guard, a stale cache could cause the rest of the install to run with an older .shellrc that is missing newly added functions.

Adopting these changes

  • Rebase from upstream, resolve conflicts. To migrate all existing repos to reftable format (optional, but recommended), run in any open terminal:

    delete_caches
    unfunction is_shellrc_sourced
    FOLDER="${HOME}" MAXDEPTH=7 run-all.sh migrate_git_repo_to_reftable
    
  • Quit and restart the Terminal application.


3.1.4

Optimise zsh shell startup latency

  • [.zshrc] Deferred the initial _mise_hook call in the mise activate cache by appending a zsh-defer-guarded invocation and stripping the bare _mise_hook line from mise activate zsh output. zsh-defer fires after the first ZLE idle event (before any keypress), saving ~25ms from time-to-first-prompt. Falls back to a synchronous call when zsh-defer is unavailable.
  • [.zshrc] Fixed eager PROMPT2 fork in starship init cache generation. starship init zsh emits PROMPT2="$(...)" (double-quoted — forks starship at source time, ~9-15ms). Cache generation now strips that line and appends a lazy single-quoted PROMPT2='$(...)' matching the pattern already used by PROMPT and RPROMPT.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    # delete the mise activate cache and starship init cache to force regeneration
    rm -f ~/.cache/mise-activate-cache.zsh ~/.cache/starship-init-cache.zsh
    
  • Quit and restart the Terminal application.


3.1.3

Harden capture-prefs key stripping

  • [capture-prefs-excluded-keys.txt] Commented out overly-aggressive global patterns (*|NSWindow Frame *, *|NSSplitView Subview Frames *, *|*Identifier, *|*identifier) that stripped legitimate app config keys and caused data loss (e.g. Clocker lost configured timezones). Added “cannot use” notes for *Date, *Timestamp, *Time patterns — these would also cause app startup failures if applied globally.
  • [capture-prefs-excluded-keys.txt] Added targeted per-domain SULastCheckTime exclusions for 15 apps (Sparkle update-check timestamp, criterion 3). Added specific entries for com.abhishek.analyticsLastSignalDate and iVersionLastChecked (Clocker), LastAutoUpdateCompletion/LastUpdatesCheck/LastUpdatesPerform (com.apple.appstored), LastPeriodicAnalyticsPostDate (com.apple.controlcenter), and _DKThrottledActivityLast_* (com.apple.knowledge-agent).
  • [capture-prefs.sh] After stripping, delete the exported plist if no <key> elements remain — empty plists have no value in git history and cannot be meaningfully imported.
  • [capture-prefs.sh] Show count of files actually saved (after empty-plist deletion) in the export success message.

Fix import robustness

  • [capture-prefs.sh] Guard import with is_file before cp — skips domains for which no exported plist exists (app not installed on the source machine) instead of crashing with cp: No such file or directory.
  • [capture-prefs.sh] Fixed mktemp template: removed .plist suffix — BSD mktemp on macOS requires the Xs to be at the very end of the template; a suffix after them causes mkstemp failed: File exists.

Prompt user to restart apps after import

  • [capture-prefs.sh] Replaced the generic “restart any open apps” user_action with _notify_apps_needing_restart — detects which of a curated list of terminal/IDE apps are currently running and emits a single targeted restart message, excluding login-item apps already handled by kill/restart_login_item_apps.

Refactor Finder handling in login-item restart

  • [.aliases] Removed Finder from _MACOS_LOGIN_ITEM_APPS and moved killall Finder directly into both kill_login_item_apps and restart_login_item_apps — Finder is launchd-managed (killall causes immediate relaunch) and cannot be handled the same way as SMAppService login items. Removed the special-case Finder branch from restart_login_item_apps.

Prune uninstalled app domains from capture-prefs allowed list

  • [capture-prefs-allowed-list.txt] Removed 16 domains for apps no longer installed on this machine.
  • [capture-prefs-denied-list.txt] Moved com.apple.Music to the denied list — library file path is device-specific and iCloud Music Library sync state is account- and device-bound (criteria 1 and 2).
  • [Brewfile] Added inline comment to the commented-out knockknock cask explaining what the app does.

Enable per-domain exclusion entries after verification

  • [capture-prefs-excluded-keys.txt] Enabled all eu.exelban.Stats exclusion entries after confirming the keys exist: id, remote_id (device UUIDs), Clock_list (per-device UUIDs), remote_tokens_migrated_to_keychain (credential migration flag), version / runAtLoginInitialized / setupProcess (onboarding sentinels), ble_* (Bluetooth sensor state), sensor_* (hardware sensor state), *_ts (timestamp watermarks), and NSStatusItem Preferred/Restore Position * (display geometry).
  • [capture-prefs-excluded-keys.txt] Enabled com.abhishek.Clocker entries for defaultPreferences (binary NSKeyedArchiver blobs), install (install timestamp), com.abhishek.defaultsLastUpdateKey, and NSStatusItem Preferred Position ClockerStatusItem; left entries absent from this machine commented as documentation.
  • [capture-prefs-excluded-keys.txt] Enabled com.apple.universalaccess|History (per-session accessibility event log, criterion 3); com.sproutcube.Shortcat|telemetryIdentifier (device UUID) and NSStatusItem Preferred Position * (display geometry); and all four io.github.keycastr entries: default.textColor (display-specific ICC color blob), NSStatusItem Preferred Position *, NSSplitView Subview Frames *, and NSToolbar Configuration *.

Document arithmetic increment pitfall under set -e

  • [shell-scripting.instructions.md] Added ## Arithmetic Increment — Safety Under set -e section: (( var++ )) post-increment evaluates to the old value, so (( 0 )) on the first iteration silently aborts the script under set -e. Always use (( var += 1 )) || true.
  • [copilot-instructions.md] Added summary bullet under ### set -euo pipefail cross-referencing the full rule in shell-scripting.instructions.md.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    unfunction is_aliases_sourced; source ${ZDOTDIR}/.aliases    # to pick up new functions and bug fixes
    
  • Quit and restart the Terminal application.


3.1.2

Fix setup-login-item.sh for macOS 26 and complete Brewfile login item hooks

  • [setup-login-item.sh] Rewrote to use SMAppService.loginItem(url:) on macOS 14–25 and fall back to the legacy System Events AppleScript on macOS 13 and macOS 26+. On macOS 26 (Tahoe), Apple removed loginItem(url:) and replaced it with loginItem(identifier:), which only works for login item helpers bundled within an app — it cannot register standalone third-party apps externally. The legacy System Events path (items appear as “Legacy” in System Settings) is the only viable option on macOS 26.
  • [setup-login-item.sh] Added -b (background/hidden mode) flag, proper -h help, usage() using print_usage, and deferred warning collection via _record_warning — bringing the script in line with the standard script skeleton.
  • [Brewfile] Added missing postinstall: "...setup_login_items_script... -a 'KeyClu'" and postinstall: "...setup_login_items_script... -a 'ProtonVPN'" — these two apps were being installed without registering as login items. Removed aldente from Brewfile and com.apphousekitchen.aldente-pro from the allowed prefs list.

Remove hardcoded user-specific paths

  • [software-updates-cron.sh] Replaced hardcoded /Users/vijay with ${HOME} in all FOLDER= assignments — the previous values were machine-specific and would silently do nothing on any other machine.
  • [.aliases] _create_crontab: removed ${HOMEBREW_PREFIX}/bin/ prefixes from the cron entry — zsh and chronic resolve via the PATH set earlier in _create_crontab, so hardcoding Homebrew’s bin path was redundant and fragile across arm/Intel prefix differences.

Harden antidote update and fix antidote .zwc crash

  • [.aliases] delete_caches: split the single find into two separate calls — one for HOME (no -L, uses -delete) and one for HOMEBREW_PREFIX (with -L, uses -exec rm -f {} + because macOS find forbids -delete when symlinks are followed). Without -L, find silently skips symlinked directories, so .zwc files under opt/<formula>/ paths (which are all symlinks into Cellar/) were never deleted. A stale antidote.zsh.zwc could therefore persist across brew upgrades, causing antidote’s ZSH_EVAL_CONTEXT source-detection check to fail and exit 1 to fire on every interactive shell startup.
  • [.zlogin] Ensured antidote.zsh is never compiled to .zwc — antidote 2.1.0’s source-detection check uses *:file:* against ZSH_EVAL_CONTEXT, but zsh sets that token to filecode (not file) when loading from .zwc bytecode. The CLI branch fires, calls exit 1, and crashes every interactive shell. antidote.zsh must always be loaded from raw source.
  • [zsh-startup.instructions.md] Added “Do NOT compile antidote.zsh to .zwc” section documenting the filecode bug and the fix applied to delete_caches.

Document two-phase preference architecture and no-hardcoded-paths rule

  • [TechnicalDeepDive.md] Added § 12 — Two-Phase Preference Architecture: explains why osx-defaults.sh -s (baseline seed) must run before capture-prefs.sh -i (UI-configured overrides), and the decision rule for where new preference code belongs.
  • [Extras.md, GettingStarted.md] Added adopter-facing summaries of the two-phase architecture with links to § 12.
  • [copilot-instructions.md] Added Two-Phase Preference Architecture section (decision rule + ordering constraint) and cross-referenced shell-scripting.instructions.md § No Hardcoded User-Specific Paths.
  • [shell-scripting.instructions.md, copilot-instructions.md] Added ## No Hardcoded User-Specific Paths rule: substitution table of all derived ${HOME} paths and their canonical env var equivalents, plus a scan rule for auditing existing files.
  • [copilot-instructions.md] Documentation Update Routine: added mandatory cross-reference analysis step — after editing any doc file, scan all adopter-facing docs for mentions of the same concept and add or update links to the canonical deep-dive section.

Fix delete_caches post-deletion is_debug error and harden capture-prefs key stripping

  • [.aliases] delete_caches: added source "${ZDOTDIR}/.zlogin" at the end so all .zwc caches are rebuilt immediately in the current shell. Without this, the first new terminal after delete_caches starts with no compiled bytecode; raw-source startup leaves the function table in a state where helper functions (e.g. is_debug) are not visible to .zlogin’s background recompile subshell, producing “command not found: is_debug”. Also converted the trailing && guard for XDG_CACHE_HOME removal to an explicit if to be safe under set -e / ERR trap patterns.
  • [capture-prefs.sh] _strip_excluded_keys: rewrote as a single Ruby/REXML pass (replacing the prior Ruby-enumerate + PlistBuddy-delete approach) using /usr/bin/ruby — eliminates the only Homebrew-Python dependency in the codebase. PlistBuddy treats : as a path separator in its key-path syntax, so keys whose names contain : (e.g. _DKThrottledActivityLast_...:/app/mediaUsageActivityDate) were misinterpreted as nested dict paths and silently not deleted. File.fnmatch without FNM_PATHNAME allows * to match / and :, matching zsh’s [[ == ]] glob behaviour. Also switched capture-prefs.sh to source ${ZDOTDIR}/.aliases instead of .shellrc so the shared macOS prefs helpers (§ 3n) are available.
  • [capture-prefs-excluded-keys.txt] New file: per-domain key exclusion patterns for capture-prefs.sh. Added global date/timestamp patterns (*|*Date, *|*date, *|*Timestamp, *|*timestamp) to strip ephemeral watermark keys from every domain.
  • [capture-prefs-allowed-list.txt, capture-prefs-denied-list.txt] Moved com.apple.xpc.activity2 from allowed to denied (contains only background-task scheduling timestamps and OS version stamps — no portable user preferences). Removed Apple Global Domain and screencapture from the allowed list — these domains accumulate too many system-managed non-portable keys to be safely captured wholesale.

Unify script logging decoration across shell and Ruby (remaining call sites)

  • [capture-prefs.sh, osx-defaults.sh, setup-login-item.sh] Removed the now-redundant "${_SCRIPT_NAME}" argument from remaining print_script_summary call sites.

Gate all script banners on outermost-script depth (remaining scripts)

  • [capture-prefs.sh, osx-defaults.sh, setup-login-item.sh] Each script now decrements _DOTFILES_SCRIPT_DEPTH on exit (clean or error) via _decrement_script_depth.
  • [TechnicalDeepDive.md] Added section on the nesting depth counter, is_outermost_script / outermost_script? guard, and why subprocess scripts still decrement even though only the outermost script prints output.

Harden shell utility infrastructure (remaining changes)

  • [capture-prefs.sh, .zshrc, .zlogin] Eliminated inline (N) glob qualifiers — replaced with setopt localoptions NULL_GLOB inside anonymous functions ().
  • [.aliases, capture-prefs.sh] Replaced remaining raw POSIX test switch (-d) usages and unsafe &&-as-conditional guards with named utility functions and explicit if statements.

Fix cron-safety issues (remaining changes)

  • [capture-prefs.sh] Scoped kill_login_item_apps and restart_login_item_apps to import-only or interactive (TTY) export — cron export no longer kills running apps mid-session.

Harden git alias safety and portability

  • [.gitconfig] Added git pull-safe: fetches all remotes unconditionally, then rebases onto @{u} only if the working tree is clean.
  • [.gitconfig] Updated git upreb: guards the entire rebase + push workflow behind a dirty-tree check before touching anything.
  • [.gitconfig] Refactored all !-prefixed aliases to accept an optional [<dir>] as their first argument via git -C "${1:-.}", enabling git <alias> /path/to/repo as an alternative to git -C /path/to/repo <alias>.
  • [.gitconfig] Fixed git next-version to account for commits already made since the last tag.
  • [software-updates-cron.sh] Replaced git pull with git pull-safe and git upreb (which now have the dirty-tree guard built in); changed the outer failure handling from _record_error to _record_warning — a dirty skip during cron is an expected state.

Establish “do not combine both forms” rule for git aliases

  • [.gitconfig, git-config.instructions.md, copilot-instructions.md] Documented that git -C <path1> alias <path2> is undefined behaviour — the explicit arg wins and -C is silently ignored.

Extract shared macOS prefs helpers into .aliases § 3n

  • [.aliases] Extracted kill_login_item_apps, restart_login_item_apps, and reload_macos_prefs into a new § 3n. macOS prefs helpers section, along with the canonical _MACOS_LOGIN_ITEM_APPS array.
  • [.aliases] Added suspend_softwareupdate_schedule and resume_softwareupdate_schedule to § 3n.
  • [osx-defaults.sh, capture-prefs.sh] Moved softwareupdate --schedule management into the shared helpers; wired resume_softwareupdate_schedule into the EXIT trap.

Expand osx-defaults.sh

  • [osx-defaults.sh] Added sections for new apps: Clocker, DBeaver, DockDoor, Drawio, Firefox, Keybase, KeyCastr, KeyClu, OnlyOffice, Rancher Desktop, Shortcat, Stats, Thaw, Zen Browser.
  • [osx-defaults.sh] Replaced inline kill/restart arrays with calls to kill_login_item_apps and trap 'restart_login_item_apps' EXIT.
  • [osx-defaults.sh] Removed stale Dock/Dashboard keys no longer present in macOS Catalina+.

Add technical deep-dive documentation and restructure adopter docs

  • [TechnicalDeepDive.md] New document covering internal architecture for adopters.
  • [README.md, Extras.md, GettingStarted.md, Prerequisites.md] Added links and callouts to TechnicalDeepDive.md; improved prose clarity on idempotency and the Brewfile first-install model.
  • [GettingStarted.md] Rewrote the post-install checklist; replaced external gist links with local template files.
  • [templates/gitconfig-inc.template, templates/ssh-config.template] New template files for per-context git config and SSH config.

Fix shell and startup infrastructure

  • [.gitconfig] Fixed delta whole-line diff rendering: minus-style/plus-style now use "syntax <bg-color>" to preserve syntax highlighting on whole-line diffs; added line-fill-method = ansi.
  • [.zshrc, .zlogin] Fixed trailing [[ ]] && ... conditionals that caused source to return exit code 1 on normal runs.
  • [.zshrc] Added Option+arrow key bindings for Terminal.app word navigation — Terminal.app’s “Use Option as Meta key” covers Option+B/F but not arrow keys; \033[1;9D/C mapped to ZLE word-motion (inert in iTerm2, which remaps these at the terminal level).
  • [.zshrc] Commented out setopt null_glob — the option causes commands that receive zero arguments silently rather than producing a clear “no matches found” error; setopt localoptions NULL_GLOB inside an anonymous function is the correct scoped alternative.
  • [.aliases, capture-prefs.sh] Fixed remaining unsafe &&-as-conditional patterns.
  • [.editorconfig, custom.gitattributes, .shfmtignore] Removed *.defaults binary/charset entries (leftover from the 3.1.1 .defaults.plist format migration); added capture-prefs.sh to .shfmtignore (uses ${~pattern} zsh glob matching that shfmt cannot parse).
  • [fresh-install-of-osx.sh] Removed trailing manual user_action prompts from main() — the deferred-collection summary (introduced in 3.1.1) already surfaces all follow-up actions.

Update documentation

  • [shell-scripting.instructions.md] Added NULL_GLOB scoping rules: setopt localoptions NULL_GLOB inside an anonymous function is the only permitted form; banned bare setopt NULL_GLOB, unsetopt NULL_GLOB, and inline (N) qualifiers.
  • [copilot-instructions.md, shell-scripting.instructions.md] Documented the _MACOS_LOGIN_ITEM_APPS file-scope constant array exception to the global-state variable naming convention.
  • [git-config.instructions.md] Added working directory argument convention, dirty-tree guard pattern, and ## [delta] — Diff Rendering section.
  • [.opencode/skills/dotfiles-domain/SKILL.md] Updated opencode dotfiles skill with key rules, file reference tables, and documentation update routine.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    install-dotfiles.rb
    unfunction is_shellrc_sourced; source ~/.shellrc    # to pick up new functions and bug fixes
    unfunction is_aliases_sourced; source ${ZDOTDIR}/.aliases    # to pick up new functions and bug fixes
    rm -rfv  /opt/homebrew/opt/antidote/share/antidote/antidote.zsh.zwc*
    delete_caches                                       # clear any stale .zwc bytecode and cached shell environment files.
    _create_crontab "${PERSONAL_CONFIGS_DIR}/crontab.txt" # re-generate the cron file with correct settings
    recron                                              # to pick up the simplified crontab entry
    
  • Quit and restart the Terminal application (to guarantee that the latest versions of the zsh autoload scripts are loaded).
  • On macOS 26: if any app is missing from Login Items, re-run setup-login-item.sh -a '<AppName>' for each, or re-run brew bundle install to trigger the postinstall hooks.

3.1.1

Introduce deferred error/warning collection

  • [.shellrc] Added user_action() logging function (bold yellow, ➡️ prefix) for manual-step messages — distinct from warn (unexpected problem) and info (informational). Suppressed in direnv subshells.
  • [.shellrc] Extracted _record_warning, _record_error, and print_script_summary as shared helpers using zsh dynamic scoping; print_script_summary fires a macOS notification when errors or warnings were collected.
  • [add-upstream-git-config.sh, capture-prefs.sh, cleanup-browser-profiles.sh, osx-defaults.sh, recreate-repo.sh, run-all.sh, setup-login-item.sh] Applied the deferred collection pattern — operation failures use _record_warning/_record_error; print_script_summary called at end of main().
  • [fresh-install-of-osx.sh] Two-level collection: _step_warnings for recoverable issues, _step_errors for significant failures; _cleanup_and_exit calls print_script_summary before the fatal crash message.
  • [software-updates-cron.sh] Two-level collection for update and infrastructure failures; escalated _title_icon to ⚠️ when outdated packages need manual update.

Gate all script banners on outermost-script depth

  • [.shellrc] Added is_outermost_script (_DOTFILES_SCRIPT_DEPTH <= 1) and _decrement_script_depth; print_script_start, print_script_duration, and print_script_summary now guard on is_outermost_script || return 0.
  • [logging.rb] Added outermost_script?, increment_script_depth (registers at_exit decrement), and decrement_script_depth; all three print helpers guard on outermost_script? — mirrors shell behaviour.
  • [.shellrc, logging.rb] print_script_duration now prefixes output with the script name, eliminating ambiguity in multi-script cron logs.
  • [add-upstream-git-config.sh, cleanup-browser-profiles.sh, fresh-install-of-osx.sh, recreate-repo.sh, run-all.sh, software-updates-cron.sh] Each now decrements _DOTFILES_SCRIPT_DEPTH on exit via _decrement_script_depth.
  • [resurrect-repositories.rb] Calls Logging.increment_script_depth before print_script_start; at_exit hook handles the decrement.
  • [ruby-scripting.instructions.md] Documented depth counter, is_outermost_script / outermost_script? guard, and why subprocess scripts still decrement.

Expand Ruby logging.rb with deferred collection and timing

  • [logging.rb] Added record_warning, record_error, current_section=, and print_script_summary — Ruby mirrors of the shell deferred-collection pattern. section_header now sets @current_section as a side effect.
  • [logging.rb] print_script_start returns the Unix epoch it logs — eliminates the two-call pattern; displayed timestamp and in-memory start time are always identical.
  • [logging.rb] print_script_summary(start_time = nil) calls print_script_duration internally when provided — no separate call needed.
  • [logging.rb] Added Logging.user_action to mirror the shell user_action() function.
  • [resurrect-repositories.rb] Converted to record_warning/record_error; added section_header calls per phase; updated to script_start_time = print_script_start / print_script_summary(script_start_time) pattern.
  • [ruby-scripting.instructions.md] Documented the deferred-collection pattern, two shell-version deviations (print_script_start return value; print_script_summary start-time argument), and _SCRIPT_NAME dynamic-scoping behaviour.

Unify script logging decoration across shell and Ruby

  • [.shellrc] print_script_start prefixes banner with _SCRIPT_NAME; _record_warning/_record_error prefix entries with [_SCRIPT_NAME][_current_section]; print_script_summary reads _SCRIPT_NAME via dynamic scoping — no argument needed.
  • [add-upstream-git-config.sh, cleanup-browser-profiles.sh, recreate-repo.sh, run-all.sh] Removed the now-redundant "${_SCRIPT_NAME}" argument from all print_script_summary call sites.

Harden shell utility infrastructure

  • [.shellrc] Added user_action() — see “Introduce deferred error/warning collection” above.
  • [.shellrc] Extracted has_sudo_credentials into § 1e; replaced all raw sudo -n true 2>/dev/null checks.
  • [.shellrc] Fixed is_zsh from [[ "${0}" =~ 'zsh' ]] to [[ -n "${ZSH_VERSION-}" ]].
  • [.shellrc] Added is_debug and is_first_install predicates; replaced all raw inline forms.
  • [.shellrc, .aliases] User-controlled boolean flags (DEBUG, FIRST_INSTALL) now use :-; shell-provided vars (ZSH_VERSION) keep -.
  • [.shellrc] Added debug logging to load_zsh_configs.
  • [.aliases] require_env_var: replaced raw -z test with is_zero_string and warn.
  • [.shellrc, .aliases] Log-level reclassifications: idempotency guards → info; expected-absent tools → debug; action items → user_action; “Successfully sourced ~/.shellrc” → success.
  • [6 scripts] Fixed unsafe &&-as-conditional patterns in software-updates-cron.sh, recreate-repo.sh, capture-prefs.sh, run-all.sh, fresh-install-of-osx.sh (×2) — converted to explicit if blocks.

Fix cron-safety issues in .shellrc

  • [.shellrc] Added ${COLUMNS:-80} fallback in _section_header_impl and print_chars_for_length — zsh sets COLUMNS to 0 with no terminal.

Align startup files and autoload functions to established conventions

  • [files/–ZDOTDIR–/.zshenv] Changed ${DEBUG+1}${DEBUG:-}.
  • [files/–ZDOTDIR–/.zshrc] Changed ${DEBUG+1}${DEBUG:-} and ${ZSH_PROFILE_RC+1}${ZSH_PROFILE:-}; renamed ZSH_PROFILE_RCZSH_PROFILE; converted final [[ ]] && one-liner to if/fi.
  • [files/–ZDOTDIR–/.zlogin] Changed three ${DEBUG+1}${DEBUG:-}; added || true to rm -f/zrecompile calls; converted final [[ ]] && echo to if/fi.
  • [files/–XDG_CONFIG_HOME–/zsh/{cc,count,pull,push,st,status_all_repos,update_all_repos,upreb}] Changed compdef guard to is_zsh && (($+functions[compdef])) — guards zsh-only syntax from bash; updated inline comment.

Expand shell-scripting documentation

  • [shell-scripting.instructions.md] Documented _SCRIPT_NAME at script scope (not local) for dynamic-scoping availability; added ${_SCRIPT_NAME:-<interactive>} fallback.
  • [shell-scripting.instructions.md] Updated “Always Quote Variables” example to use is_file instead of [[ -f ]].
  • [shell-scripting.instructions.md] Added double-quotes exception for strings containing single quotes.
  • [shell-scripting.instructions.md] Added ## Parameter Expansion Operators — \:-` vs `-`` section with scan rule.
  • [shell-scripting.instructions.md] Fixed DIRENV_IN_ENVRC variable name (was DIRENV_DIR).
  • [shell-scripting.instructions.md] Rewrote cron section: load_zsh_configs now conditional; added sudo, is_running_in_tty, and COLUMNS subsections.
  • [shell-scripting.instructions.md] Updated autoload template and compdef guard to use is_zsh.
  • [shell-scripting.instructions.md] Added ## _DOTFILES_SCRIPT_DEPTH — Increment and Decrement section.
  • [shell-scripting.instructions.md] Updated shfmtignore example to use has_sudo_credentials.
  • [zsh-startup.instructions.md] Updated profiling example to use ZSH_PROFILE.
  • [shell-scripting.instructions.md, ruby-scripting.instructions.md] Added unified ## Logging — Level Usage classification table.
  • [shell-scripting.instructions.md] Added ## '&&' as Conditional — Safety Under 'set -e' / ERR Trap section.
  • [all shell scripts] Ran shfmt across all non-ignored scripts.

Expand copilot-instructions documentation

  • [copilot-instructions.md] Added ## Four-Context Validation section.
  • [copilot-instructions.md] Fixed is_running_in_tty table entry (stdin, [[ -t 0 ]]).
  • [copilot-instructions.md] Updated deferred-collection pattern description: depth counter, decrement trap, Ruby equivalent.
  • [copilot-instructions.md] Removed "${_SCRIPT_NAME}" from print_script_summary example.
  • [copilot-instructions.md] Added load_zsh_configs ZDOTDIR safety note, conditional-cron guidance, has_sudo_credentials guard, is_running_in_tty gate, and COLUMNS fallback bullets.
  • [GettingStarted.md] Updated bootstrap curl command to pipe through tee "${HOME}/Downloads/fresh-install-of-osx.log".

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    install-dotfiles.rb
    unfunction is_shellrc_sourced; source ~/.shellrc   # to pick up new functions and bug fixes
    unfunction is_aliases_sourced; source ${ZDOTDIR}/.aliases   # to pick up new functions and bug fixes
    delete_caches   # clear any stale .zwc bytecode and cached shell environment files.
    
  • Quit and restart the Terminal application (to guarantee that the latest versions of the zsh autoload scripts are loaded).


3.0-19

  • [.shellrc] Eliminated subprocess forks on every shell start: $(whoami)${USER} (PAM builtin); $(uname -m)${${MACHTYPE%%-*}/#arm/arm64} (zsh builtin, correctly maps armarm64 on Apple Silicon); 16× $(colorize ...) calls for color variable initialisation → $'\e[...' ANSI escape literals; $(tput cols) in print_chars_for_length and _section_header_impl${COLUMNS} (zsh special variable, no external process).
  • [.shellrc] replace_home_with_tilde rewritten as pure-zsh parameter expansion ${1//${HOME}/~}, eliminating two subprocess forks (echo + sed) on every call.
  • [.zshrc] brew shellenv cache block: renamed internal variable cache_filebrew_shellenv_cache for clarity; added is_executable "${brew_bin}" guard so the block is skipped on a vanilla OS before Homebrew is installed.
  • [.zshrc] Added iterm2_hostname="${HOST}" before the antidote bundle is sourced — pre-empts the iterm2 shell-integration precmd hook which forks hostname -f (~4ms) when this variable is unset.
  • [starship.toml] Git operation state indicators changed from plain-space padding (e.g. ' MERGING ') to >> LABEL << delimiters (e.g. '>> MERGING <<') for better visibility in the prompt during rebase/merge/cherry-pick/revert/bisect operations.
  • [.aliases] Dynamic per-project run-all.sh aliases extracted into _generate_repo_aliases and cached to ${XDG_CACHE_HOME}/repo-aliases.zsh. Cache is regenerated only when PROJECTS_BASE_DIR is newer than the cache or the cache is missing. Public regenerate_repo_aliases function added for manual refresh.
  • [.aliases] $(extract_first_word "${EDITOR}") at two startup-path callsites (editor existence check and edit alias definition) replaced with ${EDITOR%% *} (no subshell).
  • [.aliases] $(pwd) default in folder_size replaced with ${PWD} (zsh builtin).
  • [.zlogin] find_in_folder_and_recompile now uses a per-directory mtime sentinel file in ${XDG_CACHE_HOME} — subsequent login shells skip the find scan entirely if the directory has not changed since the last recompilation, eliminating redundant work on every login.
  • [.zlogin] The five large directory recompilation scans (DOTFILES_DIR, PERSONAL_BIN_DIR, PROJECTS_BASE_DIR, /opt/homebrew, /usr/local) are now run in a disowned background job (&!) so they do not block the first prompt on login shells. Sentinel guards ensure they are no-ops when nothing has changed.
  • [.zlogin] Added recompile_zsh_autoload_dir to compile extensionless autoload function files under ${XDG_CONFIG_HOME}/zsh/ (e.g. cc, count, pull, push, st, etc.) which find_in_folder_and_recompile would silently skip due to its *.sh/*.zsh pattern filter. Cache files under ${XDG_CACHE_HOME} are also compiled via find_in_folder_and_recompile.
  • [all zsh scripts + autoload functions] type is_shellrc_sourced &>/dev/null guard replaced with (( $+functions[is_shellrc_sourced] )) — pure zsh builtin check, no subshell.
  • [.shellrc, .aliases] Centralised the re-source guard inside each file itself — all call sites now source unconditionally (no more per-call (( $+functions[...] )) || guards). All custom zsh git commands (cc, count, pull, push, st, status_all_repos, update_all_repos, upreb) updated accordingly. Sourcing of these 2 files is now refined/tightened without duplication.
  • [all shell scripts] Added # Usage: lines to all functions that accept arguments; added prose doc comments to all previously undocumented functions; converted # == GROUP banners to # --- section dividers throughout both files. Fixed file-header banner width (was 81 chars, now exactly 80); split two combined local var="$(…)" declarations into separate declaration + assignment lines.
  • [custom.gitattributes] Added *.defaults binary so Apple binary plist captures are never diffed or line-ending-normalised by git.
  • [.editorconfig (both repo root and HOME)] Full idiom audit: removed incorrect insert_final_newline = false overrides for *.json and *.md; added indent_style = tab for *.{cmd,bat}; added charset = unset + insert_final_newline = false for *.zwc, *.zwc.old, *.defaults; added [*.sql] and [*.{xml,plist}] with indent_size = 4; added [*.envrc] shfmt block; added [{custom.gitattributes,…}] indent_size = unset; set max_line_length = off globally.
  • [.gitconfig] Added git pull-unshallow and git fetch-unshallow aliases (auto-unshallow shallow repos before pulling/fetching). git pullsub now delegates to pull-unshallow. Fixed git upreb to use grep -x upstream (exact match). Simplified git size to avoid a nested subshell. Removed redundant git prune from git cc. Fixed git mn to pass --no-ff (required when merge.ff=only is set globally). Changed help.autoCorrect from 10 (100ms delay) to prompt.
  • [zsh git commands: cc, count, pull, push, st] Replaced inline argument-parsing loop with parse_folder_and_switches; replaced inline dispatch_or_fallback expansion with the shared helper call.
  • [scripts/] Fixed stale source comments across all scripts (add-upstream-git-config.sh, capture-prefs.sh, capture-raycast-configs.sh, cleanup-browser-profiles.sh, post-brew-install.sh, recreate-repo.sh, run-all.sh, setup-login-item.sh, software-updates-cron.sh); added missing function doc comments; added warn in place of echo where .shellrc helpers are available.
  • [scripts/osx-defaults.sh] Structural refactor: ask() moved from nested (inside main()) to top-level; auto promoted to a script-level variable; sourcing changed from .shellrc to ${ZDOTDIR}/.aliases; usage() added with print_usage; getopts ':s' (colon-prefixed for error handling) replaces bare getopts 's'.
  • [scripts/run-all.sh] Refactored: source "${HOME}/.shellrc" replaced with ${ZDOTDIR}/.aliases; all global MINDEPTH/MAXDEPTH/FOLDER/FILTER vars replaced with locals; find … | grep | sort -u pipeline replaced with a zsh-native dedup loop using :h and an associative array; $(date +%s)${EPOCHSECONDS} (no fork); usage() rewritten with print_usage.
  • [scripts/resurrect-repositories.rb] Major refactor: all public functions renamed to private (_find_git_repos_from_disk, _apply_filter, _generate_each, etc.); CliParser integrated in place of inline OptionParser; section_header/print_script_start/print_script_duration calls added; _verify_all gains summary statistics (discovered_count/common_repos); resurrection loop gains per-repo begin/rescue error isolation with successful_repos/failed_repos tracking — failures are now reported rather than aborting the loop.
  • [.shellrc] Fixed ${(j.:.)RUBYLIB_PATHS} bad substitution error when sourced by non-zsh runtimes (e.g. direnv). Wrapped the RUBYLIB block in is_zsh guard; updated comment to explain the direnv/bash incompatibility.
  • [.shellrc] Added RUBYLIB setup to point to scripts/utilities/ so Ruby scripts can require shared utilities by name without require_relative.
  • [.aliases] Refactored regenerate_repo_aliases into a single public function (removed separate _generate_repo_aliases): accepts optional -f flag to force-rebuild; always sources the cache at the end; prints progress only when -f is given. Renamed cache file from repo-aliases.zsh to repo-aliases-cache.zsh for consistency with other cache files.
  • [.aliases] resurrect_tracked_repos: collect repo ancestor dirs once via _collect_repo_ancestor_dirs and share via _SHARED_REPO_DIRS across both allow_all_direnv_configs and install_mise_versions calls (avoids running the expensive find traversal twice, but otherwise fall back to _collect_repo_ancestor_dirs if that’s not set.); call regenerate_repo_aliases at the end; unset _SHARED_REPO_DIRS when done.
  • [.aliases] Moved is_aliases_sourced function definition to immediately after the re-source guard (before source "${HOME}/.shellrc"), mirroring the is_shellrc_sourced placement in .shellrc.
  • [.aliases] Added GROUP 3 header clarification: (Groups 1 and 2 are defined in .shellrc — bootstrap utilities and core predicates.).
  • [scripts/install-dotfiles.rb, scripts/resurrect-repositories.rb] Added $LOAD_PATH.unshift(File.join(__dir__, 'utilities')) to both scripts — ensures shared utilities are loadable regardless of whether RUBYLIB is set (necessary during FIRST_INSTALL where the dotfiles repo is cloned after .shellrc is first sourced). Switched require_relative 'utilities/logging'require 'logging' in both scripts.
  • [scripts/install-dotfiles.rb] Replaced inline OptionParser block with CliParser.parse from the new shared cli_parser utility.
  • [scripts/utilities/cli_parser.rb, scripts/utilities/hash_ext.rb, scripts/utilities/path_utils.rb] New shared Ruby utilities. cli_parser wraps OptionParser with standard error handling and --help; hash_ext extends Hash with deep_sort; path_utils exposes extract_path_segment_at(folder, index) extracts a path component by index (no subprocess fork).
  • [scripts/utilities/logging.rb] Refactored terminal_width to avoid ||= begin...end pattern (rufo formatter instability); updated usage comment from require_relative to require.
  • [scripts/utilities/string.rb] colorize now suppresses color output when $stdout is not a TTY; tilde substitution applied inside colorize automatically (callers no longer pre-substitute). Color codes changed from plain integers (e.g. 31) to composite SGR strings (e.g. '0;31') to support bold/dim variants. Eight new color methods added; pink renamed to purple.
  • [scripts/utilities/file.rb] Deleted — the File.append method it provided is no longer used anywhere in the codebase.
  • [scripts/software-updates-cron.sh] Renamed perform_update_perform_update (private convention). Replaced [[ ${#array[@]} -gt 0 ]] with is_non_empty_array in two places. Fixed stale unset cutoff_epochunset cutoff_date. Added git restore-mtime, git maintenance register, and git maintenance start steps for all tracked repos. Added bat cache --build step to keep the bat cache current after plugin changes. Removed the ollama pull block. Uses _collect_repo_ancestor_dirs / _SHARED_REPO_DIRS pattern to avoid duplicate find traversals. Sources update_all_repos and status_all_repos autoload scripts directly. Replace home pull, oss upreb, and bcg alias calls with direct equivalents (run-all.sh, brew outdated --greedy) — aliases are not expanded in non-interactive shells (cron).
  • [scripts/setup-login-item.sh] Replaced osascript | \grep -i pipeline with zsh glob pattern match ${${(M)${(f)all_login_items}:#(#i)*${app_name}*}[1]}.
  • [Brewfile] Uncommented shfmt — now an explicit dependency.
  • [all eligible shell and Ruby scripts] Reformatted using shfmt (shell/zsh) and rufo (Ruby) per .editorconfig rules.
  • [.shfmtignore] New file. Excludes .zshrc and cleanup-browser-profiles.sh (unparseable zsh-only syntax), and .shellrc and ${ZDOTDIR}/.aliases (keep_padding expands intentional one-liners).
  • [.shellrc] In the info/error/debug/warn functions, do not print anything if its being called from within direnv. This is to suppress the noisy logs when cd’ing to different directories.
  • [scripts/install-dotfiles.rb] custom.git* files now use mtime-based conflict resolution instead of always treating the destination as authoritative. On FIRST_INSTALL (env var set) the destination always wins (moved into repo, copied back). On subsequent runs, the newer file wins; source wins on a tie. --force bypasses mtime and always overwrites.
  • [files/–HOME–/.gitconfig] Made all aliases scripts POSIX-compatible.
  • [files/–HOME–/custom.gitignore, files/–HOME–/custom.gitattributes, files/–PERSONAL_PROFILES_DIR–/custom.gitignore] Header comments updated to document the mtime-based resolution rules and FIRST_INSTALL behaviour.
  • [.shellrc → .aliases] Moved 8 functions out of .shellrc into ${ZDOTDIR}/.aliases — reducing .shellrc’s curl-download payload and startup cost on a vanilla OS install.
  • [.shellrc] set_ssh_folder_permissions: updated comment to document both reasons it stays in .shellrc — (1) vanilla OS pre-install-dotfiles.rb bootstrap; (2) bash-compat: called from .envrc files evaluated by direnv in a bash subshell (${ZDOTDIR}/.aliases cannot be sourced in bash).
  • [scripts/software-updates-cron.sh] Added ERR trap: calls error() (which triggers notify) on unexpected failure. Added profiles repo size check to notify if 2 GB threshold is breached.
  • [files/–HOME–/.envrc, files/–PERSONAL_PROFILES_DIR–/.envrc] Added ERR trap to both .envrc files. On any unexpected failure, notify() fires an osascript notification with the filename and line number — visible even when the terminal is not in focus.
  • [scripts/data/capture-prefs-denied-list.txt] New file listing 44 domains that must never be exported/imported: device identity UUIDs, MDM enrollment tokens, Apple ID credentials, AirTag beacon MACs, CloudKit cache blobs, printer presets keyed to IP addresses, and ephemeral UI/OS-version state. This script now loads the denied-list into an associative array at startup; skips any allowed-listed domain that also appears in the denied-list with a warn message instead of silently exporting/importing machine-specific data.
  • [scripts/data/capture-prefs-allowed-list.txt] Removed all 44 denied-listed domains from the allowed-list.
  • [files/–HOME–/.aliases] find_and_append_prefs: checks each discovered domain against the denied-list before appending to the allowed-list; prints a warn and skips rather than adding a denied-listed domain.
  • [files/–HOME–/.aliases] recron now reads from the existing ${PERSONAL_CONFIGS_DIR}/crontab.txt instead of regenerating it from a hardcoded template every time. _create_crontab is now a bootstrap-only seed — called only when crontab.txt does not exist yet (vanilla OS scenario).
  • [files/–HOME–/.shellrc] notify now strips ANSI escape codes from the message before passing to osascript, preventing raw escape sequences from appearing as literal characters in macOS notifications. Uses inline zsh parameter expansion ((S) flag + extendedglob) instead of a sed subshell — avoids ERR trap inheritance into $(...) subshells where shell functions like current_timestamp are unavailable. Uses setopt local_options extendedglob to ensure ## works correctly in non-interactive shells (cron) where extendedglob is off by default.
  • [files/–HOME–/.gitconfig] git cc: accept --expire=<when> to override the reflog expiry (default remains 1.week.ago). Uses --expire= flag style (matching git reflog expire’s own interface) so the zsh autoload passes it through switches with zero extra code. Examples: git cc --expire=now, git cc --expire=3.days.ago.
  • [files/–HOME–/.gitconfig] git rfc: rewrote as a !f() shell function; uses git for-each-ref to enumerate refs/heads, refs/remotes explicitly instead of --all, so refs/stash is never expired and stashes are preserved. refs/tags excluded — tags have no reflogs.
  • [files/–HOME–/.gitconfig] git cc: replaced --all in reflog expire with an explicit git for-each-ref enumeration of refs/heads, refs/remotes only — stashes preserved, tags excluded to avoid “reflog could not be found” errors on shallow clones.
  • [files/–HOME–/.gitconfig] git sci: replaced locale-dependent grep "to unstage" staging detection with git diff --cached --quiet — robust across all git locales.
  • [files/–HOME–/.gitconfig] git relative-path: fixed broken git root reference (alias never existed) and corrected path resolution to use realpath + git rev-parse --show-toplevel with proper absolute-path stripping. The old implementation was silently producing wrong output.
  • [files/–HOME–/.gitconfig] git fo: removed redundant --all and --tags flags — fetch.all=true, fetch.prune=true, and fetch.pruneTags=true in config make plain git fetch equivalent.
  • [files/–HOME–/.gitconfig] git se: added -z/-0 to rev-list/xargs pipeline for null-safe handling of filenames containing spaces.
  • [files/–HOME–/.gitconfig] git standup: now defaults author to git config user.name when called with no argument.
  • [files/–HOME–/.gitconfig] git rpo: added comment noting it is a no-op after any fetch due to fetch.prune=true, but remains useful as an explicit one-shot command.
  • [files/–XDG_CONFIG_HOME–/zsh/cc] Updated header documentation to reflect --expire flag, default behaviour, --stale-fix, and --dry-run example.
  • [.github/instructions/shell-scripting.instructions.md, .github/copilot-instructions.md] Sharpened shfmt formatting rules: added explicit “check .shfmtignore first” directive, concrete before/after example of the while true; do ...; done one-liner corruption bug, and explanation that running shfmt on an excluded file corrupts intentional one-liners with no inline suppression escape.
  • [.shellrc] info and success are now suppressed when DIRENV_DIR is set (i.e. running inside a direnv subshell evaluating an .envrc). warn and error always print. Cron jobs, CI, and interactive shells are unaffected. This silences routine .envrc log output from direnv without losing actionable messages.
  • [scripts/fresh-install-of-osx.sh] Added set -E immediately after set -euo pipefail so the existing _cleanup_and_exit ERR trap is inherited by all helper functions defined in the file — previously a failure inside a helper would not trigger the trap.
  • [scripts/fresh-install-of-osx.sh] Fixed dead $? check after brew bundle. Uncommented resurrect_tracked_repos (now runs automatically, synchronously, before allow_all_direnv_configs and install_mise_versions so repos exist before those sweep). DNS fallback changed from 8.8.8.8 to 1.1.1.1.
  • [.github/copilot-instructions.md] custom.git* exception block rewritten with FIRST_INSTALL / mtime resolution rules. .shellrc vs ${ZDOTDIR}/.aliases decision rule rewritten: clarifies the install-dotfiles.rb boundary, bash-compat reason for .shellrc retention, and lists zsh-autoload functions as ${ZDOTDIR}/.aliases candidates.
  • [.github/instructions/git-config.instructions.md, Extras.md] custom.git* handling descriptions updated to reflect mtime-based resolution rules.
  • [files/–ZDOTDIR–/.zsh_plugins.txt, files/–ZDOTDIR–/.zsh_plugins.zsh] Un-deferred fast-syntax-highlighting, zsh-autosuggestions, and zsh-history-substring-search. kind:defer uses zle -F but reschedules itself when PENDING > 0 (bytes already in the TTY buffer) — a fast typist beats the idle window and gets no highlighting, no suggestions, and non-functional Up/Down arrow on their first command. These three plugins directly affect the live typing experience and must be synchronous. Alias-only and cosmetic plugins (eza, git, termsupport, iterm2, sudo, zbell) remain deferred. Updated deferral policy comment to document the PENDING race condition and the deliberate tradeoff.
  • [files/–XDG_CONFIG_HOME–/zsh/update_all_repos, status_all_repos, st, pull, push, upreb, count, cc] Fixed zsh_eval_context self-invocation guard from *:file* to *file*. When sourced in a zsh -c context (as in the cron script), zsh_eval_context is cmdarg file (space-separated, not colon-separated), so *:file* did not match — causing the function to auto-execute on source, then execute again on the explicit call (double-run). *file* matches both toplevel:shfunc:file (sourced from a script) and cmdarg file (sourced in zsh -c).
  • [files/–HOME–/.shellrc] Renamed notify_dotfiles_notify to avoid collision with system/plugin commands (e.g. mise’s command_not_found_handler) that return 127 and trigger the ERR trap in cron. Updated all call sites in .shellrc, .envrc files, and software-updates-cron.sh.
  • [files/–HOME–/.shellrc] success, info, warn, debug: replaced is_non_zero_string ... || echo with if ! is_non_zero_string ...; then echo; fi — the bare || pattern caused is_non_zero_string returning 1 (outside direnv) to fire the ERR trap in any caller running under set -e.
  • [files/–XDG_CONFIG_HOME–/zsh/st, update_all_repos, status_all_repos, pull, push, upreb, count, cc] Added || true to the compdef registration guard in all autoload scripts. (($+functions[compdef])) exits 1 when compdef is not yet defined (non-interactive shells, cron, pre-compinit), firing the ERR trap in any script that sources these files.
  • [files/–HOME–/.shellrc] _dotfiles_notify: use [[ -x '/usr/bin/osascript' ]] instead of command_exists — more precise (won’t match a function/alias named osascript) and correct for a fixed system binary path.
  • [files/–ZDOTDIR–/.zshrc, files/–HOME–/.aliases, scripts/wait-editor] Fixed crontab -e (and tools like visudo, fc) not blocking for GUI editors. EDITOR is always 'wait-editor' — a thin wrapper that re-execs $GIT_EDITOR via POSIX word-splitting so --wait flags are passed correctly. GIT_EDITOR holds the full editor invocation (e.g. 'zed --wait', or 'vi' for SSH). The SSH/local if-else in .zshrc collapsed into a single loop with a per-context preferred-editors list. VISUAL is not set — legacy concept, every modern tool falls back to EDITOR. Removed now-redundant ${EDITOR%% *} stripping in ${ZDOTDIR}/.aliases.
  • [files/–ZDOTDIR–/.zshrc] Added ZSH_AUTOSUGGEST_USE_ASYNC=1, ZSH_AUTOSUGGEST_MANUAL_REBIND=1, ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20 before the antidote bundle load. Async mode fetches suggestions in a background process so ZLE never blocks on history lookups. Manual rebind skips the full ZLE widget re-wrap that autosuggestions performs on every precmd (~10–20ms per prompt). Buffer max size skips suggestion lookups for long command lines.
  • [files/–ZDOTDIR–/.zshrc] Changed ZSH_AUTOSUGGEST_STRATEGY from (history completion) to (history) and added ZSH_AUTOSUGGEST_HISTORY_IGNORE="?(#c100,)". The completion strategy spawns a zpty on every suggestion request (~10–30ms overhead); history alone covers the vast majority of useful suggestions. The ignore pattern skips history entries >100 chars, reducing regex matching cost on large history files.
  • [files/–HOME–/.gitconfig] git cc and git rfc: removed refs/tags from git for-each-ref enumeration passed to git reflog expire. Tags have no reflogs (especially in shallow clones such as antidote cache repos), causing “reflog could not be found” errors for every tag. Only refs/heads and refs/remotes are valid reflog targets.
  • [files/–ZDOTDIR–/.zshrc] Fixed silent bug: list-suffixeszstyle on one line (missing newline typo) meant both list-suffixes and expand prefix suffix completion styles were never set. Split into two separate zstyle calls.
  • [files/–ZDOTDIR–/.zshrc] Removed three compctl (old pre-compsys zsh 2.x) calls and the man_glob() helper they depended on. compctl -k hosts referenced an undefined $hosts array; all three calls conflicted silently with compsys _ssh/_man completers from zsh-completions.
  • [files/–ZDOTDIR–/.zsh_plugins.txt, files/–ZDOTDIR–/.zsh_plugins.zsh] Deferred 6 additional plugins via kind:defer to reduce synchronous startup work: lib/termsupport.zsh (terminal title/CWD hooks — cosmetic), plugins/eza (aliases only), plugins/git (heaviest plugin at 431 lines, aliases only), plugins/iterm2 (shell integration hooks — cosmetic), plugins/sudo (ESC-ESC key binding), plugins/zbell (long-command bell hooks). Cannot defer: lib/functions.zsh, lib/completion.zsh, lib/correction.zsh, lib/key-bindings.zsh, lib/misc.zsh, zsh-completions, plugins/direnv.
  • [files/–ZDOTDIR–/.zshrc] Added ensure_dir_exists "${XDG_CACHE_HOME}" before the first cache write. When delete_caches removes ~/.cache, all subsequent >| cache-write redirections failed silently — the brew shellenv cache was never written, so fpath never received ${HOMEBREW_PREFIX}/share/zsh/site-functions, breaking brew completions and antidote plugins on the next shell start.
  • [files/–ZDOTDIR–/.zlogin] Moved find_in_folder_and_recompile "${XDG_CACHE_HOME}" into the disowned background block. The mtime sentinel never actually prevented the find scan: .zshrc always writes cache files before .zlogin runs, so the sentinel’s -nt check always failed and find ran synchronously on every login shell.
  • [personal/dev/configs/crontab.txt, files/–HOME–/.aliases] Expanded crontab PATH to include /usr/local/bin, /usr/sbin, /sbin, ${PERSONAL_BIN_DIR}, and ${DOTFILES_DIR}/scripts — previously only /opt/homebrew/bin:/usr/bin:/bin, causing run-all.sh, capture-prefs.sh, regenerate_repo_aliases, and standard utilities to be not found in cron.
  • [personal/dev/configs/crontab.txt, files/–HOME–/.aliases] Fixed run-all.sh and capture-prefs.sh not found in cron despite correct PATH. Crontab treats # as part of a value on assignment lines, so an inline comment was appended to the last PATH directory name, making it invalid. Moved the explanation to a standalone comment line above the PATH= assignment.
  • [scripts/software-updates-cron.sh] Removed load_zsh_configs call — it sourced .zshrc which activated mise and installed command_not_found_handler. That handler returned 127 in the non-interactive cron environment, firing the ERR trap. The script only needs ${ZDOTDIR}/.aliases.
  • [personal/dev/configs/crontab.txt, files/–HOME–/.aliases] Fixed cron invocation: changed from chronic /opt/homebrew/bin/zsh script.sh 2>&1 | tee (tee outside chronic’s scope — log never written on success) to chronic /opt/homebrew/bin/zsh -c 'zsh script.sh 2>&1 | tee' so chronic wraps the full pipeline.
  • [scripts/software-updates-cron.sh] Added setopt LOCAL_TRAPS inside main() and moved ERR trap setup there, so the trap is scoped to main and not inherited into called functions.
  • [scripts/software-updates-cron.sh] Consolidated macOS notifications: removed the mid-run outdated notification (immediately replaced by the final “done” notification). The final notification now includes the comma-separated outdated package list when present.
  • [.github/instructions/shell-scripting.instructions.md, .github/copilot-instructions.md, .github/instructions/git-config.instructions.md] Updated docs: zsh_eval_context guard (*file*), compdef || true guard, and git cc/git rfc refs/tags exclusion rule.
  • [all shell script] Removed all redundant unset calls on local variables — local variables auto-clean on function return, so unset inside the same function is always a no-op.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    cp ${DOTFILES_DIR}/files/--HOME--/custom.gitattributes ${HOME}/.gitattributes
    cp ${DOTFILES_DIR}/files/--HOME--/custom.gitignore ${HOME}/.gitignore
    install-dotfiles.rb
    
  • Run delete_caches to clear any stale .zwc bytecode and cached shell environment files.
  • Quit and restart the Terminal application.

3.0-18

  • [.zshrc] Replaced Oh My Zsh with antidote as the plugin manager. A pre-generated static bundle (${ZDOTDIR}/.zsh_plugins.zsh) is checked into the home repo and sourced directly — antidote itself does not need to be installed for the shell to start. The antidote formula (installed via brew) and sourced at shell startup is only required for antidote update / antidote bundle to refresh plugin sources.
  • [.zshrc] Removed all Oh My Zsh bootstrap variables (ZSH, ZSH_CUSTOM, ZSH_THEME, ZSH_DISABLE_COMPFIX, zstyle ':omz:update' ..., plugins=(...)) and the source "${ZSH}/oh-my-zsh.sh" call. compinit -C is now called explicitly (no longer delegated to OMZ). Stale alias/comment block referencing OMZ examples removed.
  • [.zshrc] mise activate zsh is now cached — output written to ${XDG_CACHE_HOME}/mise-activate-cache.zsh keyed on the mise binary mtime, regenerated only when mise itself is updated. The OMZ mise plugin was removed because it referenced ${ZSH_CACHE_DIR} (undefined without OMZ), which caused a “no such file or directory: /completions/_mise” error on every shell start.
  • [.zshrc] Added typeset +x FPATH fpath cdpath CDPATH after the dedup pass — FPATH and CDPATH must never be exported. Both are zsh-internal variables (autoload search path and cd search path respectively). Exporting them causes their contents to leak into the macOS launchd user-session environment, where they persist across iTerm2 restarts and are inherited by every new shell before any rc file runs. Symptoms: zsh -f -c 'echo $FPATH' showed stale ~/.oh-my-zsh/... paths even after ~/.oh-my-zsh was deleted. All other *path vars on that line (PATH, MANPATH, INFOPATH, CPPFLAGS, LDFLAGS, PKG_CONFIG_PATH) are intentionally exported — child processes need them.
  • [.zshrc] compinit refactored to use -C (skip compaudit scan) when the dump file already exists, saving ~11ms per startup. Wrapped in an anonymous function so autoload -Uz compinit does not pollute the global function table. ZSH_COMPDUMP moved to ${XDG_CACHE_HOME}/zcompdump to keep ${HOME} clean.
  • [.zshrc] Starship prompt initialisation cached to ${XDG_CACHE_HOME}/starship-init-cache.zsh, keyed on the starship binary mtime — avoids forking starship init zsh on every shell start. ${commands[starship]} used instead of $(command -v starship) (O(1) zsh hash lookup, no fork). Note: sourcing via a precmd hook was attempted but causes setopt promptsubst (emitted by starship’s init) to be scoped to the hook function, leaving PROMPT as an unexpanded literal after the first command — the cache is therefore sourced directly at startup.
  • [.zshrc] autoload -Uz colors && colors removed — none of the active plugins use $fg/$bg/$color from the zsh colors function; own color variables are defined as $'\e[...' literals in .shellrc.
  • [.zshrc] $(extract_first_word "${editor}") in the preferred-editor detection loop replaced with ${editor%% *} (inline parameter expansion, no subshell).
  • [.zshrc] Fixed bug in autoload loop: autoload -Uz "${func_file}"autoload -Uz "${func_file:t}". Without :t (basename), autoload registers the function under its full path (e.g. /path/to/myfunc) which can never be invoked by short name.
  • [.zlogin] recompile_zsh_scripts now removes .zwc.old before and after calling zrecompile -pqzrecompile moves the existing .zwc to .zwc.old before writing the new one; if zcompile fails mid-write the backup is left behind indefinitely. Cleanup is unconditional so stale backups never accumulate.
  • [.shellrc] Added export ANTIDOTE_HOME, ANTIDOTE_ZSH, and ANTIDOTE_PLUGIN_ZSH — set early (before antidote.zsh is sourced) so they are available in .zlogin and other contexts. ANTIDOTE_HOME mirrors antidote’s own platform defaults: ~/Library/Caches/antidote on macOS, ${XDG_CACHE_HOME}/antidote on Linux.
  • [.zsh_plugins.txt] New file — canonical antidote plugin list, replacing the old plugins=(...) array in .zshrc. Loads selected OMZ lib files, OMZ plugins and third-party plugins.
  • [Brewfile] Added antidote to the base-configs section (installed on every machine, including FIRST_INSTALL). Replaced diff-so-fancy with delta as the diff/pager tool. Replaced jq with jaq (a faster Rust reimplementation). Commented out codeql cask.
  • [scripts/fresh-install-of-osx.sh] Replaced install_oh_my_zsh_and_custom_plugins (curl install + three git clone calls for custom plugins) with a call to update_antidote_and_regenerate_plugin_bundle placed after homebrew is installed.
  • [scripts/post-brew-install.sh] Added antidote update and bundle regeneration step by invoking update_antidote_and_regenerate_plugin_bundle on every brew bundle / bupc run, keeping the bundle in sync after antidote itself is installed or upgraded.
  • [scripts/software-updates-cron.sh] Replaced omz update with the equivalent antidote update function update_antidote_and_regenerate_plugin_bundle.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps in any open terminal:

     cp ${DOTFILES_DIR}/files/--HOME--/custom.gitignore ${HOME}/.gitignore
     rm -rf "${HOME}/.oh-my-zsh"
     install-dotfiles.rb                                                              # Symlink .zsh_plugins.txt and .zsh_plugins.zsh into ${ZDOTDIR}
     brew install antidote                                                            # Install antidote (a zsh script, not a binary)
     source "${HOMEBREW_PREFIX}/opt/antidote/share/antidote/antidote.zsh"             # Load the antidote function into the current shell
     antidote bundle < "${ZDOTDIR}/.zsh_plugins.txt" > "${ZDOTDIR}/.zsh_plugins.zsh"  # Generate the static plugin bundle
     launchctl unsetenv FPATH                                                         # One-time flush of the stale FPATH from the launchd user environment
     delete_caches                                                                    # Clear stale .zwc bytecode and all generated cache files to pick up the typeset +x change
    
  • Quit and restart the Terminal application (a full restart is required — sourcing in-place leaves old OMZ functions in memory).


3.0-17

  • [files/–HOME–/.p10k.zsh (deleted), files/–XDG_CONFIG_HOME–/starship.toml (new), files/–HOME–/Brewfile, files/–ZDOTDIR–/.zshrc] Replaced powerlevel10k with Starship as the prompt engine. Deleted .p10k.zsh and the OMZ p10k instant-prompt setup from .zshrc; added starship.toml; replaced tap 'romkatv/powerlevel10k' and brew 'powerlevel10k' with brew 'starship' in the Brewfile.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then run in any open terminal:

    cp ${DOTFILES_DIR}/files/--HOME--/custom.gitignore ${HOME}/.gitignore
    install-dotfiles.rb
    rm -f "${HOME}/.p10k.zsh"      # remove dangling symlink — source deleted from repo
    brew install starship
    delete_caches
    
  • Quit and restart the Terminal application.


3.0.16

  • [fresh-install-of-osx.sh] Fixed issue where this script was failing silently on the first run on a vanilla OS (root cause: curl timeout within homebrew while downloading installables). Replaced the HOMEBREW_BASE_INSTALL variable with FIRST_INSTALL and added a guard which disables the ERR trap during brew operations, forces re-download of .shellrc, sets a long curl timeout (--max-time 3600), splits brew bundle into separate tap/formula/cask passes for better isolation and resilience, and restores the trap + unsets the extra curl args afterwards.
  • [Brewfile] Renamed the early-exit guard from HOMEBREW_BASE_INSTALL to FIRST_INSTALL for consistency.
  • [software-updates-cron.sh] Prune Zen session-backup files older than 7 days from the browser-profiles repo (works with both macOS BSD and GNU date).
  • [.shellrc] Added TODO comments in clone_repo_into for future reftable support once p10k resolves the vcs_info incompatibility, including a HEAD-fixup snippet for post-move repos.
  • [.gitconfig, custom.gitignore, custom.gitattributes, recreate-repo.sh] Git performance tuning and ignore/attribute reorganisation.
  • [.gitconfig] Tuned git performance settings: set core.autocrlf=false, increased core.compression and pack.compression to 9, raised pack.deltaCacheSize to 2047m and pack.windowMemory to 1g, enabled pack.useDeltaBaseOffset, added fetch.negotiationAlgorithm=skipping, http.version=HTTP/2, protocol.version=2, and repack.packKeptObjects=false / repack.useDeltaBaseOffset=true for faster and smaller pack operations.
  • [.gitconfig] Added init.defaultRefFormat=reftable as a commented-out TODO pending p10k support.
  • [custom.gitattributes] Added explicit eol=lf enforcement via * text=auto; added binary markers for common image, font, and byte-compiled extensions (*.png, *.jpg, *.woff*, *.ttf, *.pyc, *.zwc*, etc.) so git never mangles them.
  • [custom.gitignore (home)] Major reorganisation: grouped all ignore rules under labelled section headers (OS, shell history, caches, build tools, IDE, AI tools, XDG config, SSH, home directories, dev workspace, misc app data, symlinked dotfiles, negations); added new entries for opencode auto-generated files, Zed conversations/themes, GitHub Copilot, Gemini/Qwen/Safety AI tools, and various other tools.
  • [custom.gitignore (profiles)] Full rewrite with labelled sections; consolidated browser-profile ignore rules across all *Profile dirs (lock files, caches, crash artefacts, telemetry, security state, network/SW state, runtime DBs); added detailed per-browser sections for Firefox, Zen, Thunderbird, and Chrome Beta with explicit comments on what is intentionally tracked.
  • [.shellrc] Added TODO comments in clone_repo_into for future reftable support; added logic to fix the .git/HEAD file after reftable clone-via-move.
  • [recreate-repo.sh] Added TODO comment for future git init --ref-format=reftable support.
  • [software-updates-cron.sh] Added a new step to prune tracked Zen session backup files older than 7 days from the browser-profiles repo (compatible with both macOS BSD and GNU date).
  • [.shellrc] Added step_start, step_end, and step_timing_init helper functions for per-step and total elapsed time reporting in scripts.
  • [fresh-install-of-osx.sh, software-updates-cron.sh] Instrumented all major steps with step_start/step_end calls for granular timing output. Also initialise _SCRIPT_START_TIME explicitly so timing is accurate before .shellrc is sourced.
  • [Brewfile] Enabled cairo, gnu-tar, mercurial, and sccache (previously commented out) for zen-browser development.
  • [.zshrc] Added gnu-tar to the list of keg-only Homebrew packages that override macOS defaults. Removed the git_scripts path addition.
  • [.envrc (profiles)] Temporarily disabled natsumi-browser cloning as a trial. Removed timeout wrapper from add-upstream-git-config.sh call.
  • [software-updates-cron.sh] Temporarily disabled natsumi codebase update block as a trial.
  • [Brewfile] Added Mechvibes since Haptyk turned out to be payware after some days. Removed Haptyk from capture-prefs-domains.txt and added Mechvibes.
  • [GettingStarted.md] Updated bootstrap one-liner to use FIRST_INSTALL instead of the old HOMEBREW_BASE_INSTALL variable name.
  • [.shellrc] Added ServerAliveInterval=10 and ServerAliveCountMax=3 SSH options to the submodule update call in clone_repo_into to prevent silent hangs on flaky connections.
  • [.aliases] Updated grep/fgrep/egrep aliases: removed VCS dirs from --exclude-dir (since Homebrew grep handles them natively) and added *.zwc* / .*.zwc* to --exclude patterns. Removed --all flag from bupc’s brew bundle call. Added allow_all_direnv_configs and install_mise_versions calls inside resurrect_tracked_repos.
  • [.zshrc] Refactored use_homebrew_installation_for to accept a package name (e.g. curl) instead of a full path; the function now derives the path internally via ${HOMEBREW_PREFIX}/opt/${1}. Added grep to the keg-only packages loop. Added explicit prepend_to_path_if_dir_exists calls for ${HOMEBREW_PREFIX}/bin and ${HOMEBREW_PREFIX}/sbin.
  • [mise/config.toml] Enabled experimental = true for mise.
  • [zed/settings.json] Enabled thinking mode (enable_thinking = true) and set effort = "high" for the default Zed AI model.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitattributes" "${HOME}/.gitattributes"
    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    
  • Quit and restart the Terminal application.


3.0.15

  • [scripts] AI-based refactoring of shell scripts and ruby scripts to remove redundant scripting issues like unnecessary local/unset declarations.
  • [.curlrc, .envrc, .gitconfig, .iex.exs, .profile, .zlogin, .zshrc] General cleanup and minor improvements across dotfiles.
  • [zsh scripts] Refactored cc, count, pull, push, st, status_all_repos, update_all_repos, and upreb scripts.
  • [.eclintignore, .editorconfig] Added editor config and eclint ignore files for consistent code style enforcement.
  • *[add-upstream-git-config.sh, .shellrc] Potential fix for direnv allow hanging when run in the $PERSONAL_PROFILES_DIR folder by ensuring that the git config is properly set up for that folder.
  • [.gitconfig] Added new alias default-branch to get the default branch of a git repository.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    
  • Quit and restart the Terminal application.


3.0.14

  • [Brewfile] Replaced Ice with Thaw.

3.0.13

  • [Brewfile] Added Dockdoor, flux-markdown, dbeaver and codeql to the Brewfile and captured their preferences for backup.
  • [.aliases] The dynamically generated aliases for the git repositories found under the $PROJECTS_BASE_DIR will now enable more fine-grained control. To find out what all aliases have been setup on your machine, you can run alias | \grep rug.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    source "${ZDOTDIR}/.aliases"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    
  • Quit and restart the Terminal application.


3.0.12

  • [.aliases] New alias for mkdir that will create the directory and its parent directories if they don’t exist.

3.0.11

  • [.aliases] recron will now generate the default crontab file and then register it with the system’s crontab command.
  • [Brewfile] Added mole instead of pearcleaner for a cli-based tool to clean disk space.
  • Custom git-related zsh scripts in ${XDG_CONFIG_HOME}/zsh/ now properly handle git switches passed to them.
  • Added direnv configuration file.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    source "${HOME}/.shellrc"
    source "${ZDOTDIR}/.aliases"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    recron
    crontab -l # should now show the crontab with the software updates cron job
    
  • Quit and restart the Terminal application.


3.0.10

  • [install-dotfiles.rb] Now handles the case where there’s no env var substitution needed in the file’s relative path, in which case, the file is treated as needing to be processed from the machine’s root directory.
  • Use git restore instead of git checkout to restore files.

3.0.9

  • Fixed issues when running install-dotfiles.rb script on a vanilla macos with ruby 2.6 and optimized it for better performance.
  • Fixed all shell scripts using claude-sonnet for better readability and maintainability.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    rm -rf "${XDG_CONFIG_HOME}/zsh"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    
  • Quit and restart the Terminal application.


3.0.8

  • [Brewfile] Added opencode for terminal-based free/OSS AI assistant.
  • [Brewfile] Removed AlDente since its built into Tahoe now.
  • [Brewfile] Removed Brave browser since I will use Chrome if needed.
  • [install-dotfiles.rb] Use SSH_CONFIGS_DIR environment variable for ssh config directory.

3.0.7

  • Moved files/--HOME--/.ssh/global_config file to files/--SSH_CONFIGS_DIR--/ to make use of the correct ssh folder location if it was customized.
  • mise will default to using pre-compiled ruby binaries if available.
  • [Brewfile] Install keyclu and drawio apps and captured their preferences for backup.

3.0.6

  • [osx-defaults.sh] Fix syntax issue.
  • Remove redundant lines in multiple shell scripts.
  • [Brewfile] Remove unquarantine flag in Brewfile since its no longer supported.

3.0.5

  • [install-dotfiles.rb] and [run-all.sh] Added support for running in ‘dry-run’ mode and printing the summary.
  • [software-updates-cron.sh] Removed pruning of mise-installed software since that doesn’t work with the latest version of mise.

3.0.4

  • [Brewfile] Replaced ‘Raycast’ with ‘Sol’ (https://github.com/ospfranco/sol) - lightweight, FOSS, faster.
  • [Brewfile] Added ‘Shortcat’ (https://github.com/shortcatapp/shortcat) for faster and more efficient keyboard shortcuts.
  • [resurrect-repositories.rb] Support for ruby 2.6 (default ruby in macos 26 Tahoe): added ‘pathname’ to require list.

3.0.3

  • Revamped the documentations to improve clarity, readability and adoptability.

3.0.2

  • [run-all.sh] Renamed the script to follow the naming convention (using hyphen instead of underscore) for all shell scripts.
  • Replaced the HOME env var with the tilde (~) to represent the home directory when printing so as to reduce the amount of text being displayed on the console.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    source "${HOME}/.shellrc"
    source "${ZDOTDIR}/.aliases"
    "${DOTFILES_DIR}/scripts/install-dotfiles.rb"
    
  • Quit and restart the Terminal application.


3.0.1

  • [install-dotfiles.rb] Optimized the installation script for performance.
  • Introduced qwen-code and claude-code. (settled on qwen-code)

3.0.0

  • Squashed all commits into a single commit.
  • Tested on a fresh vanilla macos (26.2) machine.

Adopting these changes

  • Quit all browsers completely
  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    git tag -d 1.0
    git tag -d 2.0
    mv "${HOME}/.dotfiles" "${XDG_CONFIG_HOME}/dotfiles"
    mv "${HOME}/personal/${USERNAME}/profiles" "${HOME}/personal/${USERNAME}/browser-profiles"
    source "${XDG_CONFIG_HOME}/dotfiles/files/--HOME--/.shellrc"
    cp "${XDG_CONFIG_HOME}/dotfiles/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    "${XDG_CONFIG_HOME}/dotfiles/scripts/install-dotfiles.rb"
    allow_all_direnv_configs
    
  • Quit and restart the Terminal application.

2.0.47

  • *[.aliases] Extract restore_cron function to remove some duplication.
  • [fresh-install-of-osx.sh] Removed resurrecting all tracked repos to save time while re-imaging/setting up the laptop.
  • [osx-defaults.sh] Turned off spotlight indexing for all volumes.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit and restart your Terminal application for these changes to take effect.

2.0.46

  • Moved processing of the natsumi browser extension into the .envrc file so that direnv will take care of it automatically. This also handles cases where a new browser is installed after the first time setup.
  • Moved resurrecting of tracked repos to the end after the import of preferences and setting up the cron job since it takes a long time and should not block the import process.

2.0.45

  • Added a new script run-all.sh to run any unix command in matched git repos.
  • [fresh-install-of-osx.sh] Removed cloning of the git_scripts repo since the run-all.sh script has now been moved into this repo.
  • [.shellrc] Replaced function dir_has_children with is_dir_empty which checks if a directory is empty.
  • [.zlogin] Recompile scripts in the foreground since running in the background results in silent failures.
  • [.aliases] Added a new alias resurrect_tracked_repos to resurrect all tracked repositories.
  • Renamed FIRST_INSTALL to DEBUG to better reflect the functionality.

2.0.44

  • Updated documentation to include the setup of the cronjobs.

2.0.43

  • Added a new function is_shellrc_sourced to check if the shellrc file is sourced.
  • Changed all shell scripts to use single quotes where possible to ensure that we don’t accidentally expand variables or execute commands.
  • [osx-defaults.sh] Converted to a zsh script.

2.0.42

  • Changed all shell scripts to use switches instead of positional arguments for more intuitive usage.
  • Removed the use of colors if there’s no terminal (for eg for cron jobs).
  • Removed boring-notch cask since it was causing issues when installing on a fresh vanilla os.

2.0.41

  • Adopted Zed as the default editor and removed VSCodium.
  • Miscellaneous fixes and improvements to shell scripts.
  • Cleanup documentation.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    bupc
    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    
  • Quit and restart your Terminal application for these changes to take effect.


2.0.40

  • [resurrect-repositories.rb] Fixed an issue while cloning git repos where the script was silently proceeding further.

2.0.39

  • [Brewfile] Added common & essential OSS packages that are typically behind in macos (typically due to license issues).
  • [.zshrc] Fixed issue with RUBY_CONFIGURE_OPTS not being set correctly when openssl is installed.

2.0.38

  • [resurrect-repositories.rb] Changed the repo-resurrection generation logic to reduce manual edits to the generated yaml structure. This now handles generating the yaml with references to the PROJECTS_BASE_DIR and HOME env variables to make it generic and not hardcode the user’s login name/home folder.

2.0.37

  • [.shellrc] Restructured the env var’s section to be more explicit as to what section/vars need to be changed, and which ones can be optionally changed.
  • [.shellrc] Extracted usages of ${HOME}/.ssh into a new env var defined in .shellrc so that custom locations can be easily changed in a single place.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit and restart your Terminal application for these changes to take effect.
  • Run install-dotfiles.rb in the new shell.
  • Manually edit ${HOME}/.ssh/config to replace the reference to ~/.ssh/global_config towards the last line with ./global_config. If this results in a duplicate line, remove the duplicate line.
  • Verify the above changes in the ${HOME}/.ssh/config file by running git pull in one of the cloned repos on your local machine.

2.0.36

  • All git push invocations now have the explicit --progress flag.
  • [.shellrc] error function will no longer exit the process. It just returns a non-zero code which needs to be handled by the caller.
  • [.aliases] kbgc alias has been changed to a function, which now accepts parameters as to which repo to process.

Adopting these changes

  • Rebase from upstream, resolve conflicts.
  • Quit and restart your Terminal application for these changes to take effect.

2.0.35

  • Make handling of stdout and stderr consistent across all usages.
  • Handle immediate exit from shell scripts with appropriate error messages.
  • IMPORTANT: The post-brew-install.sh script was not being invoked when running brew bundle command due to a path issue. Even if the path was hardcoded into the Brewfile, another issue (relating to that block being evaluated when the Brewfile was being read itself) is present. So, this invocation has been turned off.

Adopting these changes

  • Quit and restart your Terminal application for these changes to take effect.

2.0.34

  • [fresh-install-of-osx.sh] Move the custom handling of the direnv for the home and profiles folders into allow_all_direnv_configs.
  • [cleanup-browser-profiles.sh] Remove parallelization since the code seems cleaner.
  • General cleanup for maintainability and removing duplicate code.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    
  • Quit and restart your Terminal application for these changes to take effect.


2.0.33

  • Show the git repo size in the p10k prompt.

2.0.32

  • Minor fixes for using ZSH env variable instead of hardcoding ${HOME}/.oh-my-zsh in multiple places.

2.0.31

  • Unignore ${HOME}/.ssh/known_hosts so that the repository resurrection process is done without user interaction.
  • When using the error function, a visual notification is also raised in the Notifications area so that the user need not monitor the mail command if there are any outdated GUI apps that need upgrading using bcug.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    

2.0.30

  • Updated documentation to clearly call out where references to my username (vraravam) should NOT be changed when forking for your usage.
  • [.aliases] Renamed delete_zsh_compilations to delete_caches.

2.0.29

  • Added Tor Browser.
  • Updated instructions for exporting/importing Raycast configurations.

2.0.28

  • Fixed issue with upreb and cc scripts since they were not evaluating the current working directory at the time of invocation. Instead, they were evaluating at the time of shell startup.
  • [Brewfile] Added dua-cli for disk usage measurement from the cli.

2.0.27

  • [.aliases] Removed upreb_me alias and upreb-universal.sh and combined both into a single zsh autoloaded script. This also allows to override it with a folder-specific implementation that can handle pre- and post- (or full override) steps as needed.
  • [.shellrc] Reduce line length when invoking the section_header function by replacing the value of HOME env var with ~.
  • Introduced .terraformrc file for configuring terraform.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    install-dotfiles.rb
    

    After running the above script, there might be changes that show up in the dotfiles repo, which again need to be reconciled. While doing so, please keep in mind how this will need to work when running on a vanilla OS (even in cases where the prior machine is not working/accessible). So, ensure that any logic that you add should work in that scenario.

  • Quit and restart your Terminal application for these changes to take effect.


2.0.26

  • Fixed an issue where running fresh-install-of-osx.sh caused the whole terminal app to quit at the end.

2.0.25

  • [Brewfile] Removed ghostty since there are some features that make iTerm better suited for my usecase.

2.0.24

  • [Brewfile] Introduce ghostty and capture its configuration.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    

2.0.23

  • De-duplicate upreb script to handle all locally checked out branches in a generic manner using a universal script rather than duplicating for each folder.
  • [.shellrc] Updated the section_header function to be smart about viewport column width and center the text as optimally as possible.

2.0.22

  • Introduce configuration in git to use pandoc for diffing word documents.

2.0.21

  • Commented out the update to FF & Zen browser’s user.js scripts since I have started using RapidFox settings.

2.0.20

  • Trying to grayjay for youtube replacement.

2.0.19

  • Enhanced curl configurations and enable retry even for first time setup.
  • Turn on compression for ssh connections.
  • Use repack.MIDXMustContainCruft in git config to optimize repo size.

2.0.18

  • [Brewfile] Replace deprecated tldr with tlrc.
  • Run the ssh-add command via direnv for the HOME folder. (It’s idempotent, and so safe to be re-run for each new terminal window startup.)

2.0.17

  • [.gitignore_global] Add all .*keep files to not be ignored.
  • Fix gitignore configs for profiles repo.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${DOTFILES_DIR}/files/--PERSONAL_PROFILES_DIR--/custom.gitignore" "${PERSONAL_PROFILES_DIR}/.gitignore"
    cp "${DOTFILES_DIR}/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    

2.0.16

  • [.gitconfig] Enable clone.rejectShallow.
  • [Brewfile] Try out BoringNotch.

2.0.15

  • [.gitconfig] Fixed issues with incorrect sorting configurations.
  • [Brewfile] Replaced ‘floorp’ with ‘google chrome beta’ since floorp doesn’t expose custom key-bindings for switching workspaces. Moved to ice beta to support macos 26 Tahoe beta.

2.0.14

  • Removed ZenProfile from being processed to inject Natsumi for user chrome.
  • Updated documentation for catching up with multiple commits from upstream.

2.0.13

  • Fixed an issue where the homebrew’s libraries were not picked up first in the PATH.

2.0.12

  • [post-brew-install.sh] Fixed issue with app name for Visual Studio Code while crearing cmd-line executable.
  • [Brewfile] Removed Picocrypt and Unarchiver due to non-usage.

2.0.11

  • [software-updates-cron.sh] Runs the bcg alias as the last command and if there are any oudated softwares, it will error out. This serves as a simple mechanism to prompt the user that some softwares need manual updating.

2.0.10

  • [fresh-install-of-osx.sh] Added command to add the checked-out ssh keys to the ssh-agent.
  • [.gitconfig] Added some more configurations.
  • [Brewfile] Use new name for ollama cask.

2.0.9

  • [fresh-install-of-osx.sh] approve-fingerprint-sudo.sh has now been converted from a standalone script into a function.

2.0.8

  • [fresh-install-of-osx.sh] Moved each logical block into a function so its easier to understand and maintain.

2.0.7

  • [Brewfile] Onyx is now only processed if the current OS is non-beta.

2.0.6

  • Updated more documentation.
  • [capture-raycast-configs.sh] and [capture-prefs.sh] now handle switches vs arguments/parameters consistently.
  • [software-updates-cron.sh] Now also pulls ollama models: codellama and deepseek-r1.

2.0.5

  • Updated README.md to make adoption steps clearer to follow.
  • Formatting of markdown files.

2.0.4

  • [.aliases] Introduced a new function find_and_append_prefs that finds and appends the preferences associated with the partial string passed in as an argument. Also, sorts (and removes duplicates) from the config file used to capture preferences.

2.0.3

  • Trying to fix issue with osx-defaults somehow corrupting the System Settings app.

2.0.2

  • [.shellrc] Exposed a new function is_arm to denote whether the current machine architecture is ARM.
  • [post-brew-install.sh] Will cleanup the keybase executables from the /usr/local/bin folder if they are present.

2.0.1

  • [Brewfile] Added Picocrypt.

2.0.0

  • Squashed all commits into a single commit.
  • Tested on a fresh vanilla macos (15.5) machine.

1.1-23

  • [Brewfile] Removed unused apps, moved commented out lines towards the bottom of the file.

1.1-22

  • [Brewfile] Fix issue with vscode not being in PATH when running bupc command.

1.1-21

  • [Brewfile] Replace AppCleaner with PearCleaner, and KeepingYouAwake with an extension to Raycast (Coffee).

1.1-20

  • [Brewfile] Trial to check if returning 0 will make the fresh installation script continue without needing to be rerun.
  • Minor tweaks to fix the gitignore for profiles repo.
  • [.aliases] Renamed alias code-gist to edit-gist to make it more generic.
  • Handle setting up of Zed and Zed-Preview for cli access (if installed).

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${HOME}/.dotfiles/files/--PERSONAL_PROFILES_DIR--/custom.gitignore" "${PERSONAL_PROFILES_DIR}/.gitignore"
    install-dotfiles.rb
    
  • Quit and restart the Terminal application.


1.1-19

  • Moved a lot of the shell functions from ${ZDOTDIR}/.aliases into individual files in ${XDG_CONFIG_HOME}/zsh/ so that they can be autoloaded/lazy-loaded on-demand. (Theoretically, this should improve shell startup time)

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${HOME}/.dotfiles/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    install-dotfiles.rb
    
  • Quit and restart the Terminal application.


1.1-18

  • [Brewfile] Ice is not installed on MacOS < 14, added KnockKnock.
  • [fresh-install-of-osx.sh] Use natsumi-browser in Firefox profile (similar to Zen profile).
  • [.gitignore_global] Regenerate from https://gitignore.io with more options.
  • Major refactoring for ruby scripts to optimize for time and use of ruby idioms.
  • [.zlogin] Optimize recompiling of zsh shell scripts.

1.1-17

  • [software-updates-cron.sh] Removed parallelism (something that was introduced in the previous version when optimzing using gemini) - since this was causing lots of confusion when looking through the logs.
  • [gitconfig] Removed editor config setting since that’s already being governed by the env var EDITOR set from ~/.zshrc.
  • [Brewfile] Removed unused tools / added new tools.
  • [capture-prefs-domains.txt] Added entries to capture PdfGear, TinkerTool, UTM.
  • Removed partial line comments from the other config data files since they are inconsistent/might cause issues when parsing / applying them during the cleanup steps.

1.1-16

  • Ran gemini to optimize the shell configuration scripts aimed at optimizing the shell startup time.
  • Renamed ‘scripts/capture-defaults.sh’ to ‘scripts/capture-prefs.sh’
  • Extracted ‘setup_login_item’ function from ${ZDOTDIR}/.aliases into a standalone script so as to avoid issues between bash vs zsh when running postinstall step in Brewfile.
  • [capture-prefs.sh] Extracted the whitelist of preferences into a separate file: capture-prefs-domains.txt.
  • [cleanup-browser-profiles.sh] Extracted the whitelist of files and directories that needs to be cleaned into separate files.

Note: This version has been successfully tested on a Macbook M1 on 2 May, 2025.


1.1-15

  • Added config settings file for mise to handle idiomatic_version_file_enable_tools

1.1-14

  • [shellrc] Introduced new is_zsh function for defensively loading ${ZDOTDIR}/.aliases when running brew install/update commands (which runs bash shell)

1.1-13

  • [Brewfile] Removed deprecated vscode plugins.
  • [software-updates-cron.sh] Fix issue with BetterFox user.js not being put in correct Firefox profile; Added BetterZen’s user.js into Zen profile.

1.1-12

  • [fresh-install-of-osx.sh] Set PATH even if dotfiles repo is present - so that future scripts can be invoked without issues.
  • [Brewfile] Cleaned up some softwares that I rarely use.
  • [.tcshrc] Removed empty file

1.1-11

  • [.gitconfig] Minor changes to decorate git log.
  • [.aliases] Added upreb_me shell script that will intelligently run a shell script (if present) for the current folder or fall back to the global git upreb alias
  • [.npmrc] Set some npm configurations to hide progress bar and save the exact version into the BOM file.

1.1-10


1.1-9

  • Moved setting up of login items into the Brewfile so that can be managed along with the cask block itself.

1.1-8

  • Minor cleanup (removed leftover references to Arc).

1.1-7

  • [software-updates-cron.sh] Added more steps/commands to be run via a cron job.

1.1-6

  • Minor refactoring to reuse utilize utility methods defined in .shellrc.

1.1-5

  • [.cshrc] Removed empty file
  • [.shellrc] Re-aligned colors for the success, warn, debug and error functions

1.1-4

  • Simplify color output for scripts (avoid nesting) within the same line.

1.1-3

  • [.aliases] install_mise_versions now handles config files from more language-version-managers.
  • [fresh-install-of-osx.sh] Removed duplicate function defn: build_keybase_repo_url.
  • [fresh-install-of-osx.sh] Moved some post-install steps into a new script which is invoked from the Brewfile’s at_exit block.
  • [software-updates-cron.sh] Corrected defensive checking of installed software before running some update commands.

1.1-2

  • Moved setup_login_item function into the Brewfile since its used after app-installations.

1.1-1

  • [Brewfile] Replaced libreoffice with onlyoffice.
  • [.aliases] Fixed issue with start_docker and stop_docker.

1.0-53

  • [Brewfile] Added rsync to be used from homebrew so as to avoid the recently announced RCE vulnerability.
  • Changed the DOTFILES_DIR env var to use ${HOME}/.dotfiles instead of ${HOME}/.bin-oss.

Adopting these changes

  • Rebase from upstream, resolve conflicts, and then proceed with the following steps:

    cp "${HOME}/.bin-oss/files/--HOME--/custom.gitignore" "${HOME}/.gitignore"
    mv "${HOME}/.bin-oss" "${HOME}/.dotfiles"
    source "${HOME}/.shellrc"
    install-dotfiles.rb
    
  • Quit and restart the Terminal application.


1.0-52

  • Removed auto-configuration from rancher desktop to not manage/change the PATH env var since that’s already done in this line of the .zshrc file.

Adopting these changes

  • Start rancher desktop, go into its preferences, and change the setting to not automatically set the PATH.
  • Restart Terminal app and verify that docker is in your PATH.

1.0-51

  • [.aliases] Uncommented start_docker and stop_docker and made them defensive.
  • Removed ‘ccleaner’ preferences since I am no longer using it.

1.0-50

  • All Firefox-based browsers are now handled for their respective chrome folders to be tracked and get updated as git repos.
  • [.aliases] Added utility functions for pull and push similar to st, count, etc taking in an optional git repo.
  • [.shellrc] Moved a utility function (set_ssh_folder_permissions) so that it can be reused.

Adopting these changes

  • After rebasing, just quit and restart the terminal emulator so that the .envrc is processed. (Hint: Use allow_all_direnv_configs to accept and process all .envrc files in your system.)

1.0-49

  • [capture-raycast-configs.sh] Automated initial password setup for Raycast export.

1.0-48

  • [.shellrc] Extract common functions strip_trailing_slash and extract_last_segment.
  • Use unset to jettison local variables once they are no longer needed.

Adopting these changes

  • After rebasing, just quit and restart the terminal emulator so that the .shellrc is loaded into memory.

1.0-47

  • [capture-defaults.sh] Added more macos preferences to be exported/imported for backup.
  • Removed Itsycal since raycast and/or a desktop widget can be used instead of a dedicated application.

1.0-46

  • Removed duplication (now scripts/resurrect-repositories.rb invokes the common function defined in the .shellrc).
  • Removed usage of eval to simplify running of shell commands.

Adopting these changes

  • After rebasing, just quit and restart the terminal emulator so that the .shellrc is loaded into memory.

1.0-45

  • [capture-raycast-configs.sh] Added script to export/import raycast configs. More details can be found here. Code contributed by/adapted from @arunvelsriram’s gist.
  • Reuse utility functions defined in .shellrc

1.0-44

  • [recreate-repo.sh] Fix an issue where a trailing slash would not properly process the repo in ${PERSONAL_PROFILES_DIR} (ie would not force-squash)
  • Cleaned files/--PERSONAL_PROFILES_DIR--/custom.gitignore

Adopting these changes

  • After rebasing, run the following command prior to running the install-dotfiles.rb script.

    cp "${DOTFILES_DIR}/files/--PERSONAL_PROFILES_DIR--/custom.gitignore" "${PERSONAL_PROFILES_DIR}/.gitignore"
    

1.0-43

  • Nested all Firefox-based profiles one level deeper.

Adopting these changes

These changes are optional, but if you don’t follow them, then the aliases/scripts pertaining to the browser profiles repo can be messed up..

  • Quit any FF-based browser before rebasing from my repo.
  • Run git -C "${DOTFILES_DIR}" upreb
  • Resolve all conflicts.
  • Open Finder on the ${PERSONAL_PROFILES_DIR}/
  • Inside each of the FF-based profiles folders, create a new folder called DefaultProfile and move all other sibling files/folders into that one.
  • Edit the profiles.ini and installs.ini files at the root of the FF profile folder, and add /DefaultProfile to the lines referring to the profile folder (usually it’ll be a relative path).
  • Restart your FF-based browser to verify that all functionality continues to work.

1.0-42

  • Added dev dependencies for zen-browser.
  • Unignore some files from the personal folder that were somehow ignored globally.

1.0-41

  • Added new script scripts/add-upstream-git-config.sh.

1.0-40

  • Fixed documentation and reduced hardcoding of upstream repo-owner’s name.

1.0-39

  • Introduced a new script to cleanup browser profiles folders.
  • [fresh-install-of-osx.sh] Minor refactoring to enhance clone_repo_into to handle an optional target git branch which is also validated.

1.0-38

  • [.aliases] Added extra checks for the status_all_repos and count_all_repos utility functions.

1.0-37

  • Removed Raycast from being tracked via the profiles repo since that corrupts Raycast’s internal db.

Adopting these changes

These instructions are only necessary if you had previously adopted changes from v1.0-24

  • In Raycast, use the Export Settings & Data option to export your current settings.
  • After successfully exporting the settings, quit Raycast and ensure that Raycast is completely shut down.
  • Rebase the dotfiles repo, fix any conflicts and run the install-dotfiles.rb script.
  • Manually reconcile the diffs / dirty state of files/--PERSONAL_PROFILES_DIR--/custom.gitignore with $PERSONAL_PROFILES_DIR/.gitignore on your local machine
  • Run the following commands in the terminal

    git -C "${DOTFILES_DIR}" restore files/--PERSONAL_PROFILES_DIR--/custom.gitignore
    cp "${DOTFILES_DIR}/files/--PERSONAL_PROFILES_DIR--/custom.gitignore" "${PERSONAL_PROFILES_DIR}/.gitignore"
    rm -rf "${HOME}/Library/Application Support/com.raycast.macos"
    mv "${PERSONAL_PROFILES_DIR}/Raycast" "${HOME}/Library/Application Support/com.raycast.macos"
    git -C "${PERSONAL_PROFILES_DIR}" rm -rf Raycast
    open /Applications/Raycast.app
    
  • Once Raycast is restarted AND if it shows an error about the database being corrupt, then choose the Reset option, and use the Import Settings & Data option to import your previously exported settings back in.
  • Once the above steps are done, if you rerun the install-dotfiles.rb script, it should not show any dirty files (especially the 2 custom.gitignore files) - and if this is the case, your setup is now back to normal working state.

1.0-36

  • Use is_git_repo instead of is_directory if the next command(s) expects it to be a git repo.
  • Remove Arc from Brewfile (since I moved to Zen).

1.0-35

  • Use git-restore-mtime from git-tools (as opposed to git-utimes from git-extras) since its > 1x faster performance.

1.0-34

  • Set the DNS server to ‘8.8.8.8’ only if running in a Jio network.
  • Introduce PDFGear and KeyClu.
  • Fixed some old documentation.

1.0-33

  • Reuse utility functions defined in .shellrc.

1.0-32

  • [fresh-install-of-osx.sh] Added date calculation in fresh-install-of-osx.sh to track total execution time.

1.0-31

  • [approve-fingerprint-sudo.sh] Handled case to execute approve-fingerprint-sudo.sh based on touchId hardware.

1.0-30

  • [resurrect-repositories.rb] Handled the case where git wouldn’t allow cloning a repo into a pre-existing, non-empty folder.
  • [.zshrc] Handled case where docker-related aliases were not setup since it was not in the PATH when files/--HOME--/.aliases was evaluated.

1.0-29

  • [capture-defaults.sh] Removed some applications that I no longer use.
  • [fresh-install-of-osx.sh] Replaced TODO with explanation for future reference as to why we can’t use homebrew to install omz custom plugins.

1.0-28

  • [Brewfile] Stop processing the Brewfile such that the minimal installation can happen in a shorter duration of time. This is controlled by the env var HOMEBREW_BASE_INSTALL which is set in the fresh-install-of-osx.sh script when installing from scratch.

1.0-27

  • [.aliases] Added 2 new utility functions: count and count_all_repos

1.0-26

  • Merged fresh-install-of-osx-advanced.sh into fresh-install-of-osx.sh to reduce complexity of loading different config files into the shell session.
  • [.gitconfig] Remove git sub-command currentDir in favor of root.
  • [Brewfile] Remove git-tools since git-extras has an equivalent git sub-command.
  • [.gitignore_global] Generate from gitignore.io for common languages, OSes and editors.
  • [fresh-install-of-osx.sh] Minimize use of eval and sub-shells.
  • [fresh-install-of-osx.sh] Moved utility scripts (from files/--HOME--/.aliases) that are only loaded while running the fresh-install-of-osx.sh into that single script to optimize shell startup time.
  • [fresh-install-of-osx.sh] Removed cloning of natsumi-browser from .envrc and moved into fresh-install script. Updating the repo is now handled as part of scripts/software-updates-cron.sh.
  • [.zshrc] Removed zsh-defer since that was introducing more complexity in maintenance.
  • [.shellrc] Use mktemp to enhance implementation of clone_repo_into which reduces need to process the home-repo in a special manner while doing a fresh install.
  • [.shellrc] Moved homebrew env vars from files/--HOME--/.zshenv into files/--HOME--/.shellrc.
  • Merged files/--HOME--/.zshrc.custom into files/--HOME--/.zshrc and files/--HOME--/.aliases.custom into files/--HOME--/.aliases to reduce complexity of loading different config files into the shell session.

Adopting these changes

  • After rebasing and resolving the conflicts
  • Manually reconcile the diffs between files/--HOME--/custom.gitignore & ${HOME}/.gitignore, and files/--PERSONAL_PROFILES_DIR--/custom.gitignore & ${PERSONAL_PROFILES_DIR}/.gitignore.
  • Open the Terminal application and run the following commands:

      rm -rf ${HOME}/.aliases.custom ${HOME}/.zshrc.custom ${HOME}/.oh-my-zsh/custom/plugins/zsh-defer
      cp files/--HOME--/custom.gitignore ${HOME}/.gitignore
      cp files/--PERSONAL_PROFILES_DIR--/custom.gitignore ${PERSONAL_PROFILES_DIR}/.gitignore
      install-dotfiles.rb
    
  • Quit and restart your Terminal application for the env vars, aliases & functions to be re-evaluated into the session memory.
  • Run bupc to cleanup brews and casks.

Note: This version has been successfully tested on a Macbook M1 on 22 Dec, 2024.


1.0-25

  • [capture-defaults.sh] Capture defaults script now aborts when the PERSONAL_CONFIGS_DIR env var is not defined.
  • [.shellrc] Extracted common utility function to remove duplication and invoke them in the setup scripts.
  • [fresh-install-of-osx-advanced.sh] Fixed potential issue with the PATH not being updated if the fresh-install-advanced script was run without starting a new terminal session.
  • [.aliases] Added a new profiles alias to handle git repos checked out into the PERSONAL_PROFILES_DIR.

1.0-24

  • Capture the Raycast configs/extensions/etc in the profiles repo

Adopting these changes

  • Open Terminal and run the install-dotfiles.rb script.
  • Change the current directory in terminal to the profiles repo (direnv will take care of the rest)

1.0-23


1.0-22

  • [.shellrc] Moved functions that are only needed in the basic fresh-install script into that so as to reduce shell startup time.

Note: This version has been successfully tested on a Macbook M1 on 19 Dec, 2024.


1.0-21

  • [fresh-install-of-osx-advanced.sh] Nested conditions and print more specific warning message when skipping cloning of the home and profiles repos.
  • [.shellrc] Extracted some utility functions to remove duplication and invoke them in the setup scripts.

Adopting these changes

  • Manually edit your ${HOME}/.ssh/config file, and change all occurrences of ~ to ${HOME}

1.0-20

  • Removed necessity of quitting and restarting the Terminal application between executing the fresh-install-of-osx.sh and fresh-install-of-osx-advanced.sh.
  • [.shellrc] Extracted some utility functions to remove duplication and invoke them in the setup scripts.
  • [.shellrc] Renamed ensure_dir_exists_if_var_defined into ensure_dir_exists and clone_if_not_present into clone_omz_plugin_if_not_present.
  • [Brewfile] Removed gs, wifi-password and virtualbox.

Note: This version has been successfully tested on a Macbook M1 on 16 Dec, 2024.

Adopting these changes

  • Run git delete-tag success-tested-on-m1; git push origin :success-tested-on-m1 to cleanup the defunct tag.

1.0-19

  • [Brewfile] Added keycastr to help with pairing and presentations of screen-grabs.
  • Added some more logging while running the fresh-install scripts.

1.0-18

  • Restructured Brewfile to convey what are bare minimum formulae vs recommended vs optional ie left to the user’s choice.

Adopting these changes

  • The reason for this restructuring is explained up above. Since most of the adoptees have customized this file, it will probably result in conflicts. Please be diligent in resolving the conflicts.

1.0-17

  • All GH urls now also take into account the branch that’s being tested for the setup scripts. Read the new section in the README if you are making changes that you want to test against a PR branch before the PR is merged.

1.0-16

  • Moved some of the core zsh config files from files/--HOME--/ to files/--ZDOTDIR--/ to accommodate custom location of ZDOTDIR.
  • [.shellrc] Merged all relevant lines from files/--ZDOTDIR--/.zprofile into files/--HOME--/.shellrc and deleted files/--ZDOTDIR--/.zprofile since that is the first file loaded during the fresh machine setup. This also avoids the defensive definition of ZDOTDIR in duplicate files.

Adopting these changes

  • After rebasing, you will end up with conflicts. The env vars that were previously defined in files/--ZDOTDIR--/.zprofile have been moved into files/--HOME--/.shellrc. You might have to manually fix them. You can go ahead and delete the ${HOME}/.zprofile since that is no longer needed.
  • Run install-dotfiles.rb so that the symlinked zsh config files in ${HOME} point to the correct locations (files/--ZDOTDIR--/ instead of files/--HOME--/)

1.0-15

  • [README.md] Fixed some grammatical errors in README.
  • [.gitconfig] Added new git alias for logs.

1.0-14

  • Use ‘zsh-defer’ to try to bring down shell startup time.

Adopting these changes

  • Run fresh-install-of-osx.sh so that the zsh-defer plugin is cloned to the correct directory.
  • Restart terminal for the deferred-loading to take effect. (No harm in keeping the old session).

1.0-13

  • [.shellrc] Introduced new utility functions section_header and debug and standardized on usages.

1.0-12

  • Reverted changes from v1.0.9 related to ‘bupc’ since the 1st cleanup might be skipped due to the ‘   ’ condition status

1.0-11

  • Converted from ‘iBar’ menubar app to ‘Ice’ since its open source and seems to have better features. This also removes the need to login into the App Store!

1.0-10

  • Fix zsh auto-completion since some of the options were set after the compinit invocation
  • [.zprofile] Ensure that directories are created for env vars defined in .zprofile
  • setopt paramters are case-insensitive and can handle underscore and so changed them for readability
  • [.shellrc] Introduced new utility function ensure_dir_exists_if_var_defined to help in cases where code-gist used to create unsaved files instead of directories for undefined env vars

1.0-9

  • Remove redundant cleanup in ‘bupc’
  • Removed MS Teams and MS Remote Desktop

Adopting these changes

  • Restart terminal for the revised alias function to get loaded. (No harm in keeping the old session; just that it will perform an extra step unnecessarily on bupc alias)

1.0-7

  • [fresh-install-of-osx.sh] Fix issue when running in a fresh/vanilla machine since ‘ZDOTDIR’ was undefined.

1.0-6

  • [install-dotfiles.rb] Fix issue when creating the include line for ~/.ssh/config if it was not present.

1.0-5

  • [approve-fingerprint-sudo.sh] Persists authorization config for triggering touchId when running sudo commands in terminal across software updates.

Adopting these changes

  • Run approve-fingerprint-sudo.sh

1.0-4

  • [install-dotfiles.rb] Refactored environment variable resolution logic to use gsub! for improved performance.

1.0-3

  • Moved all files & nested folders inside the files directory into files/--HOME-- to make that location explicit (earlier it was implied)

1.0-2

  • [install-dotfiles.rb] Refactored the logic to handle ssh global configuration file for ease of readability and maintainability.

1.0-1

  • [Brewfile] Added virtualbox to test out linux as a Virtual machine.
  • [CHANGELOG.md] Added changelog which will be maintained going forward for each commit.
  • [README.md] Added a new section detailing steps to adopt updates/catchups for new changes on an ongoing basis.
  • Changed all colored messages to be uniform and added a success function to print in green. These are optimized for a dark theme in your terminal emulator.

1.0

  • install-dotfiles.rb can now handle multiple env vars for nested files/folders in the files sub-folder. They follow the naming convention of the env var being enclosed within 2 pairs of hyphens (--). For eg, files/--PERSONAL_PROFILES_DIR--/.envrc will be symlinked on your local machine into ${HOME}/personal/<yourLocalUsername>/profiles/.envrc assuming that the PERSONAL_PROFILES_DIR env var has been defined. This is not a breaking change.

Adopting these changes

  • Since I recreated the 1.0 tag as part of this push, you might need to delete the tag in both your local and your remote and then do git upreb.
  • Run the install-dotfiles.rb script which will automatically remove the older (broken) symlink and recreate the new one in the correct location.