Getting Started

How It Works

The resolve → materialize → act pipeline that every uxd workspace command runs, and what each step actually does.

Every workspace command in uxd is the same pipeline:

resolve(ref) → materialize(workspace) → act(command)

checkout is the pipeline with a no-op act step. code, run, exec, shell, sync, and diff are thin wrappers that swap the act step. Once you know these three stages, you can predict what any verb will do.

Resolve

uxd takes the ref you typed and turns it into a concrete git target: a pull request, a branch, a commit, or an existing working tree to adopt. Resolution is ordered and first-match-wins — see Refs for the full table and the flags that force an interpretation.

Resolution produces a slug, the stable directory name for the workspace:

Ref kindSlug
Pull requestpr-<n>
Branchbranch name, sanitized (feat/loginfeat-login)
Commitsha-<first 10 chars>
Adopted pathadopted-<dirname>-<hash>

When two branch names sanitize to the same slug, the loser gets a short content hash appended, so distinct refs never share a directory.

Materialize

Materializing is idempotent. Running it on a workspace that already exists is cheap and skips the network entirely — the fast path just reports worktree ready (reused).

One bare clone per project

The first materialization creates a single bare, partial clone at repo_path. Every workspace for that project is a git worktree off that one clone, so the second ref you check out costs a fetch rather than a full clone.

→ cloning git@github.com:my-org/my-project.git (bare, partial)

One worktree per ref

uxd fetches the ref and adds a worktree under worktrees_path/<slug>:

→ fetching origin/feat/login
→ worktree ready: ~/dev/uxd/my-project/trees/feat-login

Ports get allocated

Each workspace is assigned a contiguous block of ports, sized by the ports config key. Allocation is deterministic first: the candidate is derived by hashing the slug into a 100-stride window above base_port, then scanning upward only if the candidate is already claimed or occupied. The same workspace therefore tends to get the same port every time, which keeps bookmarks and OAuth callback URLs working.

Seed files get copied

Files listed in setup.seed_files are copied in from <config-dir>/seeds/<project>/ (or setup.seed_from). Existing files are never overwritten unless you pass --reseed. This is how .env.local gets into a fresh workspace without you remembering to copy it.

Setup runs, cached

If [setup].run is configured, uxd hashes the files matched by setup.cache_key and compares that hash to the one recorded for this workspace. Unchanged means skipped:

→ setup up to date (cache hit)

Changed — or never run — means the install command runs and the new hash is recorded on success. A failed install leaves the old hash in place, so the next attempt retries rather than silently assuming success.

checkout does not run [setup] by default — it is the scriptable primitive and stays fast. Pass --setup if you want it. run, exec, and shell always run setup unless you pass --no-setup.

Act

The act step is the verb you asked for:

VerbAct step
checkoutnone — print the worktree path
codelaunch the configured editor
run <name>execute a [commands.<name>] entry through a shell
exec -- <argv…>spawn an argv directly, no shell
shellstart an interactive shell
syncre-fetch and hard-reset the worktree to its ref
diffrender gh pr diff, or git diff <merge-base>...HEAD

Every child process gets the full uxd environment exported — UXD_PATH, UXD_PORT, UXD_BRANCH, and the rest — plus whatever you declared in [env]. See Environment and templates.

Where uxd keeps things

LocationContentsDefault
Config dirProject TOML files and seed trees~/.uxd
repo_pathThe bare, partial clone{root}/{project}/repo
worktrees_pathOne directory per workspace{root}/{project}/trees
Data dirPer-workspace scratch space ($UXD_DATA_DIR){worktrees_path}/.data/<slug>
State dirPer-project JSON: workspaces, ports, PR cache, last ref${XDG_STATE_HOME:-~/.local/state}/uxd

State is the bookkeeping — it records which slugs exist, which ports they hold, and which ref you used last (that is what - resolves to). It is derived data: if it drifts from reality, uxd doctor flags it and uxd <project> clean --prune-state reconciles it.

Next

  • Workspaces and worktrees — the consequences of the worktree model, and the sharp edges worth knowing.
  • Refs — every ref form, in resolution order.