One folder per task. Every repository the task needs, on the branch it needs, and a note of where you left off.
dev organizes local development around tasks instead of clones. A feature, a PR review and an incident can be open at the same time, each in its own workspace, and none of them touches the others. It also puts your pull requests and work items in the terminal, searches documentation across every repository you keep, and gives a coding agent a folder that already knows what the task is. Works with GitHub and Azure DevOps.
Under three minutes from dev init: a workspace from one URL and a jump into it with dev go, an incident workspace built from a workset, and a coding agent answering from the indexed docs. Each workflow's Demo run keeps the full-quality MP4 as a workflow artifact.
Most days touch more than one repository and more than one task. The feature you are building spans an app and its API. Someone asks for a review on that same API. Then the checkout service pages you, and you need it next to the infrastructure repo and the runbooks.
Each of those wants the same repositories on different branches. One clone per repository means stash, switch, and lose your place. One clone per task means a disk full of folders whose purpose you forget by Thursday.
dev gives each task a workspace: a folder with a Git worktree for every repository the task needs and a ws.md that records what the task is, what is mounted, and where you stopped. Repositories are cloned once and shared, so a fifth workspace of the same repository costs a worktree, not a download.
dev is a single binary for macOS, Linux and Windows. Install it with Mise:
mise use -g github:gabrielmoreira/dev-cliDirect installers are also available.
macOS and Linux:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/gabrielmoreira/dev-cli/releases/latest/download/dev-installer.sh | shWindows PowerShell:
irm https://github.com/gabrielmoreira/dev-cli/releases/latest/download/dev-installer.ps1 | iexThe installers verify the downloaded binary against the published SHA-256 checksums. See the setup guide for custom and manual installation.
After installing dev with any method, run the interactive setup:
dev initdev init asks where to keep your work (~/dev by default) and whether to connect a GitHub owner or an Azure DevOps organization. A provider is optional. With one, dev knows your repositories, so the pickers, dev pr and dev wi have something to show. Without one, you give it URLs. It reuses your gh and az sessions when you have them (details).
Let dev go change your shell's directory:
eval "$(dev shell-init zsh)" # bash, zsh, fish and PowerShellInteractive pickers use fuzzy search. Without the integration, dev go prints the path instead of jumping to it.
Each prompt explains what your answer changes. Enter keeps a shown proposal; confirmations say what Yes and No do.
Results name what changed and the command you can run next. An empty list still succeeds and explains whether you need setup, a first task, or a different filter.
If a name is wrong, you see a close match or known names and a command you can use there. Missing setup points to creation, not to a sync that needs a connection.
Paste a repository URL. dev asks for a name and a one-line objective; accept the defaults or type your own:
dev ws init https://github.com/can1357/oh-my-pi
dev gogh-can1357-oh-my-pi/
├── oh-my-pi/ # Git worktree on the default branch
├── ws.md # what this is, what it holds, where you stopped
└── .local/ # scratch that never gets committed
ws.md is what makes a workspace resumable. dev owns the frontmatter and reads it to know what to mount and update. The body is for you, and for any coding agent that opens the folder (abridged):
---
name: gh-can1357-oh-my-pi
description: Contribute to Oh My Pi
mounts:
- path: oh-my-pi
source: https://github.com/can1357/oh-my-pi
revision:
mode: track
branch: main
---
# Workspace: gh-can1357-oh-my-pi
## Objective
Contribute to Oh My Pi
## Current Progress
## Decisions
## Next StepsCome back in a week, or hand the folder to an agent, and the brief is already there.
dev pr lists the open pull requests you wrote or are asked to review, live, across your connected providers. In a terminal, type to find one and choose what to do with it: check it out, open it in the browser, or read its details. Pick one repository with -i, or narrow to a group of repositories with a label. Azure DevOps work items come along with dev wi, which reads a local cache until --refresh pulls the latest.
dev pr
dev pr -i
dev pr --label team:checkout
dev wiMost reviews end there. When you do need the code, the PR URL becomes a workspace on the PR's source branch, forks included, while your own work on that repository stays where it is. The source branch must still exist, even for a merged PR. If the example's branch has been deleted, choose a current PR with dev pr -i above instead:
dev ws init https://github.com/cli/go-gh/pull/309Start empty and add what the task needs. With a provider connected, dev ws add opens a picker over your repositories. Each mount tracks a branch, or is pinned to a tag or a commit:
dev ws init checkout-incident --desc "Checkout times out two or three times a day"
dev go checkout-incident
dev ws add # pick from your repositories
dev ws add checkout-api --tag v2026.09.1 # by name or URL; pin exactly what production runs
dev ws add infra --commit 3f9c2ab
dev ws add runbooks --readonly # reference only, skipped by syncRunning the same workspace initialization again succeeds without changing its manifest. An explicit --desc must match the existing objective; a different objective is a conflict, not permission to overwrite it.
checkout-incident/
├── checkout-api/ tag v2026.09.1
├── payments-gateway/ branch main
├── infra/ commit 3f9c2ab
├── runbooks/ branch main, read-only
└── ws.md
Tomorrow, dev status compares what ws.md declares with what is on disk and reports each mount as clean, dirty, ahead, behind, diverged or missing. dev sync fetches the remotes and brings the workspace back in line: it checks out a mount that is missing, puts a clean mount that drifted to another branch back on the declared one, and fast-forwards the clean ones. A mount with uncommitted changes, local commits or a diverged history is skipped and named, not touched. Add --offline to compare with the last fetch instead.
dev status
dev syncTo catch up on everything at once, dev sync --all refreshes the provider caches, the mirrors, and every workspace. It only reads from remotes: it pushes nothing and runs no hook.
Switch between tasks with dev ls and dev go, or dev go checkout when you know part of the name.
If a workspace's ws.md is invalid, dev ls marks only that workspace as invalid. With --json, its entry includes error.code and error.message instead of a mount count.
If every checkout incident starts with the same four repositories, save the setup as a workset and create a fresh workspace from it each time:
dev workset manage # name, repositories, refs, paths, why, and a setup command
dev ws init incident-0919 --workset checkout-incidentA workset is a template. A workspace is an instance of one, with its own worktrees.
A workset can carry a setup command, such as mise install, and each repository can override it or opt out. dev ws init runs it in each new checkout once every repository is in place, never when you open the workspace later. A repository outside your trusted scopes runs it only with --consent. A failed or skipped command does not stop the others or undo the workspace; dev ws setup runs them again.
Architecture in one repository, runbooks in another, the handbook in a Git-backed wiki. Keep each one as a reference checkout, label the ones that belong to the same knowledge base, and let QMD index them:
dev label add index:platform-docs # pick the repositories; an index:* label keeps them mirrored
dev qmd sync # every index:* label, one collection per repository
dev qmd x query "how does production authentication work?"The answer can live in any of them; you search the set. Your coding agent can search the same index, through dev qmd x or through QMD's own CLI and MCP server after one setting (how).
A label names a set of repositories, each on its default branch or one you choose, and one label serves more than one command:
dev label # see every label, then add, edit, rename, or remove one
dev pr --label team:checkout # pull requests from the checkout repositories
dev ws init --label team:checkout # a workspace with every checkout repository
dev qmd sync index:platform-docsA workspace is already a brief: ws.md holds the objective, the decisions so far and the next steps, and the repositories the task needs are one folder down. Point an agent at the folder, or let dev start one there:
dev start # dev ws start: opens OMP in a HerdR pane for this workspace, and finds it again next timeThe workspace, pull request and work item commands take --json, and dev --help --llms prints the command contract in a form written for models, so an agent can drive dev itself. OMP and HerdR are optional; see Integrations.
| Concept | What it is for | How it differs |
|---|---|---|
| root | The folder where dev keeps your workspaces, mirrors and settings, ~/dev by default. |
Most people need one; add another to keep one client's work apart. |
| provider | A connection to GitHub or Azure DevOps, so dev knows your repositories and pull requests. | Without one, you work from repository URLs. |
| repository | A Git repository on GitHub, Azure DevOps or any URL. | dev never changes where it lives; it copies it into a workspace or a mirror. |
| workspace | A folder for one task: notes in ws.md, plus the repositories the task needs. |
A workset is a recipe; a workspace is what you work in. |
| mount | One repository inside a workspace, on its own branch. You edit and commit here. | A mirror is for reading; a mount is for changing. |
| workset | A saved recipe for a workspace: which repositories, on which branches, and why. | Start a workspace from it with dev ws init --workset <name>. |
| label | A name for a group of repositories, like team:payments. |
A label groups repositories; a workset says what one task needs from them. index: labels also keep their repositories mirrored and searchable. |
| mirror | A reference copy of a repository that dev keeps up to date, for reading, search and agents. | Do task work in a workspace mount, not in a mirror. |
~/dev/
├── dev.yaml # providers, sources, labels, worksets, plugins
├── AGENTS.md # instructions for agents working inside this root
├── ws/ # one folder per task
└── mirrors/ # reference checkouts
Under the hood every repository is cloned once, as a bare mirror in .dev/, and every mount and reference checkout is a worktree on it. If you already use git worktree, that is the mechanism. dev adds the folder per task, the manifest, the pinning, the update that skips your dirty work, and the pickers.
- Setup: what
dev initcreates, credentials, several roots, shell integration for each shell. - Integrations: OMP, HerdR and QMD in detail.
- Command reference, generated from the CLI.
dev <command> --helpworks everywhere. - Contributing: building, testing, and regenerating the demo.
