// ZSH-GIT-REPO-CACHE — ENGINEERING REPORT

zsh plugin · indexes every git repo on disk · fd-or-find crawl · 3 cache files (all / dirty / clean) · fzf interactive selection · 10 ZPWR verbs

>_EXECUTIVE SUMMARY

zsh-git-repo-cache is the indexing layer for "every git repo on the machine". The plugin crawls the root filesystem once with fd (or falls back to find), strips trailing /.git, removes macOS /System/Volumes read-only mirror prefixes, and writes the deduped, sorted list to $ZPWR_ALL_GIT_DIRS. A second pass walks each path with git diff-index --quiet HEAD + git ls-files --exclude-standard --others and bisects the master list into a dirty and clean cache. All queries — live and cache-backed, list and fzf-interactive — are 6 thin shims over those three files. The plugin is the upstream feeder for cd-like fzf workflows in zpwr; its job is to make the master list cheap enough that the prompt can read it on every command.

287
Plugin LOC
8
Autoload Fns
3
Cache Files
10
ZPWR Verbs
29
zunit @test Cases
6
Test Files
21
Docs/Repo Gates
42
Git Commits

Plugin Source — 287 LOC

232 autoload / 55 plugin entrypoint · 80.8% autoload

Entrypoint (zsh-git-repo-cache.plugin.zsh, 55 LOC) only sets default env vars, defines a fallback zpwrExists + zpwrPrettyPrint, appends autoload/ to fpath, autoloads every fn, and registers 10 ZPWR_VERBS when running under zpwr. All real work lives in the 8 autoload functions (232 LOC) lazy-loaded by zsh's autoload -Uz.


#FILE STRUCTURE

Every autoload file follows the canonical zsh-plugin pattern: # -*- mode: sh -*- + function name(){...} + a trailing self-call so the file is also source-able as a script. Stems match function names, so fpath autoload + direct source both resolve identically.

FileLinesRole
zsh-git-repo-cache.plugin.zsh55Plugin entrypoint: default env, fallback zpwrExists/zpwrPrettyPrint, fpath append, autoload -Uz, ZPWR_VERBS registration
autoload/zsh-git-repo-searchDirtyGitRepos58Live walk: git diff-index --quiet HEAD + git ls-files --exclude-standard --others per path; emits dirty paths; fzf or list mode
autoload/zsh-git-repo-searchCleanGitRepos55Live walk: complement of the above; emits paths with no staged diff and no untracked files
autoload/zsh-git-repo-regenAllGitReposDirty35Bisect master cache into dirty + clean caches in a single pass via exec 3> + exec 4>
autoload/zsh-git-repo-searchAllGitRepos34Cache-backed query: regenerate if missing/empty, then list or fzf-pick
autoload/zsh-git-repo-searchCleanGitReposCache34Cache-backed clean query: regenerates dirty/clean caches if either is missing/empty
autoload/zsh-git-repo-searchDirtyGitReposCache34Cache-backed dirty query: same regen-on-empty contract as the clean variant
autoload/zsh-git-repo-regenAllGitRepos25Scorched-earth rescan: fd '\.git$' / (or find / -name .git -type d -prune), strip trailing /.git, strip macOS /System/Volumes mirror prefixes, sort + uniq
autoload/zsh-git-repo-searchGitCommon12Shared tail: capture zsh-git-repo-goThere output, echo it for transparency, eval to actually cd
9 source files2871 entrypoint + 8 autoload fns

$DATA MODEL

Three plain-text caches under $HOME, one path per line, sorted + deduped. No SQLite, no LMDB, no on-disk format version — the file format is "newline-separated absolute paths." Reading is cat + fzf; writing is sort | uniq. The format choice is load-bearing for the prompt-read use case: read -t0 against a flat file is the fastest possible "is this repo dirty?" answer.

Env varDefault pathProducerPurpose
ZPWR_ALL_GIT_DIRS~/.zsh-git-repo-cacheregenAllGitReposMaster list of every git repo on disk
ZPWR_ALL_GIT_DIRS_DIRTY~/.zsh-git-repo-cache-dirtyregenAllGitReposDirtyRepos with uncommitted changes or untracked files
ZPWR_ALL_GIT_DIRS_CLEAN~/.zsh-git-repo-cache-cleanregenAllGitReposDirtyRepos with no staged diff and no untracked files
ZPWR_TEMPFILE3$TMPDIR/.zsh-git-repo-tempregenAllGitReposScratch buffer for inline perl -i -pe edits before sort+uniq
ZPWR_FZFfzfplugin entrypointFzf invocation name (override for custom wrappers)

@SCAN PIPELINE

A single regenAllGitRepos invocation produces a fully normalized master list. Two-tool dispatch (fd when available, find as fallback) means the plugin works on a fresh box and gets the 8-thread fd speedup on developer machines. Two perl -i -pe passes strip trailing /.git and (on macOS) the read-only Apple-mounted mirror prefixes that would otherwise produce a 2x-duplicated list.

  /                                                  ZPWR_ALL_GIT_DIRS
   │                                                       ▲
   │ sudo fd '\.git$' / --type d --threads=8                │ sort | uniq
   │   --hidden --no-ignore        (or)  sudo find /        │
   ▼                              -name .git -type d -prune │
  ┌──────────────────────┐                                  │
  │ raw .git path stream │                                  │
  └─────────┬────────────┘                                  │
            │ perl -ne 'print "$1\n" if m{(/.*.git)/*$}'    │
            ▼                                                │
  ┌──────────────────────┐                                  │
  │ ZPWR_TEMPFILE3       │                                  │
  └─────────┬────────────┘                                  │
            │ perl -i -pe 's@/.git$@@'                       │
            │ perl -i -pe 's@^/System/Volumes/Data@@'       │ (macOS)
            │ perl -i -pe 's@^/System/Volumes/Update/mnt1@@'│ (macOS)
            ▼                                                │
  ┌──────────────────────┐                                  │
  │ normalized repo dirs │──────────────────────────────────┘
  └──────────────────────┘
            │
            │ regenAllGitReposDirty: walk + git diff-index --quiet HEAD
            │                       + git ls-files --exclude-standard --others
            ▼
  ┌──────────────────────┐    ┌──────────────────────┐
  │ ZPWR_ALL_GIT_DIRS_   │    │ ZPWR_ALL_GIT_DIRS_   │
  │ DIRTY (exec 3>)      │    │ CLEAN (exec 4>)      │
  └──────────────────────┘    └──────────────────────┘

The dirty/clean bisection writes to both files concurrently via two long-lived file descriptors (exec 3>, exec 4>) so a single while read loop over the master list produces both caches in one pass. Branch order matters: git diff-index --quiet HEAD first (cheapest, no I/O when clean), then git ls-files --exclude-standard --others only on the clean side to catch repos that have untracked files but no staged delta. Worktree is saved/restored via origPwd="$PWD" + builtin cd -q "$origPwd" at the end — the function is reentrant and won't poison the caller's CWD even on error.


&QUERY SURFACE

Two axes of query: live (walk master list, ask git fresh) vs cached (read pre-computed dirty/clean files), and list (stdout) vs fzf (interactive). Six query functions + 1 shared tail = full matrix. The cached path is the prompt-friendly one (no fork-per-repo); the live path is the up-to-the-second one (no stale-cache risk).

FunctionModeSourceOutput
searchAllGitReposcache$ZPWR_ALL_GIT_DIRSall repos (list or fzf)
searchDirtyGitReposlivewalk master + git probedirty repos right now
searchCleanGitReposlivewalk master + git probeclean repos right now
searchDirtyGitReposCachecache$ZPWR_ALL_GIT_DIRS_DIRTYdirty repos as of last bisect
searchCleanGitReposCachecache$ZPWR_ALL_GIT_DIRS_CLEANclean repos as of last bisect
searchGitCommontailshared exec for the 5 fns aboveechoes the cd command, then evals
regenAllGitReposrebuildfd or findmaster cache
regenAllGitReposDirtyrebuildwalk master + git probedirty + clean caches in one pass

Each query fn defines two inner closures — zsh-git-repo-goThere (pipe through fzf, materialize a cd "..." command) and zsh-git-repo-listThere (raw stdout). The dispatch is one if [[ $cmd == list ]] guard; the regen guard (shouldRegen == regen OR cache missing OR cache empty) is identical across all 5 search variants so adding a new cache type is a copy-paste-rename, not a redesign.


~ZPWR VERB BINDINGS

When the plugin loads inside zpwr ((( ${+ZPWR_VERBS} )) guard), it registers 10 verbs into the global ZPWR_VERBS hash: 5 list-mode + 5 fzf-mode, full cross product of (all / clean / dirty / clean-cached / dirty-cached). Each value is the function name to run plus a free-text help line zpwr surfaces in its help screen.

gitreposlist

zsh-git-repo-searchAllGitRepos n list — emit all repos to stdout

gitreposcleanlist

zsh-git-repo-searchCleanGitRepos n list — live-walk clean repos to stdout

gitreposdirtylist

zsh-git-repo-searchDirtyGitRepos n list — live-walk dirty repos to stdout

gitreposcleancachelist

zsh-git-repo-searchCleanGitReposCache n list — clean cache to stdout

gitreposdirtycachelist

zsh-git-repo-searchDirtyGitReposCache n list — dirty cache to stdout

gitrepos

zsh-git-repo-searchAllGitRepos — fzf-pick + cd

gitreposclean

zsh-git-repo-searchCleanGitRepos — fzf-pick live clean repos

gitreposdirty

zsh-git-repo-searchDirtyGitRepos — fzf-pick live dirty repos

gitreposcleancache

zsh-git-repo-searchCleanGitReposCache — fzf-pick cached clean repos

gitreposdirtycache

zsh-git-repo-searchDirtyGitReposCache — fzf-pick cached dirty repos


*TEST COVERAGE

29 zunit @test cases across 6 test files pin behavioural invariants: plugin-contract (entrypoint name, zsh -n clean parse, #compdef directive on completions), function-level contract (autoload presence, ZPWR_VERBS values, defensive fallback definitions), and surface syntax (every autoload file parses, every fn name matches its file stem). 21 additional .sh doc-hygiene gates pin README/docs/workflow structure — final newlines, body+html tags, h1 presence, https-only links, target=_blank rel=noopener pairing, no inline handlers, no placeholder hrefs, no deprecated tags.

Test file@testPins
tests/t-fns.zsh8Every autoload fn is defined after sourcing the plugin; fn name matches file stem; autoload/ on fpath; fallback zpwrExists + zpwrPrettyPrint respect existing definitions
tests/t-contract2.zsh6ZPWR_VERBS contains all 10 expected keys when sourced under a stubbed zpwr; verb values reference real autoload fns
tests/t-contract3.zsh5Env var defaults: ZPWR_ALL_GIT_DIRS, _DIRTY, _CLEAN, ZPWR_TEMPFILE3, ZPWR_FZF all set when unset
tests/t-contract4.zsh5Existing env values are not clobbered when plugin is re-sourced; idempotent re-source contract
tests/t-contract.zsh3Entrypoint stem matches plugin directory; entrypoint parses under zsh -n; every _* completion file starts with #compdef
tests/t-syntax.zsh2Every autoload/* file parses under zsh -n; no syntax drift on push
6 zunit files29+ 21 .sh doc-hygiene gates

// DOC GATES

GatePins
docs-final-newlineEvery docs/*.html ends with \n; git diff stays one-line clean
docs-has-body-tagEvery docs/*.html contains a <body> element
docs-has-h1Every docs/*.html contains an <h1>
docs-has-html-closingEvery docs/*.html closes </html>
docs-no-deprecated-tagsNo <font>, <center>, <marquee>, <blink>, etc.
docs-no-http-linksNo bare http:// — https only
docs-no-inline-handlersNo inline DOM event-handler attributes (onclick, onchange, ...) — Tauri CSP guard
docs-no-placeholder-hrefNo placeholder anchor hrefs (literal hash or javascript: scheme); real anchors only
docs-target-blank-rel-noopenerEvery target="_blank" pairs with rel="noopener"
readme-final-newline / has-badges / has-h2-section / has-https-linkREADME structural pins
man-page-final-newline / no-trailing-whitespace / synopsis-sectionMan-page hygiene (passes vacuously if no man pages)
tests-shell-executable / tests-shell-shebangEvery tests/*.sh is +x and has a shebang
workflow-final-newline / workflow-no-tabsGitHub Actions YAML hygiene
cargo-final-newlineVacuous (no Cargo.toml); kept for cross-repo template parity

?KEY DESIGN DECISIONS

Each call-out is a decision the implementation could have gone either way on, with the rationale for the path taken.

Flat-file caches, not SQLite

One repo per line, sorted+uniq. fzf wants newline-separated input directly; cat $CACHE | fzf is the entire query path. SQLite would force a SELECT ... INTO bridge for every read and add a build-time dep on sqlite3. Flat text is the fastest format for the actual access pattern.

fd preferred, find fallback

zpwrExists fd probe at every regen. fd with --threads=8 + --hidden --no-ignore walks a developer-laptop tree in seconds; find -name .git -type d -prune works everywhere zsh runs. The plugin doesn't break on a fresh server, but speeds up wherever fd is available.

One-pass dirty/clean bisect

regenAllGitReposDirty opens exec 3> on the dirty cache and exec 4> on the clean cache, walks the master list once, and writes each path to exactly one fd. No second pass, no grep -vf diff. The cleanup pair (exec 3>&-, exec 4>&-) closes both fds even on early return.

macOS Volumes mirror strip

APFS mounts the user data root at /System/Volumes/Data/ and the update mirror at /System/Volumes/Update/mnt1/. find / emits paths under both prefixes for the same physical repo. Two perl -i -pe passes strip those prefixes so sort | uniq collapses the dupes. Without this the cache double-counts every repo.

Sudo for the crawl

The walk uses sudo -E "PATH=$PATH" env fd ... (or sudo find /) because the root filesystem contains repos owned by system users and read-restricted dirs that an unprivileged walk would miss or warn on. -E preserves the user's env (and PATH=$PATH ensures fd resolves) instead of switching to root's PATH.

Inner closures, not exported helpers

Each search fn defines zsh-git-repo-goThere + zsh-git-repo-listThere as inner fns visible only inside its body. The shared tail (searchGitCommon) then resolves whichever closure was just defined. Avoids a global namespace collision when two search fns are nested in one shell session.

Echo-then-eval for transparency

searchGitCommon captures zsh-git-repo-goThere output (a literal cd "..." string), echoes it to the terminal, then evals it. The user sees the cd command in scrollback — useful for re-running, sharing, or just understanding what the fzf pick resolved to.

Defensive zpwr stubs

If zpwrExists or zpwrPrettyPrint aren't defined (plugin loaded outside zpwr), the entrypoint defines minimal fallbacks. The ZPWR_VERBS registration is guarded by (( ${+ZPWR_VERBS} )) so a standalone install gets all 8 functions usable directly without zpwr.


!STRATEGIC POSITION

The plugin is the upstream feeder for any "jump to a git repo on this machine" workflow. zpwr's gitrepos* verbs are the public face; the same files back ad-hoc prompts, dirty-tree indicators, and CI-side audits ("which repos on this host have uncommitted work").

Prompt feeder

The dirty cache is small (one line per dirty repo) and cheap to grep -F "$PWD" on every prompt redraw — faster than calling git status in a possibly-large repo.

fzf cd

gitrepos + fzf is the canonical "jump to any of my repos" workflow. Master cache means the user can cd to a 5-deep nested repo by typing two letters of its basename.

CI audit

On a build host, gitreposdirtylist answers "did anything leak uncommitted changes on this box". The list mode is grep-friendly — the canonical use is gitreposdirtylist | wc -l as a health metric.

Multi-repo dispatch

The master cache is the input list for "run X across every repo on this machine" loops — the same primitive that backs parallel-style multi-repo pulls.


+LOAD MODEL

The plugin is autoload-clean: sourcing the entrypoint costs only env-var defaults + fpath append + 10 hash assignments. The 8 autoload functions don't run until called, and each runs the file body exactly once (the trailing self-call inside each file is also the autoload-time body). Zero git invocations, zero fd/find invocations, zero stat calls at source time.

StepCostSide effect
Source zsh-git-repo-cache.plugin.zsh~milliseconds5 env-var defaults (only if unset), 2 fallback fn defs (only if missing), fpath+=, autoload -Uz register, ZPWR_VERBS hash assignment
First call to any search*cache hit: cheap; cache miss: fd/find walkCache files created/refreshed under $HOME
First call to regenAllGitReposfd: ~5s on a developer laptop; find: ~30sMaster cache rewritten
First call to regenAllGitReposDirty2 git calls per repoDirty + clean caches rewritten in one pass
Subsequent cached queriescat + fzf onlyNone (read-only)

.GIT POSTURE

42 commits on main. The repo carries no submodules, no generated artifacts, and no vendored deps — every byte is hand-written zsh + the docs cyberpunk template (hud-static.css + tutorial.css) shared across the MenkeTechnologies plugin stack. CI runs the zunit + doc-gate suite on every push (.github/workflows/ci.yml).

Compat floor: works on every zsh 5.x. Hard deps: zsh, perl (system perl, for the trailing-slash + Volumes-mirror strip passes), git. Soft deps: fd (accelerated crawl), fzf (interactive pick), sudo (full-disk crawl). No deps on zpwr — the plugin works standalone, ZPWR_VERBS is opt-in.