A CLI tool that organizes repositories by organization and name in ~/code/{domain}/{org}/{repo}. Jump to code faster; spend less time wondering where you cloned that thing three months ago.
No database, no daemon, no cloud sync that stops working when your wifi hiccups.
Good fit:
- You work across multiple GitHub orgs and/or a GitHub Enterprise instance
- You've typed
find ~ -name '.git' -type dmore than once this month - You want
cdbut for repos, with fuzzy matching and the memory of a goldfish (frecency) - You're comfortable with CLI tools and don't need a GUI to feel safe
Not a good fit:
- You have one repo. Congratulations on your focus
- You already have a system that works (don't fix what isn't broken, I'm not your mom)
- You want something battle-tested by thousands of users (this is a hobby project, there are dozens of us)
- Go 1.26+ (build from source)
- GitHub CLI (
gh) (authentication -- implementing OAuth device flow just to avoid typinggh auth loginseemed like the wrong kind of yak shaving) - Git (presumably you have this if you're here)
Download the binary for your platform from the releases page:
| Platform | Binary |
|---|---|
| macOS (Apple Silicon) | work-darwin-arm64 |
| macOS (Intel) | work-darwin-amd64 |
| Linux (x86_64) | work-linux-amd64 |
| Linux (ARM) | work-linux-arm64 |
# Example: macOS Apple Silicon
curl -Lo work https://github.com/anoldguy/work/releases/latest/download/work-darwin-arm64
chmod +x work
sudo mv work /usr/local/bin/workRequires Go 1.26+:
go install github.com/anoldguy/work/cmd/work@latestgit clone https://github.com/anoldguy/work.git
cd work
make build
# binary lands at bin/workWithout shell integration, work cd just prints a path and stares at you. You'd have to manually cd to it, which defeats approximately 90% of the purpose. Add to your shell config:
# bash (~/.bashrc)
eval "$(work activate bash)"
# zsh (~/.zshrc)
eval "$(work activate zsh)"
# fish (~/.config/fish/config.fish)
work activate fish | sourceRestart your shell or source the file. Run type work to verify; it should show a function, not just a path.
work completions bash --install
work completions zsh --install
work completions fish --installTab completion turns work cd my<TAB> into something useful. Without it you're just typing full repo names like an animal.
Three steps: authenticate, enable shell integration, navigate to repositories. That's it.
# First-time setup -- walks you through config and auth
work setup
# Jump to a repo (fuzzy match, auto-clones if needed)
work cd myrepo
# Open a repo in your editor
work edit myrepo
# Open a repo on GitHub in the browser
work browse myrepoRepository lists are cached locally and refresh automatically when stale. Hitting the GitHub API on every typo would be excessive, and typos happen more than anyone wants to admit. You never need to think about the cache -- it updates itself in the background the first time a command notices it's stale. If you're impatient, work update forces a refresh, but you probably won't need it.
Search ranks repositories by match quality and usage patterns. The repo you fat-finger every morning beats the exact match you haven't touched since Q2. This is called frecency scoring (frequency + recency) and it works better than pretending all matches are equally important.
Every time you interact with a repo (cd, browse, clone, edit, on, open, pull), it records the access. Recent activity counts more than old activity: last week gets full weight, last month gets half, last quarter gets 20%, and anything older than 90 days barely registers. The repo you mistype as widgts constantly still beats widgets-archive which you spelled perfectly but cloned once in 2023.
All matching is case-insensitive. Type FOO or foo or FoO; same result. Life's too short for shift keys.
For more specificity, qualify with org (acme/foo) or full slug (github.com/acme/foo). Each slash narrows the search. Multiple matches show an interactive picker unless you're in a script, where ambiguity errors instead of picking randomly and hoping.
| Command | What it does |
|---|---|
work cd <query> |
Fuzzy-match a repo and cd into it (clones first if needed) |
work on <query> |
Same as cd but opens a new terminal tab, for the tab hoarders |
work edit <query> |
Open a repo in $EDITOR (respects $WORK_EDITOR if you're picky) |
work open <query> |
Open a repo directory in your file manager (for the GUI-curious) |
work browse <query> |
Open a repo's GitHub page in the browser |
work clone <query> |
Clone a repo into the local hierarchy |
work list |
List all cached repos (supports --filter) |
work pull <query> |
Fetch + fast-forward a single repo |
work sync |
Fetch + fast-forward every cloned repo. Go get coffee |
work update |
Manually refresh the repo cache (usually unnecessary) |
work config |
Show current configuration |
work config --edit |
Open config in $EDITOR |
work config default |
Set the default domain |
work favorite |
Pick favorite orgs (only cache repos from orgs you care about) |
work setup |
Interactive first-time setup |
work auth login |
Authenticate with gh |
work auth status |
Check auth status for all domains |
work completions |
Generate shell completions |
work activate <shell> |
Output the shell integration script |
Configuration is optional. Defaults work for most people: ~/code for repositories, ~/.local/state/work for cache. XDG conventions are followed; somebody should.
Config lives at ~/.config/work/config.toml. The tool creates it on first run if needed. Edit it by hand if you prefer; it's just TOML.
work_dir = "~/code"
cache_path = "~/.local/state/work"
[filters]
exclude_orgs = ["that-org-from-2019-you-forgot-to-leave"]
include_orgs = ["my-team"]
[domains."github.com"]
is_default = true
favorite_orgs = ["my-team", "my-company"]
[domains."github.enterprise.com"]
favorite_orgs = ["team-a", "team-b"]
extra_orgs = ["partner-team", "shared-infra"]One domain can be marked as default to shorten filesystem paths. The default domain's repositories live at ~/code/org/repo. Other domains keep the full path: ~/code/domain/org/repo.
The first domain you configure gets marked as default automatically. Change the default anytime:
work config default # Interactive picker
work config default github.com # Set specific domainChanging the default doesn't move existing repositories. You'll need to re-clone or manually relocate them to match the new paths.
Cache updates sync all organizations you belong to, plus any extra orgs you've configured. If you just got added to 47 orgs at your new enterprise job and only care about 3 of them, this gets tedious.
Configure favorite organizations to sync only those:
work favorite # Interactive picker for default domain
work favorite github.enterprise.com # Pick favorites for specific domainOnce configured, only favorite orgs (plus personal repositories) get synced. Use work update --all to sync everything, ignoring favorites. Clear favorites by deselecting everything in the picker; you're back to syncing all organizations.
GitHub's API only returns organizations you're a member of. This is fine until you're working with a GitHub Enterprise instance where you have access to repos in an org through team or collaborator permissions without being a formal member. The repos exist, you can clone them, but the tool doesn't know they're there because the API never mentions them.
The extra_orgs setting fixes this. Add org names to a domain's config and they get included in every cache update alongside your member orgs:
[domains."github.enterprise.com"]
extra_orgs = ["partner-team", "shared-infra"]
favorite_orgs = ["my-team", "partner-team"]Extra orgs flow through the same filtering pipeline as everything else. Global include/exclude filters apply. Favorites narrow the list if configured. If you later become a formal member of an extra org, the tool deduplicates automatically; nothing breaks, nothing doubles.
Extra orgs also appear in the work favorite picker, so you can favorite them without editing the config file by hand.
The tool uses GitHub CLI. No tokens are stored directly. GitHub CLI already solved this problem; reimplementing it felt like trust issues with extra steps.
# Standard GitHub
gh auth login
# GitHub Enterprise
gh auth login --host github.enterprise.comRun gh auth login once; everything else works. Check work auth status if things seem broken.
work maintains a local JSON cache of repo metadata fetched from the GitHub API via gh. When you run work cd foo, it:
- Fuzzy-matches "foo" against all cached repos
- Ranks results by frecency (repos you use often float to the top)
- If there's one clear winner,
cds you there - If it's ambiguous and you're in an interactive terminal, shows a picker
- If the repo isn't cloned locally, clones it first
The cache auto-updates when stale (older than 24 hours). This happens transparently during normal use -- you don't need to manually refresh anything. The tool just quietly fetches new data in the background while a spinner pretends to keep you company.
The local directory structure mirrors GitHub's hierarchy:
~/code/
acme/ # default domain omits the prefix
api/
web/
github.enterprise.com/
corp-team/
internal-tool/
"No matching repository found"
Stale cache. Run work update to force a refresh. This happens when you join new organizations or when your coworker creates another microservice at 4pm on Friday.
Shell integration not working
The activation line didn't load. Run type work to verify; it should show a function, not a path. Check your shell config file has the activation line. Source it. Yes, I forget this step too.
Authentication failures
Check work auth status first. Usually the fix is gh auth login. Sometimes it's realizing you're on the VPN but pointing at the wrong GitHub instance.
Cloning fails
This tool shells out to git clone. If git can't clone it manually, neither can the tool. Your SSH keys, HTTPS credentials, or repository access might be the problem.