Groundwork

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.

When you'll want this One agent is editing in one repo, another left changes in a second, and you genuinely cannot remember whether a third has anything uncommitted.
Quick reference — prefix is Ctrl+a
groundwork-repos table of every repo's state
prefix R repo picker popup
prefix G lazygit for every dirty repo
groundwork-repos pick the picker, from any shell
groundwork-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:

~/.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:

sample output — your list will be your own repos
$ 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 (appclient/appcode/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.

This repo vs. every repo

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.

.groundwork/workspace.yml (planned)
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_COLOR honored). No animation anywhere.
  • Window names are readable repo names, so the tmux status line stays a legible map of your open work.