From 4eec6eeb774d909e7198fab1755156f1594963c7 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 11:57:58 -0500 Subject: [PATCH 01/11] claude(inspect-codefresh-failure): clarify how to get unblocked and rolling --- .../skills/inspect-codefresh-failure/SKILL.md | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/tools/claude/config/skills/inspect-codefresh-failure/SKILL.md b/tools/claude/config/skills/inspect-codefresh-failure/SKILL.md index dcc27c1a..d45d4b73 100644 --- a/tools/claude/config/skills/inspect-codefresh-failure/SKILL.md +++ b/tools/claude/config/skills/inspect-codefresh-failure/SKILL.md @@ -1,20 +1,54 @@ --- name: inspect-codefresh-failure -description: Inspects Codefresh CI build failures for recursionpharma org repos. Extracts build ID from status checks, fetches logs, and identifies specific errors with file/line references. Use when you see failing CI in a recursionpharma PR or when debugging CI-related issues. +description: Instruction guide for analyzing Codefresh CI failures. When invoked, IMMEDIATELY follow the step-by-step instructions to extract build IDs, fetch logs, and create a formatted failure report. Use when reviewing recursionpharma PRs with failing CI. allowed-tools: [Bash, Read] --- # Inspect Codefresh Failure Skill -Analyzes Codefresh CI failures for recursionpharma organization repositories and provides detailed failure reports with specific errors and root cause analysis. +## ⚔ THIS IS AN INSTRUCTION GUIDE - EXECUTE IMMEDIATELY + +After invoking this skill, **DO NOT WAIT** - immediately execute the following steps using the PR data you already have: + +### QUICK START - Execute Now: + +**STEP 1**: Extract build ID from the failing check + +- Look for `"state": "FAILURE"` or `"conclusion": "FAILURE"` in statusCheckRollup +- Find the Codefresh URL in `targetUrl` or `detailsUrl` +- Extract the build ID (24-char hex) from `https://g.codefresh.io/build/{build-id}` + +**STEP 2**: Fetch build metadata + +```bash +codefresh get builds -o json +``` + +**STEP 3**: Search logs for errors + +```bash +codefresh logs | grep -E -B 10 -A 20 -i "error|fail|found.*error" +``` + +**STEP 4**: Format your findings using the "Expected Output Format" template below + +**STEP 5**: Include the formatted report in your PR review under "## CI Failure Analysis" + +--- + +## Detailed Instructions + +This skill provides step-by-step guidance for analyzing Codefresh CI failures in recursionpharma organization repositories. ## Prerequisites **Required:** + - `codefresh` CLI must be installed (typically via Homebrew: `brew install codefresh`) - User must be authenticated with Codefresh (check with `codefresh auth current-context`) **If prerequisites are missing:** + - Inform the user that the Codefresh CLI is not available - Suggest they install it: `brew install codefresh` - Provide alternative: manually check the Codefresh build URL in the browser @@ -22,6 +56,7 @@ Analyzes Codefresh CI failures for recursionpharma organization repositories and ## When to Use This Skill Use this skill when: + - You see a failing CI check in a recursionpharma PR - User mentions CI failures in recursionpharma repos - Debugging "passes locally but fails in CI" issues @@ -51,12 +86,14 @@ Use this skill when: ## Usage Examples ### Example 1: From Build ID + ```bash # If you already have the build ID Build ID: 67c7469b3275b6f1b9f96f69 ``` ### Example 2: From PR Status Check Data + ```json { "statusCheckRollup": [ @@ -71,6 +108,7 @@ Build ID: 67c7469b3275b6f1b9f96f69 ``` ### Example 3: From Repository and PR Number + ```bash # Will fetch status checks and extract build ID Repository: recursionpharma/phenomics-potency-prediction @@ -173,8 +211,25 @@ Request changes - fix the 5 undefined `random_seed` errors before merging (criti - **Tested and verified** - Commands tested against build `67c7469b3275b6f1b9f96f69` (recursionpharma/phenomics-potency-prediction#2) **Common error patterns:** + - Linting errors (Ruff, Flake8, Black) - Look for "Found N errors" - Type errors (mypy, pyright) - Look for "error:" in output - Test failures (pytest, unittest) - Look for "FAILED" or "FAILURE" - Build failures (missing dependencies, import errors) - Look for "Error:" or "exit code" - Security scans (dependency vulnerabilities) - Check specific security step logs + +--- + +## šŸ”“ IMPORTANT REMINDERS + +**You are reading this skill because you are reviewing a PR with failing CI.** + +**RIGHT NOW, you should:** + +1. āœ… Have already fetched the PR data with `gh pr view --json statusCheckRollup` +2. āœ… See failing checks in that data with `"state": "FAILURE"` or `"conclusion": "FAILURE"` +3. āš ļø **NEXT**: Extract the build ID from the Codefresh URL and run the commands above +4. āš ļø **DO NOT**: Wait, ask questions, or pause - execute the steps immediately +5. āš ļø **GOAL**: Create a "## CI Failure Analysis" section in your PR review with specific errors + +**If you find yourself waiting or unsure what to do next, re-read the QUICK START section at the top.** From 76b9c84ff89142426ead493b8e225cd252e53150 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 12:34:20 -0500 Subject: [PATCH 02/11] zsh: source utils.zsh once in .zshrc --- features/check/shell.zsh | 2 -- features/install/shell.zsh | 1 - features/install/zsh/content.zsh | 1 - features/install/zsh/deprecated/homebrew.zsh | 1 - features/install/zsh/deprecated/neovim.zsh | 1 - features/install/zsh/deprecated/node.zsh | 1 - features/install/zsh/deprecated/rust.zsh | 1 - features/install/zsh/deprecated/tmux.zsh | 1 - features/install/zsh/deprecated/uv.zsh | 1 - features/install/zsh/github.zsh | 1 - features/install/zsh/settings.zsh | 1 - features/install/zsh/ssh.zsh | 1 - features/install/zsh/zsh.zsh | 1 - features/new/shell.zsh | 1 - features/restart/shell.zsh | 1 - features/run/shell.zsh | 1 - features/setup/setup.zsh | 1 - features/start/shell.zsh | 1 - features/stop/shell.zsh | 1 - features/submit/shell.zsh | 1 - features/test/shell.zsh | 1 - features/update/shell.zsh | 1 - features/update/zsh/deprecated/gcloud.zsh | 2 -- features/update/zsh/deprecated/macos.zsh | 2 -- features/update/zsh/deprecated/neovim.zsh | 1 - features/update/zsh/deprecated/npm.zsh | 1 - features/update/zsh/deprecated/rust.zsh | 1 - features/update/zsh/deprecated/symlinks.zsh | 1 - features/update/zsh/deprecated/tmux.zsh | 1 - features/update/zsh/homebrew.zsh | 1 - tools/@archive/television/shell.zsh | 1 - tools/@new/shell.zsh | 1 - tools/bat/shell.zsh | 1 - tools/btop/shell.zsh | 1 - tools/claude/config/skills/.gitkeep | 0 tools/claude/shell.zsh | 1 - tools/docker/shell.zsh | 1 - tools/eza/shell.zsh | 1 - tools/fnm/shell.zsh | 1 - tools/fzf/shell.zsh | 1 - tools/gcloud/shell.zsh | 1 - tools/homebrew/shell.zsh | 1 - tools/kitty/shell.zsh | 1 - tools/kubectl/shell.zsh | 1 - tools/lazydocker/shell.zsh | 1 - tools/lazygit/shell.zsh | 1 - tools/mise/shell/integration.zsh | 1 - tools/powerlevel10k/shell.zsh | 1 - tools/ruby/shell/integration.zsh | 1 - tools/ssh/shell.zsh | 1 - tools/stern/shell.zsh | 1 - tools/tailscale/shell.zsh | 1 - tools/tmux/shell.zsh | 1 - tools/uv/shell.zsh | 1 - tools/vscode/shell.zsh | 1 - tools/yazi/shell.zsh | 1 - tools/zoxide/shell/integration.zsh | 1 - tools/zsh/shell.zsh | 1 - 58 files changed, 60 deletions(-) delete mode 100644 tools/claude/config/skills/.gitkeep diff --git a/features/check/shell.zsh b/features/check/shell.zsh index 9760f568..e9e20755 100644 --- a/features/check/shell.zsh +++ b/features/check/shell.zsh @@ -1,5 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work - ########### # ALIASES # ########### diff --git a/features/install/shell.zsh b/features/install/shell.zsh index 19de589f..814216b5 100644 --- a/features/install/shell.zsh +++ b/features/install/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/install/zsh/content.zsh b/features/install/zsh/content.zsh index 5675dba7..f8ae2738 100755 --- a/features/install/zsh/content.zsh +++ b/features/install/zsh/content.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" repo="ooloth/content" local_repo="$HOME/Repos/$repo" diff --git a/features/install/zsh/deprecated/homebrew.zsh b/features/install/zsh/deprecated/homebrew.zsh index 1dad8044..1fe29744 100755 --- a/features/install/zsh/deprecated/homebrew.zsh +++ b/features/install/zsh/deprecated/homebrew.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" # Check if Homebrew is already installed if have brew; then diff --git a/features/install/zsh/deprecated/neovim.zsh b/features/install/zsh/deprecated/neovim.zsh index b1e5cb47..1348f36b 100755 --- a/features/install/zsh/deprecated/neovim.zsh +++ b/features/install/zsh/deprecated/neovim.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" repo="ooloth/config.nvim" local_repo="$HOME/Repos/$repo" diff --git a/features/install/zsh/deprecated/node.zsh b/features/install/zsh/deprecated/node.zsh index be3fc690..9c77dab7 100755 --- a/features/install/zsh/deprecated/node.zsh +++ b/features/install/zsh/deprecated/node.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" info "šŸ¦€ Installing Node via fnm" diff --git a/features/install/zsh/deprecated/rust.zsh b/features/install/zsh/deprecated/rust.zsh index 8cd868cb..81c7c811 100755 --- a/features/install/zsh/deprecated/rust.zsh +++ b/features/install/zsh/deprecated/rust.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" if have rustup; then printf "\nšŸ¦€ Rust is already installed\n" diff --git a/features/install/zsh/deprecated/tmux.zsh b/features/install/zsh/deprecated/tmux.zsh index a6484126..24c293e7 100755 --- a/features/install/zsh/deprecated/tmux.zsh +++ b/features/install/zsh/deprecated/tmux.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" ####### # TPM # diff --git a/features/install/zsh/deprecated/uv.zsh b/features/install/zsh/deprecated/uv.zsh index f84ae8aa..6867b5b9 100755 --- a/features/install/zsh/deprecated/uv.zsh +++ b/features/install/zsh/deprecated/uv.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" if have uv; then printf "\nāš”ļø uv is already installed\n" diff --git a/features/install/zsh/github.zsh b/features/install/zsh/github.zsh index 51cc92d1..09c5e380 100755 --- a/features/install/zsh/github.zsh +++ b/features/install/zsh/github.zsh @@ -5,7 +5,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" info "šŸ”‘ Adding SSH key pair to GitHub" diff --git a/features/install/zsh/settings.zsh b/features/install/zsh/settings.zsh index 1e50f681..39e63f6b 100755 --- a/features/install/zsh/settings.zsh +++ b/features/install/zsh/settings.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" info "šŸ’» Configuring macOS system settings" diff --git a/features/install/zsh/ssh.zsh b/features/install/zsh/ssh.zsh index c76f945c..583c8493 100755 --- a/features/install/zsh/ssh.zsh +++ b/features/install/zsh/ssh.zsh @@ -12,7 +12,6 @@ set -e DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" info "šŸ”‘ Installing SSH key pair" diff --git a/features/install/zsh/zsh.zsh b/features/install/zsh/zsh.zsh index aef213de..9739becb 100755 --- a/features/install/zsh/zsh.zsh +++ b/features/install/zsh/zsh.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -source "$DOTFILES/tools/zsh/utils.zsh" info "🐚 Configuring zsh shell" # Use the Homebrew version of Zsh diff --git a/features/new/shell.zsh b/features/new/shell.zsh index 614ee4ae..f4424e1c 100644 --- a/features/new/shell.zsh +++ b/features/new/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/restart/shell.zsh b/features/restart/shell.zsh index 6fbf5ef2..13d9174e 100644 --- a/features/restart/shell.zsh +++ b/features/restart/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/run/shell.zsh b/features/run/shell.zsh index ffd95f21..5a475f4e 100644 --- a/features/run/shell.zsh +++ b/features/run/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/setup/setup.zsh b/features/setup/setup.zsh index d1402b79..0e0159a3 100755 --- a/features/setup/setup.zsh +++ b/features/setup/setup.zsh @@ -128,7 +128,6 @@ source "${DOTINSTALL}/settings.zsh" # SUGGEST RESTART # ################### -source "$DOTFILES/tools/zsh/utils.zsh" info "šŸŽ‰ Setup complete!" printf "\nCongratulations! Your Mac is nearly set up.\n\n" diff --git a/features/start/shell.zsh b/features/start/shell.zsh index a633f884..bda8a1b1 100644 --- a/features/start/shell.zsh +++ b/features/start/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/stop/shell.zsh b/features/stop/shell.zsh index 734d9d38..f740e970 100644 --- a/features/stop/shell.zsh +++ b/features/stop/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/submit/shell.zsh b/features/submit/shell.zsh index 633446b6..3c1825e8 100644 --- a/features/submit/shell.zsh +++ b/features/submit/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/test/shell.zsh b/features/test/shell.zsh index c696bf28..a639ede2 100644 --- a/features/test/shell.zsh +++ b/features/test/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/update/shell.zsh b/features/update/shell.zsh index 606cea81..ab9b7876 100644 --- a/features/update/shell.zsh +++ b/features/update/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ########### # ALIASES # diff --git a/features/update/zsh/deprecated/gcloud.zsh b/features/update/zsh/deprecated/gcloud.zsh index b1564486..0f8e745b 100755 --- a/features/update/zsh/deprecated/gcloud.zsh +++ b/features/update/zsh/deprecated/gcloud.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -source "${DOTFILES}/tools/zsh/utils.zsh" # Return early if not installed if ! have gcloud; then @@ -8,7 +7,6 @@ if ! have gcloud; then fi # Otherwise, update -source "$DOTFILES/tools/zsh/utils.zsh" info "✨ Updating gcloud components" # The "quiet" flag skips interactive prompts by using the default or erroring (see: https://stackoverflow.com/a/31811541/8802485) diff --git a/features/update/zsh/deprecated/macos.zsh b/features/update/zsh/deprecated/macos.zsh index b71902af..c633b84a 100755 --- a/features/update/zsh/deprecated/macos.zsh +++ b/features/update/zsh/deprecated/macos.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -source "${DOTFILES}/tools/zsh/utils.zsh" # Return early on work laptop to avoid issues caused by updating too early if is_work; then @@ -8,7 +7,6 @@ if is_work; then fi # Otherwise, update -source "${DOTFILES}/tools/zsh/utils.zsh" info "šŸ’» Updating macOS software (after password, don't cancel!)" sudo softwareupdate --install --all --restart --agree-to-license --verbose diff --git a/features/update/zsh/deprecated/neovim.zsh b/features/update/zsh/deprecated/neovim.zsh index 3675642a..7d48d37b 100755 --- a/features/update/zsh/deprecated/neovim.zsh +++ b/features/update/zsh/deprecated/neovim.zsh @@ -2,7 +2,6 @@ set -euo pipefail -source "$DOTFILES/tools/zsh/utils.zsh" info "🧃 Updating Neovim lsp servers, linters and formatters" diff --git a/features/update/zsh/deprecated/npm.zsh b/features/update/zsh/deprecated/npm.zsh index 7a8466bc..2e5e7c84 100755 --- a/features/update/zsh/deprecated/npm.zsh +++ b/features/update/zsh/deprecated/npm.zsh @@ -2,7 +2,6 @@ set -euo pipefail -source "${DOTFILES}/tools/zsh/utils.zsh" main() { # TODO: install node via fnm if npm command is missing? diff --git a/features/update/zsh/deprecated/rust.zsh b/features/update/zsh/deprecated/rust.zsh index 396bbabb..d8cffa89 100755 --- a/features/update/zsh/deprecated/rust.zsh +++ b/features/update/zsh/deprecated/rust.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "$DOTFILES/tools/zsh/utils.zsh" # Install if missing if ! have rustup; then diff --git a/features/update/zsh/deprecated/symlinks.zsh b/features/update/zsh/deprecated/symlinks.zsh index 07801b6c..955bd639 100755 --- a/features/update/zsh/deprecated/symlinks.zsh +++ b/features/update/zsh/deprecated/symlinks.zsh @@ -7,7 +7,6 @@ DOTFILES="${HOME}/Repos/ooloth/dotfiles" HOMECONFIG="${HOME}/.config" -source "${DOTFILES}/tools/zsh/utils.zsh" symlink() { # Both arguments should be absolute paths diff --git a/features/update/zsh/deprecated/tmux.zsh b/features/update/zsh/deprecated/tmux.zsh index 99064ef7..10be8089 100755 --- a/features/update/zsh/deprecated/tmux.zsh +++ b/features/update/zsh/deprecated/tmux.zsh @@ -9,7 +9,6 @@ if [ ! -d "$TPM" ]; then fi # Then, update -source "$DOTFILES/tools/zsh/utils.zsh" info "✨ Updating tmux dependencies" # see: https://github.com/tmux-plugins/tpm/blob/master/docs/managing_plugins_via_cmd_line.md diff --git a/features/update/zsh/homebrew.zsh b/features/update/zsh/homebrew.zsh index e8fd65ac..4f05d3a8 100755 --- a/features/update/zsh/homebrew.zsh +++ b/features/update/zsh/homebrew.zsh @@ -2,7 +2,6 @@ DOTFILES="$HOME/Repos/ooloth/dotfiles" -source "${DOTFILES}/tools/zsh/utils.zsh" info "šŸŗ Updating homebrew packages" diff --git a/tools/@archive/television/shell.zsh b/tools/@archive/television/shell.zsh index f452bb36..aa7de985 100644 --- a/tools/@archive/television/shell.zsh +++ b/tools/@archive/television/shell.zsh @@ -1,6 +1,5 @@ # See: https://alexpasmantier.github.io/television/docs/Users/cli/ -source "${DOTFILES}/tools/zsh/utils.zsh" # TODO: save and source them instead? which is more performant? # see: https://alexpasmantier.github.io/television/docs/Users/shell-integration#customizing-shell-integration-scripts diff --git a/tools/@new/shell.zsh b/tools/@new/shell.zsh index 5c1da131..46f8f00e 100644 --- a/tools/@new/shell.zsh +++ b/tools/@new/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/bat/shell.zsh b/tools/bat/shell.zsh index 39ef299a..6287486e 100644 --- a/tools/bat/shell.zsh +++ b/tools/bat/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/btop/shell.zsh b/tools/btop/shell.zsh index d34d70af..ed174bc5 100644 --- a/tools/btop/shell.zsh +++ b/tools/btop/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/claude/config/skills/.gitkeep b/tools/claude/config/skills/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tools/claude/shell.zsh b/tools/claude/shell.zsh index d9633f22..467a0a4a 100644 --- a/tools/claude/shell.zsh +++ b/tools/claude/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/docker/shell.zsh b/tools/docker/shell.zsh index bf232eba..8d79f5d7 100644 --- a/tools/docker/shell.zsh +++ b/tools/docker/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work if ! have docker; then return 0 diff --git a/tools/eza/shell.zsh b/tools/eza/shell.zsh index 49cde68a..f3de56fd 100644 --- a/tools/eza/shell.zsh +++ b/tools/eza/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/fnm/shell.zsh b/tools/fnm/shell.zsh index 74c01394..ba3b4de8 100644 --- a/tools/fnm/shell.zsh +++ b/tools/fnm/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/fzf/shell.zsh b/tools/fzf/shell.zsh index 761e148a..db919c7d 100644 --- a/tools/fzf/shell.zsh +++ b/tools/fzf/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/gcloud/shell.zsh b/tools/gcloud/shell.zsh index 8deeaba7..8337c10e 100644 --- a/tools/gcloud/shell.zsh +++ b/tools/gcloud/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work if ! have gcloud; then return 0 diff --git a/tools/homebrew/shell.zsh b/tools/homebrew/shell.zsh index 63c51abc..8bfc428a 100644 --- a/tools/homebrew/shell.zsh +++ b/tools/homebrew/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/kitty/shell.zsh b/tools/kitty/shell.zsh index 0646fe48..11d00ddc 100644 --- a/tools/kitty/shell.zsh +++ b/tools/kitty/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/kubectl/shell.zsh b/tools/kubectl/shell.zsh index ff6b4d9d..461a4611 100644 --- a/tools/kubectl/shell.zsh +++ b/tools/kubectl/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/lazydocker/shell.zsh b/tools/lazydocker/shell.zsh index 205de00b..6cd74214 100644 --- a/tools/lazydocker/shell.zsh +++ b/tools/lazydocker/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/lazygit/shell.zsh b/tools/lazygit/shell.zsh index 2ca06be5..d87e1058 100644 --- a/tools/lazygit/shell.zsh +++ b/tools/lazygit/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/mise/shell/integration.zsh b/tools/mise/shell/integration.zsh index 9f963770..5a818ef9 100644 --- a/tools/mise/shell/integration.zsh +++ b/tools/mise/shell/integration.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -source "${DOTFILES}/tools/zsh/utils.zsh" if have mise; then eval "$(mise activate zsh)" diff --git a/tools/powerlevel10k/shell.zsh b/tools/powerlevel10k/shell.zsh index 8266bbdb..740aa2ab 100644 --- a/tools/powerlevel10k/shell.zsh +++ b/tools/powerlevel10k/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/ruby/shell/integration.zsh b/tools/ruby/shell/integration.zsh index 205472af..6f2aff08 100644 --- a/tools/ruby/shell/integration.zsh +++ b/tools/ruby/shell/integration.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -# source "${DOTFILES}/tools/zsh/utils.zsh" # TODO: replace command # if have tool_command; then diff --git a/tools/ssh/shell.zsh b/tools/ssh/shell.zsh index 943f2644..8de8e49d 100644 --- a/tools/ssh/shell.zsh +++ b/tools/ssh/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/stern/shell.zsh b/tools/stern/shell.zsh index 3166adb2..b507a8bf 100644 --- a/tools/stern/shell.zsh +++ b/tools/stern/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/tailscale/shell.zsh b/tools/tailscale/shell.zsh index ffec4671..3d84dcd4 100644 --- a/tools/tailscale/shell.zsh +++ b/tools/tailscale/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/tmux/shell.zsh b/tools/tmux/shell.zsh index 96eda29d..63202750 100644 --- a/tools/tmux/shell.zsh +++ b/tools/tmux/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/uv/shell.zsh b/tools/uv/shell.zsh index 1e80fa5f..8205ad09 100644 --- a/tools/uv/shell.zsh +++ b/tools/uv/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/vscode/shell.zsh b/tools/vscode/shell.zsh index 76408545..e3e2cdc6 100644 --- a/tools/vscode/shell.zsh +++ b/tools/vscode/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/yazi/shell.zsh b/tools/yazi/shell.zsh index 01e1762a..853cab18 100644 --- a/tools/yazi/shell.zsh +++ b/tools/yazi/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # diff --git a/tools/zoxide/shell/integration.zsh b/tools/zoxide/shell/integration.zsh index 99b48686..c209bec4 100644 --- a/tools/zoxide/shell/integration.zsh +++ b/tools/zoxide/shell/integration.zsh @@ -1,6 +1,5 @@ #!/usr/bin/env zsh -source "${DOTFILES}/tools/zsh/utils.zsh" if have zoxide; then eval "$(zoxide init zsh)" diff --git a/tools/zsh/shell.zsh b/tools/zsh/shell.zsh index 31ca7f42..836f776a 100644 --- a/tools/zsh/shell.zsh +++ b/tools/zsh/shell.zsh @@ -1,4 +1,3 @@ -source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work ######################## # ENVIROMENT VARIABLES # From 724c6a72c57d13a2e676ea77fa777fdd78e57550 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 13:23:12 -0500 Subject: [PATCH 03/11] zsh: cache a file manifest identifying all shell/*.zsh and shell.zsh files --- .gitignore | 1 + features/update/zsh/generate-manifest.zsh | 83 +++++++++++++++++++++++ tools/zsh/config/tools.zsh | 73 +++++--------------- tools/zsh/shell.zsh | 2 + 4 files changed, 102 insertions(+), 57 deletions(-) create mode 100644 features/update/zsh/generate-manifest.zsh diff --git a/.gitignore b/.gitignore index e4216f53..7c302852 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ # Mine **/.claude/settings.local.json +.cache .secrets Brewfile.lock.json diff --git a/features/update/zsh/generate-manifest.zsh b/features/update/zsh/generate-manifest.zsh new file mode 100644 index 00000000..c9f74b04 --- /dev/null +++ b/features/update/zsh/generate-manifest.zsh @@ -0,0 +1,83 @@ +#!/usr/bin/env zsh + +# Generate a manifest of all shell configuration files to source +# This eliminates expensive find operations on every shell startup + +DOTFILES="${DOTFILES:-${HOME}/Repos/ooloth/dotfiles}" +MANIFEST="${DOTFILES}/.cache/shell-files-manifest.zsh" + +# Create cache directory if it doesn't exist +mkdir -p "$(dirname "$MANIFEST")" + +# Start writing the manifest +cat > "$MANIFEST" <<'EOF' +# Auto-generated manifest of shell configuration files +# Regenerate by running: zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh + +############################ +# COMPLETE SHELL.ZSH FILES # +############################ + +# Features shell.zsh files +EOF + +# Find all shell.zsh files in features (except @new and @archive) +find "${DOTFILES}/features" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +cat >> "$MANIFEST" <<'EOF' + +# Tools shell.zsh files +EOF + +# Find all shell.zsh files in tools (except @new and @archive) +find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +cat >> "$MANIFEST" <<'EOF' + +##################################### +# LEGACY: SHELL/VARIABLES.ZSH FILES # +##################################### + +EOF + +# Find all shell/variables.zsh files in tools (except @new and @archive) +find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/variables.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +cat >> "$MANIFEST" <<'EOF' + +################################### +# LEGACY: SHELL/ALIASES.ZSH FILES # +################################### + +EOF + +# Find all shell/aliases.zsh files in features (except @new and @archive) +find "${DOTFILES}/features" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/aliases.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +# Find all shell/aliases.zsh files in tools (except @new and @archive) +find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/aliases.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +cat >> "$MANIFEST" <<'EOF' + +####################################### +# LEGACY: SHELL/INTEGRATION.ZSH FILES # +####################################### + +EOF + +# Find all shell/integration.zsh files in tools (except @new and @archive) +find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/integration.zsh" -print | sort | while read file; do + echo "source \"${file}\"" >> "$MANIFEST" +done + +printf "\nāœ… Manifest generated: ${MANIFEST}\n" diff --git a/tools/zsh/config/tools.zsh b/tools/zsh/config/tools.zsh index 97af5726..9f1ffd20 100644 --- a/tools/zsh/config/tools.zsh +++ b/tools/zsh/config/tools.zsh @@ -1,62 +1,21 @@ # Add tool-specific environment variables, aliases and completions to zsh environment +# +# This file uses a pre-generated manifest to avoid expensive find operations on every shell startup. The manifest +# includes all shell/*.zsh and shell.zsh files in this project is auto-regenerated when stale (i.e. any .zsh files +# are newer than the manifest). +# +# To manually regenerate: zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh + +MANIFEST="${DOTFILES}/.cache/shell-files-manifest.zsh" + +# Check if manifest needs regeneration +if [[ ! -f "${MANIFEST}" ]] || [[ -n $(find "${DOTFILES}/features" "${DOTFILES}/tools" -name "*.zsh" -newer "$MANIFEST" -print -quit) ]]; then + # Manifest is missing or stale, regenerate it + zsh "${DOTFILES}/features/update/zsh/generate-manifest.zsh" +fi -############################ -# COMPLETE SHELL.ZSH FILES # -############################ - -# Find all shell.zsh files in each tool directory (except @new and @archive) -shell_files_in_features=($(find "${DOTFILES}/features" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell.zsh" -print)) - -for file in "${shell_files_in_features[@]}"; do - source "${file}" -done - -# Find all shell.zsh files in each tool directory (except @new and @archive) -shell_files_in_tools=($(find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell.zsh" -print)) - -for file in "${shell_files_in_tools[@]}"; do - source "${file}" -done - -##################################### -# LEGACY: SHELL/VARIABLES.ZSH FILES # -##################################### - -# Find all shell/variables.bash files in each tool directory (except @new and @archive and zsh) -shell_variables_files=($(find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/variables.zsh" -print)) - -for file in "${shell_variables_files[@]}"; do - source "${file}" -done - -################################### -# LEGACY: SHELL/ALIASES.ZSH FILES # -################################### - -# Find all shell/aliases.bash files in each feature directory (except @new and @archive) -shell_aliases_in_features=($(find "${DOTFILES}/features" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/aliases.zsh" -print)) - -for file in "${shell_aliases_in_features[@]}"; do - source "${file}" -done - -# Find all shell/aliases.bash files in each tool directory (except @new and @archive and zsh) -shell_aliases_in_tools=($(find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/aliases.zsh" -print)) - -for file in "${shell_aliases_in_tools[@]}"; do - source "${file}" -done - -####################################### -# LEGACY: SHELL/INTEGRATION.ZSH FILES # -####################################### - -# Find all shell/integration.bash files in each tool directory (except @new and @archive) -shell_integration_files=($(find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/integration.zsh" -print)) - -for file in "${shell_integration_files[@]}"; do - source "${file}" -done +# Source the manifest (contains all shell.zsh files) +source "${MANIFEST}" ################################### # LEGACY: ONE-OFF VARIABLES SETUP # diff --git a/tools/zsh/shell.zsh b/tools/zsh/shell.zsh index 836f776a..0181a279 100644 --- a/tools/zsh/shell.zsh +++ b/tools/zsh/shell.zsh @@ -43,6 +43,8 @@ kill() { alias R="source ${HOME}/.zshenv && source ${HOME}/.zshrc" # see https://stackoverflow.com/questions/56284264/recommended-method-for-reloading-zshrc-source-vs-exec +alias regen="zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh" # regenerate shell manifest (rarely needed - happens automatically when stale) + sl() { local source_file="$1" local target_dir="$2" From f3df1a07c84be5d3fc4367b91aee0630afdb6e62 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 13:47:09 -0500 Subject: [PATCH 04/11] zsh: move core settings to a single core.zsh file --- tools/zsh/config/.zshrc | 7 +- tools/zsh/config/completions.zsh | 9 --- tools/zsh/config/core.zsh | 109 +++++++++++++++++++++++++++++++ tools/zsh/config/options.zsh | 20 ------ tools/zsh/shell.zsh | 73 --------------------- 5 files changed, 112 insertions(+), 106 deletions(-) delete mode 100644 tools/zsh/config/completions.zsh create mode 100644 tools/zsh/config/core.zsh delete mode 100644 tools/zsh/config/options.zsh delete mode 100644 tools/zsh/shell.zsh diff --git a/tools/zsh/config/.zshrc b/tools/zsh/config/.zshrc index 1cb18fb7..68d41808 100644 --- a/tools/zsh/config/.zshrc +++ b/tools/zsh/config/.zshrc @@ -7,7 +7,6 @@ fi export DOTFILES="${HOME}/Repos/ooloth/dotfiles" source "${DOTFILES}/tools/zsh/utils.zsh" # have, is_work, info, etc -source "${DOTFILES}/tools/zsh/config/options.zsh" -source "${DOTFILES}/tools/zsh/config/completions.zsh" -source "${DOTFILES}/tools/zsh/config/hooks.zsh" -source "${DOTFILES}/tools/zsh/config/tools.zsh" # source last +source "${DOTFILES}/tools/zsh/config/core.zsh" # env, history, completions, aliases +source "${DOTFILES}/tools/zsh/config/hooks.zsh" # python venv activation +source "${DOTFILES}/tools/zsh/config/tools.zsh" # tool-specific configs via manifest diff --git a/tools/zsh/config/completions.zsh b/tools/zsh/config/completions.zsh deleted file mode 100644 index 10fba999..00000000 --- a/tools/zsh/config/completions.zsh +++ /dev/null @@ -1,9 +0,0 @@ -# Must come before compinit to support rust tab completions -# See: https://rust-lang.github.io/rustup/installation/index.html -fpath+=~/.zfunc - -# Initialize zsh completion before invoking tool-specific completions -# See: https://stackoverflow.com/questions/66338988/complete13-command-not-found-compde -autoload -Uz compinit && compinit -autoload -Uz bashcompinit && bashcompinit - diff --git a/tools/zsh/config/core.zsh b/tools/zsh/config/core.zsh new file mode 100644 index 00000000..137d66f3 --- /dev/null +++ b/tools/zsh/config/core.zsh @@ -0,0 +1,109 @@ +# Core zsh configuration: environment, options, history, completions, aliases + +######################### +# ENVIRONMENT VARIABLES # +######################### + +# Path +export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:${PATH}" # Default system paths +export PATH="/opt/local/bin:/opt/local/sbin:${PATH}" # Add MacPorts to PATH +export PATH="${HOME}/.local/bin:${PATH}" # Add local bin to PATH + +# General +export EDITOR=nvim +export SHELL=/opt/homebrew/bin/zsh +export XDG_CONFIG_HOME="${HOME}/.config" + +# History variables +export HISTFILE="${HOME}/.zsh_history" +export HISTORY_IGNORE="git*" +export HISTSIZE=100000 +export SAVEHIST=$HISTSIZE + +########### +# OPTIONS # +########### + +# see: https://ryantoddgarza.medium.com/a-better-zsh-history-pt-2-dde323e0c9ca +# see: https://www.reddit.com/r/zsh/comments/wy0sm6/what_is_your_history_configuration/ +# see: https://zsh.sourceforge.io/Doc/Release/Parameters.html#Parameters-Used-By-The-Shell +setopt EXTENDED_HISTORY # Write the history file in the ':start:elapsed;command' format. +setopt HIST_EXPIRE_DUPS_FIRST # Expire a duplicate event first when trimming history. +setopt HIST_FIND_NO_DUPS # Do not display a previously found event. +setopt HIST_IGNORE_ALL_DUPS # Delete an old recorded event if a new event is a duplicate. +setopt HIST_IGNORE_DUPS # Do not record an event that was just recorded again. +setopt HIST_IGNORE_SPACE # Do not record an event starting with a space. +setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file. +setopt INC_APPEND_HISTORY # Record events as soon as they happen instead of waiting until the shell exits (so I can reload the shell without losing history). +setopt SHARE_HISTORY # Share history between all sessions. + +# enable vi mode +bindkey -v + +# restore history search while in vi mode +# bindkey ^R history-incremental-search-backward +# bindkey ^S history-incremental-search-forward + +############### +# COMPLETIONS # +############### + +# Must come before compinit to support rust tab completions +# See: https://rust-lang.github.io/rustup/installation/index.html +fpath+=~/.zfunc + +# Initialize zsh completion before invoking tool-specific completions +# See: https://stackoverflow.com/questions/66338988/complete13-command-not-found-compde +autoload -Uz compinit && compinit +autoload -Uz bashcompinit && bashcompinit + +# Third-party core completions +if have brew; then + source "/opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh" + source "/opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +fi + +########### +# ALIASES +########### + +alias ..='cd ..' +alias ...='cd ../..' +alias ....='cd ../../..' +alias .....='cd ../../../..' + +alias c="clear" + +# Crontab +alias cte="EDITOR=nvim crontab -e" +alias ctl="crontab -l" + +alias env="env | sort" + +kill() { + local port="${1}" + lsof -t -i:"${port}" | xargs kill -9; +} + +alias R="source ${HOME}/.zshenv && source ${HOME}/.zshrc" # see https://stackoverflow.com/questions/56284264/recommended-method-for-reloading-zshrc-source-vs-exec + +alias regen="zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh" # regenerate shell manifest (rarely needed - happens automatically when stale) + +sl() { + local source_file="$1" + local target_dir="$2" + + mkdir -p "$target_dir" + printf "šŸ”— " # prefix (no newline included) + ln -fsvw "$source_file" "$target_dir"; +} + +alias x="exit" + +zt() { + # [z]sh [t]ime: measure how long new shells take to launch + for i in $(seq 1 10); do + time zsh -i -c exit + done +} + diff --git a/tools/zsh/config/options.zsh b/tools/zsh/config/options.zsh deleted file mode 100644 index aaa377b6..00000000 --- a/tools/zsh/config/options.zsh +++ /dev/null @@ -1,20 +0,0 @@ -# History -# see: https://ryantoddgarza.medium.com/a-better-zsh-history-pt-2-dde323e0c9ca -# see: https://www.reddit.com/r/zsh/comments/wy0sm6/what_is_your_history_configuration/ -# see: https://zsh.sourceforge.io/Doc/Release/Parameters.html#Parameters-Used-By-The-Shell -setopt EXTENDED_HISTORY # Write the history file in the ':start:elapsed;command' format. -setopt HIST_EXPIRE_DUPS_FIRST # Expire a duplicate event first when trimming history. -setopt HIST_FIND_NO_DUPS # Do not display a previously found event. -setopt HIST_IGNORE_ALL_DUPS # Delete an old recorded event if a new event is a duplicate. -setopt HIST_IGNORE_DUPS # Do not record an event that was just recorded again. -setopt HIST_IGNORE_SPACE # Do not record an event starting with a space. -setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file. -setopt INC_APPEND_HISTORY # Record events as soon as they happen instead of waiting until the shell exits (so I can reload the shell without losing history). -setopt SHARE_HISTORY # Share history between all sessions. - -# enable vi mode -bindkey -v - -# restore history search while in vi mode -# bindkey ^R history-incremental-search-backward -# bindkey ^S history-incremental-search-forward diff --git a/tools/zsh/shell.zsh b/tools/zsh/shell.zsh deleted file mode 100644 index 0181a279..00000000 --- a/tools/zsh/shell.zsh +++ /dev/null @@ -1,73 +0,0 @@ - -######################## -# ENVIROMENT VARIABLES # -######################## - -# Path -export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:${PATH}" # Default system paths -export PATH="/opt/local/bin:/opt/local/sbin:${PATH}" # Add MacPorts to PATH -export PATH="${HOME}/.local/bin:${PATH}" # Add local bin to PATH - -# History -export HISTFILE="${HOME}/.zsh_history" -export HISTORY_IGNORE="git*" -export HISTSIZE=100000 -export SAVEHIST=$HISTSIZE - -# General -export EDITOR=nvim -export SHELL=/opt/homebrew/bin/zsh -export XDG_CONFIG_HOME="${HOME}/.config" - -########### -# ALIASES # -########### - -alias ..='cd ..' -alias ...='cd ../..' -alias ....='cd ../../..' -alias .....='cd ../../../..' - -alias c="clear" - -# Crontab -alias cte="EDITOR=nvim crontab -e" -alias ctl="crontab -l" - -alias env="env | sort" - -kill() { - local port="${1}" - lsof -t -i:"${port}" | xargs kill -9; -} - -alias R="source ${HOME}/.zshenv && source ${HOME}/.zshrc" # see https://stackoverflow.com/questions/56284264/recommended-method-for-reloading-zshrc-source-vs-exec - -alias regen="zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh" # regenerate shell manifest (rarely needed - happens automatically when stale) - -sl() { - local source_file="$1" - local target_dir="$2" - - mkdir -p "$target_dir" - printf "šŸ”— " # prefix (no newline included) - ln -fsvw "$source_file" "$target_dir"; -} - -alias x="exit" - -zt() { - # [z]sh [t]ime: measure how long new shells take to launch - for i in $(seq 1 10); do - time zsh -i -c exit - done -} - -############### -# COMPLETIONS # -############### - -if have brew; then - source "/opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh" - source "/opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" -fi From 98537f39b33b15a204b57e8f7fe651aab606eec3 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 13:49:57 -0500 Subject: [PATCH 05/11] Update tools/@new/shell.zsh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tools/@new/shell.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/@new/shell.zsh b/tools/@new/shell.zsh index 46f8f00e..945a0583 100644 --- a/tools/@new/shell.zsh +++ b/tools/@new/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### From 6465860a9a4863d62abcf09452310cb41507f48b Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 13:50:07 -0500 Subject: [PATCH 06/11] Update tools/bat/shell.zsh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tools/bat/shell.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bat/shell.zsh b/tools/bat/shell.zsh index 6287486e..bd3b23a4 100644 --- a/tools/bat/shell.zsh +++ b/tools/bat/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### From 20ecd7df881d64b08b8bd2912a6d15cb3e84f579 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 13:52:18 -0500 Subject: [PATCH 07/11] fix(typo): "ENVIROMENT" -> "ENVIRONMENT" --- tools/@new/shell.zsh | 2 +- tools/bat/shell.zsh | 2 +- tools/btop/shell.zsh | 2 +- tools/claude/shell.zsh | 2 +- tools/docker/shell.zsh | 2 +- tools/eza/shell.zsh | 2 +- tools/fnm/shell.zsh | 2 +- tools/fzf/shell.zsh | 2 +- tools/gcloud/shell.zsh | 2 +- tools/homebrew/shell.zsh | 2 +- tools/kitty/shell.zsh | 3 +-- tools/kubectl/shell.zsh | 2 +- tools/lazydocker/shell.zsh | 2 +- tools/lazygit/shell.zsh | 2 +- tools/powerlevel10k/shell.zsh | 2 +- tools/ssh/shell.zsh | 2 +- tools/stern/shell.zsh | 2 +- tools/tailscale/shell.zsh | 2 +- tools/tmux/shell.zsh | 2 +- tools/uv/shell.zsh | 2 +- tools/vscode/shell.zsh | 2 +- tools/yazi/shell.zsh | 2 +- 22 files changed, 22 insertions(+), 23 deletions(-) diff --git a/tools/@new/shell.zsh b/tools/@new/shell.zsh index 46f8f00e..945a0583 100644 --- a/tools/@new/shell.zsh +++ b/tools/@new/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/bat/shell.zsh b/tools/bat/shell.zsh index 6287486e..bd3b23a4 100644 --- a/tools/bat/shell.zsh +++ b/tools/bat/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/btop/shell.zsh b/tools/btop/shell.zsh index ed174bc5..3be1d383 100644 --- a/tools/btop/shell.zsh +++ b/tools/btop/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/claude/shell.zsh b/tools/claude/shell.zsh index 467a0a4a..9c5877b8 100644 --- a/tools/claude/shell.zsh +++ b/tools/claude/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## # See: https://docs.anthropic.com/en/docs/claude-code/settings#environment-variables diff --git a/tools/docker/shell.zsh b/tools/docker/shell.zsh index 8d79f5d7..3c90899a 100644 --- a/tools/docker/shell.zsh +++ b/tools/docker/shell.zsh @@ -4,7 +4,7 @@ if ! have docker; then fi ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/eza/shell.zsh b/tools/eza/shell.zsh index f3de56fd..1bf99283 100644 --- a/tools/eza/shell.zsh +++ b/tools/eza/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## # see: https://github.com/eza-community/eza/blob/main/man/eza.1.md#environment-variables diff --git a/tools/fnm/shell.zsh b/tools/fnm/shell.zsh index ba3b4de8..54b91dfa 100644 --- a/tools/fnm/shell.zsh +++ b/tools/fnm/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/fzf/shell.zsh b/tools/fzf/shell.zsh index db919c7d..ef65a2ba 100644 --- a/tools/fzf/shell.zsh +++ b/tools/fzf/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/gcloud/shell.zsh b/tools/gcloud/shell.zsh index 8337c10e..f1bf1d62 100644 --- a/tools/gcloud/shell.zsh +++ b/tools/gcloud/shell.zsh @@ -4,7 +4,7 @@ if ! have gcloud; then fi ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## export PATH="${HOME}/google-cloud-sdk/bin:$PATH" diff --git a/tools/homebrew/shell.zsh b/tools/homebrew/shell.zsh index 8bfc428a..4c2a026a 100644 --- a/tools/homebrew/shell.zsh +++ b/tools/homebrew/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:${PATH}" diff --git a/tools/kitty/shell.zsh b/tools/kitty/shell.zsh index 11d00ddc..9ef69b77 100644 --- a/tools/kitty/shell.zsh +++ b/tools/kitty/shell.zsh @@ -1,6 +1,5 @@ - ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/kubectl/shell.zsh b/tools/kubectl/shell.zsh index 461a4611..ad5d610b 100644 --- a/tools/kubectl/shell.zsh +++ b/tools/kubectl/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/lazydocker/shell.zsh b/tools/lazydocker/shell.zsh index 6cd74214..9e536cc9 100644 --- a/tools/lazydocker/shell.zsh +++ b/tools/lazydocker/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/lazygit/shell.zsh b/tools/lazygit/shell.zsh index d87e1058..4588f4e1 100644 --- a/tools/lazygit/shell.zsh +++ b/tools/lazygit/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/powerlevel10k/shell.zsh b/tools/powerlevel10k/shell.zsh index 740aa2ab..4c362ba3 100644 --- a/tools/powerlevel10k/shell.zsh +++ b/tools/powerlevel10k/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## # Powerlevel10k diff --git a/tools/ssh/shell.zsh b/tools/ssh/shell.zsh index 8de8e49d..9e7a8815 100644 --- a/tools/ssh/shell.zsh +++ b/tools/ssh/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/stern/shell.zsh b/tools/stern/shell.zsh index b507a8bf..26c82275 100644 --- a/tools/stern/shell.zsh +++ b/tools/stern/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/tailscale/shell.zsh b/tools/tailscale/shell.zsh index 3d84dcd4..350985c1 100644 --- a/tools/tailscale/shell.zsh +++ b/tools/tailscale/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/tmux/shell.zsh b/tools/tmux/shell.zsh index 63202750..33a251ec 100644 --- a/tools/tmux/shell.zsh +++ b/tools/tmux/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/uv/shell.zsh b/tools/uv/shell.zsh index 8205ad09..fdb58418 100644 --- a/tools/uv/shell.zsh +++ b/tools/uv/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/vscode/shell.zsh b/tools/vscode/shell.zsh index e3e2cdc6..685abbbd 100644 --- a/tools/vscode/shell.zsh +++ b/tools/vscode/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### diff --git a/tools/yazi/shell.zsh b/tools/yazi/shell.zsh index 853cab18..a5365e15 100644 --- a/tools/yazi/shell.zsh +++ b/tools/yazi/shell.zsh @@ -1,6 +1,6 @@ ######################## -# ENVIROMENT VARIABLES # +# ENVIRONMENT VARIABLES # ######################## ########### From 77dcc280c9e34955c7ae2db7ca94713538b157a9 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 14:00:37 -0500 Subject: [PATCH 08/11] zsh: source instead of spawning a subshell (small perf win) --- tools/zsh/config/core.zsh | 2 +- tools/zsh/config/tools.zsh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/zsh/config/core.zsh b/tools/zsh/config/core.zsh index 137d66f3..2be756a5 100644 --- a/tools/zsh/config/core.zsh +++ b/tools/zsh/config/core.zsh @@ -87,7 +87,7 @@ kill() { alias R="source ${HOME}/.zshenv && source ${HOME}/.zshrc" # see https://stackoverflow.com/questions/56284264/recommended-method-for-reloading-zshrc-source-vs-exec -alias regen="zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh" # regenerate shell manifest (rarely needed - happens automatically when stale) +alias regen="source ${DOTFILES}/features/update/zsh/generate-manifest.zsh" # regenerate shell manifest (rarely needed - happens automatically when stale) sl() { local source_file="$1" diff --git a/tools/zsh/config/tools.zsh b/tools/zsh/config/tools.zsh index 9f1ffd20..c7cb3813 100644 --- a/tools/zsh/config/tools.zsh +++ b/tools/zsh/config/tools.zsh @@ -4,14 +4,14 @@ # includes all shell/*.zsh and shell.zsh files in this project is auto-regenerated when stale (i.e. any .zsh files # are newer than the manifest). # -# To manually regenerate: zsh ${DOTFILES}/features/update/zsh/generate-manifest.zsh +# To manually regenerate: source ${DOTFILES}/features/update/zsh/generate-manifest.zsh MANIFEST="${DOTFILES}/.cache/shell-files-manifest.zsh" # Check if manifest needs regeneration if [[ ! -f "${MANIFEST}" ]] || [[ -n $(find "${DOTFILES}/features" "${DOTFILES}/tools" -name "*.zsh" -newer "$MANIFEST" -print -quit) ]]; then # Manifest is missing or stale, regenerate it - zsh "${DOTFILES}/features/update/zsh/generate-manifest.zsh" + source "${DOTFILES}/features/update/zsh/generate-manifest.zsh" fi # Source the manifest (contains all shell.zsh files) From fe99545d52b9760611237025cc6f5eb490a919b4 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 14:01:03 -0500 Subject: [PATCH 09/11] zsh: stop logging when manifest regenerated on startup (powerlevel10k doesn't like it) --- features/update/zsh/generate-manifest.zsh | 2 -- 1 file changed, 2 deletions(-) diff --git a/features/update/zsh/generate-manifest.zsh b/features/update/zsh/generate-manifest.zsh index c9f74b04..57aba031 100644 --- a/features/update/zsh/generate-manifest.zsh +++ b/features/update/zsh/generate-manifest.zsh @@ -79,5 +79,3 @@ EOF find "${DOTFILES}/tools" -type d \( -name "@new" -o -name "@archive" \) -prune -o -type f -path "*/shell/integration.zsh" -print | sort | while read file; do echo "source \"${file}\"" >> "$MANIFEST" done - -printf "\nāœ… Manifest generated: ${MANIFEST}\n" From ee2512f7047beed2f43a88c564ede80edf51b6d2 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 14:01:15 -0500 Subject: [PATCH 10/11] docs: commit claude's initial spec --- .claude/specs/zsh-startup-optimization.md | 354 ++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 .claude/specs/zsh-startup-optimization.md diff --git a/.claude/specs/zsh-startup-optimization.md b/.claude/specs/zsh-startup-optimization.md new file mode 100644 index 00000000..4972879e --- /dev/null +++ b/.claude/specs/zsh-startup-optimization.md @@ -0,0 +1,354 @@ +# Zsh Startup Optimization Plan + +## Problem Statement + +Zsh shell startup has become slow due to automatic file sourcing. Current startup process performs expensive operations on every shell initialization. + +## Root Causes + +### 1. Multiple Expensive `find` Operations (Primary Bottleneck) + +**Location:** `tools/zsh/config/tools.zsh:8-59` + +The file executes **6 separate `find` commands** on every shell startup: + +1. `find` for `shell.zsh` files in features/ (→ 10 files) +2. `find` for `shell.zsh` files in tools/ (→ 27 files, including @archive) +3. `find` for `shell/variables.zsh` files (→ 11 files) +4. `find` for `shell/aliases.zsh` in features/ (→ 0 files) +5. `find` for `shell/aliases.zsh` in tools/ (→ 11 files) +6. `find` for `shell/integration.zsh` files (→ 10 files) + +**Total:** ~69 files sourced after 6 directory tree traversals + +**Impact:** Each `find` command traverses the entire directory tree, making this extremely expensive. This compounds because: +- The dotfiles repository has many directories to search +- Operations are sequential (not parallel) +- This happens on every single shell startup + +### 2. Redundant Sourcing of Common Files āœ… FIXED + +**Location:** Nearly every `shell.zsh` file + +**Pattern:** +```zsh +source "${DOTFILES}/tools/zsh/utils.zsh" # sourced in ~56 files +``` + +**Impact:** +- `utils.zsh` was sourced ~56 times +- `utils.zsh` itself sources `macos/shell/aliases.zsh` +- Resulted in dozens of redundant file reads and executions + +**Fix Applied:** Removed all 56 redundant source statements. Now sourced once in `.zshrc:9`. + +### 3. Multiple `eval` Commands + +**Locations:** +- `tools/fzf/shell.zsh:15` - `eval "$(fzf --zsh)"` +- `tools/homebrew/shell.zsh:12` - `eval "$(/opt/homebrew/bin/brew shellenv)"` +- `tools/mise/shell/integration.zsh:5` - `eval "$(mise activate zsh)"` +- `tools/zoxide/shell/integration.zsh:5` - `eval "$(zoxide init zsh)"` +- `tools/fnm/shell.zsh:18` - `eval "$(fnm env --use-on-cd --shell zsh ...)"` +- `tools/uv/shell.zsh:15-16` - `eval "$(uv generate-shell-completion zsh)"` + uvx completion + +**Impact:** Each `eval` spawns a subprocess and executes commands, adding latency + +### 4. Repeated `have` Function Calls + +**Location:** Throughout shell.zsh files (16 files) + +**Pattern:** +```zsh +if have ; then + # setup for tool +fi +``` + +**Impact:** The `have` function uses `type` command which can be slow when called repeatedly. However, this is good practice for conditional loading and shouldn't be removed. + +### 5. Archive Files Exclusion āœ… VERIFIED WORKING + +**Location:** `tools/zsh/config/tools.zsh:15` + +**Status:** Verified that `find` commands correctly exclude @archive directories via `-prune` flag. No issues found. + +### 6. Many Small Files with High Overhead + +**Finding:** 34 shell.zsh files sourced at startup, with ~20 of them containing only 1-3 lines of actual content + +**Examples:** +- 10 files with just 1 line (single alias like `alias cat="bat"`) +- Files have extensive boilerplate (section headers) but minimal content +- Each file requires I/O operation regardless of size + +**Impact:** Overhead of sourcing 34 files individually vs. consolidated approach + +## Optimization Strategies + +### Strategy 1: Pre-Generate File Manifest (Recommended) + +**Approach:** Create a manifest file listing all files to source, regenerated only when the repository structure changes. + +**Implementation:** +1. Create a script to generate `${DOTFILES}/.cache/shell-files-manifest.zsh` +2. Manifest contains hardcoded paths (no `find` needed) +3. Source manifest instead of running `find` commands +4. Regenerate manifest: + - After install/update scripts run + - When new tools are added + - Optionally: check if manifest is stale (compare timestamp to newest .zsh file) + +**Pros:** +- Eliminates all `find` operations +- Fastest possible approach +- Simple implementation + +**Cons:** +- Requires remembering to regenerate manifest when adding new tools +- Could get out of sync (mitigated by staleness check) + +### Strategy 2: Lazy Loading + +**Approach:** Defer loading of non-essential tools until first use. + +**Implementation:** +1. Categorize files into: + - **Critical:** Must load on startup (PATH, core aliases) + - **Deferred:** Can load on first use (tool-specific features) +2. For deferred tools, create wrapper functions that: + - Load the real implementation on first call + - Replace themselves with the real function + +**Example:** +```zsh +# Instead of sourcing fzf immediately, create wrapper: +fzf() { + unfunction fzf + eval "$(fzf --zsh)" + fzf "$@" +} +``` + +**Pros:** +- Dramatically faster startup for features you don't use immediately +- No manifest to maintain + +**Cons:** +- Complexity: requires categorizing files and creating wrappers +- First use of each tool has slight delay +- Some tools (like PATH modifications) can't be deferred + +### Strategy 3: Consolidate Shell Files + +**Approach:** Merge the 69 separate files into a smaller number of consolidated files. + +**Implementation:** +1. Eliminate `shell.zsh` pattern entirely +2. Create consolidated files: + - `${DOTFILES}/.cache/all-variables.zsh` (all environment variables) + - `${DOTFILES}/.cache/all-aliases.zsh` (all aliases) + - `${DOTFILES}/.cache/all-completions.zsh` (all completions) +3. Source just these 3-4 files instead of 69 + +**Pros:** +- Fewer file I/O operations +- Eliminates redundant sourcing of utils.zsh +- Clean separation of concerns + +**Cons:** +- Loses modular structure +- Harder to understand what each tool contributes +- Requires generation step (similar to manifest approach) + +### Strategy 4: Fix Redundant Sourcing āœ… COMPLETED + +**Approach:** Source common files (like utils.zsh) once instead of in every shell.zsh file. + +**Implementation:** +1. Move `source utils.zsh` from individual files to main .zshrc (before sourcing other files) +2. Remove all `source utils.zsh` lines from shell.zsh files +3. Ensure utils.zsh is sourced exactly once + +**Result:** Removed from 56 files. **Massive improvement** reported by user. + +**Pros:** +- Simple change +- Immediate improvement +- No architectural changes needed + +**Cons:** +- Doesn't solve the fundamental `find` performance issue +- Still sourcing 69 files + +### Strategy 5: Cache `eval` Results + +**Approach:** Cache the output of expensive `eval` commands. + +**Implementation:** +For each expensive `eval`: +```zsh +# Before +eval "$(mise activate zsh)" + +# After +_mise_cache="${DOTFILES}/.cache/mise-activate.zsh" +if [[ ! -f "$_mise_cache" ]] || [[ $(mise --version) != $(cat "${_mise_cache}.version" 2>/dev/null) ]]; then + mise activate zsh > "$_mise_cache" + mise --version > "${_mise_cache}.version" +fi +source "$_mise_cache" +``` + +**Candidates for caching:** +- `mise activate zsh` - version manager activation +- `zoxide init zsh` - cd replacement +- `fzf --zsh` - fuzzy finder +- `fnm env` - node version manager +- `uv generate-shell-completion zsh` - Python tool completions +- `brew shellenv` - if still needed after manifest + +**Pros:** +- Eliminates subprocess spawning on most startups +- Can be combined with other strategies + +**Cons:** +- Cache can become stale if tool is updated +- Requires version checking logic +- Modest gains compared to fixing `find` issue + +## Reassessment: Additional Low-Hanging Fruit + +After Phase 1 completion, additional analysis revealed: + +### No Redundant Operations Found āœ… + +1. **No duplicate source operations** - All removed in Phase 1 +2. **No duplicate PATH exports** - Each tool adds unique paths +3. **Conditional loading working well** - 16 files use `if have` checks appropriately +4. **@archive exclusion working** - No archived files being sourced + +### Potential Quick Wins Remaining + +**None identified** in the "straight-forward" category like Phase 1. + +Remaining optimizations require structural changes: +- **Phase 2** (manifest generation) - 60-80% improvement potential +- **Phase 3** (eval caching, lazy loading) - 15-30% additional improvement + +### Current State + +After Phase 1: +- āœ… Eliminated 56 redundant file sources +- āœ… Verified exclusion patterns working +- āœ… **Massive improvement** in startup time reported +- Still sourcing 34 shell.zsh files via 6 `find` operations +- Still running 6+ `eval` commands on startup + +## Recommended Implementation Plan + +### Phase 1: Quick Wins (Immediate) āœ… COMPLETED + +1. **Fix redundant sourcing** (Strategy 4) āœ… + - ~~Move `source utils.zsh` to .zshrc:9~~ (already present) + - Removed redundant `source utils.zsh` from 56 shell.zsh files + - **Result:** Massive improvement reported by user + +2. **Fix @archive exclusion** āœ… + - Verified find exclusion patterns work correctly + - @archive directories are already properly excluded via `-prune` flag + - **No changes needed** - exclusion already working + +### Phase 2: Core Optimization (Primary fix for remaining slowness) āœ… COMPLETED + +3. **Implement file manifest** (Strategy 1) āœ… + - Created `features/update/zsh/generate-manifest.zsh` script + - Generates `.cache/shell-files-manifest.zsh` with all files to source + - Modified `tools.zsh` to use manifest with automatic staleness detection + - Added `regen` alias for manual regeneration (rarely needed) + - Staleness check uses single `find` for timestamps (not full traversal) + - **Estimated impact:** 60-80% improvement over Phase 1 state + +### Phase 3: Advanced Optimization (Optional) + +4. **Add lazy loading** (Strategy 2) for specific tools: + - Identify rarely-used tools (claude, gcloud, etc.) + - Implement lazy loading wrappers + - **Estimated impact:** Additional 10-20% improvement + +5. **Cache eval commands** (Strategy 5) for: + - `mise activate zsh` + - `zoxide init zsh` + - `fzf --zsh` + - `fnm env` + - `uv generate-shell-completion zsh` + - `brew shellenv` (if still needed after manifest) + - **Estimated impact:** Additional 5-10% improvement + +## Success Criteria + +- [x] Phase 1: Massive improvement from removing redundant sources +- [ ] Shell startup time < 200ms (if further optimization needed) +- [ ] No performance regression when adding new tools +- [ ] Solution is maintainable (clear when manifest needs regeneration) +- [ ] All existing functionality preserved + +## Measurement + +Before Phase 1: +```zsh +# User reported slow startup +``` + +After Phase 1: +```zsh +zt # User reported "massive improvement" +``` + +After Phase 2 (if implemented): +```zsh +zt # Should show additional 60-80% improvement +``` + +## Files Modified (Phase 1) + +- 56 `*/shell.zsh` and `*/shell/*.zsh` files - Removed `source utils.zsh` lines + +## Files to Modify (Phase 2) + +### Phase 2 (Core Optimization) - If Needed +- **New:** `features/update/zsh/generate-manifest.zsh` - Manifest generation script +- **New:** `${DOTFILES}/.cache/shell-files-manifest.zsh` - Generated manifest (gitignored) +- `tools/zsh/config/tools.zsh` - Replace `find` commands with manifest sourcing +- `features/update/zsh/homebrew.zsh` - Add manifest regeneration step +- `.gitignore` - Add `.cache/` directory + +### Phase 3 (Advanced) - If Needed +- Individual tool shell.zsh files - Add lazy loading where appropriate +- **New:** Cache files in `.cache/` for eval results + +## Non-Goals + +- Don't migrate to bash yet (that's a separate project per CLAUDE.md) +- Don't change the tool directory structure +- Don't remove functionality or aliases + +## Risks & Mitigations + +**Risk:** Manifest gets out of sync after adding new tools +**Mitigation:** +- Add staleness check that compares manifest timestamp to newest .zsh file +- Include regeneration in update script +- Clear documentation about when to regenerate + +**Risk:** Breaking changes to existing shell sessions +**Mitigation:** +- Test each phase independently +- Keep rollback plan (git revert) +- Verify all tools still work after changes + +**Risk:** Lazy loading breaks tools that depend on each other +**Mitigation:** +- Only lazy-load truly independent tools +- Load dependencies eagerly if needed +- Document which tools are lazy-loaded From 241eb186fcae9ce493cde586377e729e76e287d6 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Thu, 20 Nov 2025 14:05:28 -0500 Subject: [PATCH 11/11] claude: review prs once when first opened and not again on every push --- .github/workflows/claude-code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 205b0fe2..1e56f73e 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -2,7 +2,7 @@ name: Claude Code Review on: pull_request: - types: [opened, synchronize] + types: [opened] # Only review when PR is first opened (not on every push) # Optional: Only run on specific file changes # paths: # - "src/**/*.ts"