Workflow
Repository navigation: see every repo, jump to any repo, one keystroke.
When AI agents work in several repositories at once, your job is supervision — and supervision needs sight. groundwork-repos discovers every Git repository under your code folders, shows which ones hold uncommitted work, and opens any of them in its own lazygit tmux window.
groundwork-repos table of every repo's stategroundwork-repos pick the picker, from any shellgroundwork-repos changed open all dirty repos#Why: agents multiply repositories
Solo work used to mean one project at a time. Agent-assisted work does not: you review a change in one repository while an agent builds in a second and another proposal sits uncommitted in a third. Uncommitted work is invisible from anywhere except inside that repo — unless something walks all of them for you. That is this command's whole job: walk the repos, report honestly, and get you into the right one fast. The Git rule that the human owns the commit only works if the human can find everything that is not yet committed.
#Discovered, never configured
You never list repositories by name. You configure roots — the folders your projects live under — and every Git repository found beneath them is included automatically. Clone something new and it appears within the cache TTL (30 seconds), or immediately with groundwork-repos --refresh; delete a repo and it disappears the same way. The config lives at ~/.config/groundwork/repos.conf:
root = ~/code # repeat `root` for more locations
max_depth = 4 # how deep below a root a repo may sit
include_worktrees = true # linked git worktrees are listed, marked wt
cache_ttl_seconds = 30 # discovery cache (paths only; status is always live)
exclude = node_modules # names never descended into (.cache, dist, …)
That file is Groundwork-managed, so chezmoi apply keeps it current. Personal roots and excludes go in repos.local.conf next to it — unmanaged, never touched by an apply. One limitation of the deliberately simple format: # starts a comment anywhere on a line, so paths containing # are unsupported. Discovery inspects data: directory names plus read-only git queries. The precise safety guarantee: every git call the command makes disables repository hooks and the fsmonitor for the duration of the inspection (core.hooksPath, core.fsmonitor, and the untracked cache are all forced off per call), so an unfamiliar cloned repo's own configuration cannot run code just by being discovered or status-checked.
#The table: every repo's state at a glance
Plain groundwork-repos prints one line per repository — branch, dirty or clean, commits ahead/behind upstream, a wt marker for linked worktrees, and the path. State is always a symbol plus a word (● dirty, ✓ clean), never color alone, and the command respects NO_COLOR:
$ groundwork-repos
REPO BRANCH STATE SYNC WT PATH
my-app main ● dirty ahead 2, behind 0 - ~/code/my-app
my-app-fix fix/api ✓ clean ahead 0, behind 1 wt ~/code/my-app-fix
notes main ✓ clean no upstream - ~/code/notes
Worktrees matter here: agents are often pointed at a linked worktree rather than the main checkout, and a worktree's uncommitted changes are exactly the kind of work that goes unseen. Repos that share a folder name are shown with the shortest path suffix that tells them apart (app → client/app → code/client/app, extending as far as needed), so two rows — or two tmux windows — can never carry the same name.
#The picker: fuzzy-find, preview, jump
prefix R in tmux (or groundwork-repos pick in any shell) opens a fuzzy search over every discovered repo with a live git status preview, so you can see what is uncommitted before you commit your attention. Press Enter and that repo's lazygit opens in its own tmux window — or, if one is already open, you jump to the existing window instead of stacking a duplicate. Outside tmux, the same command creates or attaches a repos session, so it works from a bare terminal too. The picker needs fzf, which Groundwork installs; without it the command says so and prints the list instead.
#Everything the agents touched: prefix G
The supervision move: groundwork-repos changed opens one lazygit window per repository with uncommitted work. End of a working session, press prefix G, and every repo an agent (or you) left dirty is now a named window — review each diff, commit what earns it, discard what does not. When everything is clean it says so and opens nothing.
prefix g (lowercase) still opens lazygit for the repo you are standing in. R and G are the wide-angle versions: any repo, or all dirty repos.
#Optional: a per-repo workspace file
A repository can describe its preferred window arrangement in .groundwork/workspace.yml — additive metadata only, never required: discovery works identically whether or not the file exists.
version: 1
name: my-app
windows:
editor: nvim .
git: lazygit
dev: pnpm dev # optional
Today the picker documents this format but does not consume it — opening the listed windows automatically is planned, and until then the file is a readable statement of intent (and fair game for your own scripts). This mirrors the sesh session recipes in ~/.config/sesh/sesh.toml: arrangements are something a human declares; the repo list is always discovered.
#Built to be inspectable
- Everything is keyboard-driven, and every keystroke has a plain-command equivalent you can copy from this page or the catalog.
- State is words + symbols; color is an optional extra (
NO_COLORhonored). No animation anywhere. - Window names are readable repo names, so the tmux status line stays a legible map of your open work.