Appearance
Git and review
dqx works with the repositories you already have. It never commits, never pushes and never changes a branch you are on without being asked. What it adds: it fetches the code a project needs, shows which branch every checkout is on, installs the git hooks your team agreed on, gives you copies of a project to try a branch in, and reviews pull requests with Claude before you post anything.
This page is the map. Each part links to the page that goes into detail.
Repositories in a project
A project is either single, where the application repository is the project, or multi, a wrapper holding a checkout per site. A multi project can span several repositories: a site each, and the wrapper with the Docker setup. See single and multi layout.
dqx install clones the repositories the project's .dq/config.yml names, on the branch each site says (main when it says none). A repository marked optional that you cannot read is skipped rather than stopping the install. See creating projects.
Bitbucket's SSH host: ssh.bitbucket.org
Bitbucket Cloud moves Git over SSH from bitbucket.org to ssh.bitbucket.org. From 12 November 2026 SSH on bitbucket.org is blocked in brownouts, a few hours a day, and on 10 December 2026 it stops for good. HTTPS (https://bitbucket.org/...) and the API are not affected, and the host keys stay the same.
bash
dqx bitbucket-ssh # what would change, on this machine and in every known project
dqx bitbucket-ssh --apply # make the changes
dqx bitbucket-ssh --apply --files # also composer.json, composer.lock, package.json, .gitmodules--apply sets this machine up first:
- git: a global
insteadOfrule that sendsgit@bitbucket.org:andssh://git@bitbucket.org/to the new host, for lockfiles and dependencies that still name the old one. ~/.ssh/config: aHost bitbucket.orgblock getsssh.bitbucket.orgbeside it, so its key is used there too. The old file is kept asconfig.dqx-<date>.bak.~/.ssh/known_hosts: the new host's keys, each checked against the fingerprints Bitbucket published. A key that does not match writes nothing.
Then the remotes of every repository in every project, and the repository: URLs in .dq/config.yml. dqx sync does the same for one project, but only once ssh knows the new host: before that a moved remote would fail every fetch with "Host key verification failed". dqx doctor says what is still on the old host. The project files that --files changes are the project's own: commit them there. Inside the app container, git sends the old host to the new one by itself.
Branches on the dashboard
Every checkout on a card shows its branch. Behind open are the repository on Bitbucket or GitHub, on that branch, and your desktop git client if one is installed. See getting to a project.
Git hooks
bash
dqx githooks # write or refresh the git hooks of every site and package
dqx githooks --site api # one site, and no package
dqx githooks --dry-run # say what would change
dqx githooks --off # remove the hooks dqx wrote, and only thoseWithout configuration a site gets a pre-commit that runs Pint and Prettier over the staged files. .dq/config.yml says otherwise, per site and per package, for any hook git knows:
yaml
sites:
- domains:
primary: api.example.test
hooks:
preCommit: # shorthand for git.pre-commit
- vendor/bin/pint ${STAGED_PHP}
git:
commit-msg:
- script: .dq/hooks/ticket-key.sh # the team's own script
pre-push:
- php artisan test # a command, run in the site- A command runs in the site through
dqx exec, so it works on every backend. - A script is a bash file in the wrapper repository, run on your machine with git's own arguments.
commit-msgneeds that: the message file git passes is a path on your machine. The path is relative to the project and may not leave it. - A hook name git does not know is refused when the config is read, so a typo does not silently do nothing.
- In a
pre-commit,${STAGED_PHP},${STAGED_JS}and${STAGED_FILES}hold the staged files, and a command naming one runs only when that list has something in it.
A formatter in the pre-commit (Pint, Prettier) rewrites the staged files on disk. The hook stages them again afterwards, so the commit holds the formatted version. Only files that were staged whole: a file staged in part (git add -p) keeps out the lines you left out, so the hook leaves it alone and names it (dqx: staged in part, so not staged again: …). Stage its formatted lines yourself.
A hook dqx writes carries a stamp on its second line (# dqx-hook: <hash>). Edit the hook by hand and the stamp no longer matches: dqx githooks and dqx adopt then leave it alone and say so, and dqx githooks --force replaces it, keeping yours as pre-commit.bak. Put a lasting change in .dq/config.yml instead. A hook from a dqx version before the stamp is refreshed once, which adds it.
A hook dqx wrote and the config no longer states is removed. A hook dqx did not write is left alone unless you pass --force, which keeps the old one as .bak.
On and off. install.gitHooks switches hooks for the project. A site's install.gitHooks and a package's gitHooks override it either way; absent, they follow the project. Switched off, a checkout gets no hook and loses the ones dqx wrote; somebody else's stays.
Worktrees. Every git worktree of a checkout runs the checkout's hooks, so a hook works out at commit time where it is:
- In the checkout itself, it runs in the site as described above.
- In a worktree inside the checkout (
.claude/worktrees/…, say), which the site's container sees too, the commands run at the site's root with the worktree's path in front of each staged file: the site's Pint formats the worktree's files. - Anywhere else, it runs
dqx exec --herefrom the worktree. In a copy made withdqx clone --worktreethat is the copy's own site; outside every site's checkout dqx refuses, as no container holds those files. Run the formatters yourself there and commit with--no-verify, or put the worktree inside the checkout.
A hook written by an older dqx does not know this yet: dqx githooks rewrites it.
The other commands follow the same line. In a worktree inside the checkout, dqx exec, dqx php, dqx composer and dqx npm run where you stand, and dqx artisan and dqx test at the worktree's root, not the checkout's: the worktree needs its own vendor/ and .env (dqx composer install from there). A worktree beside the checkout is refused rather than run as the project's only site.
Packages get hooks too: see packages.
dqx install runs the same step where a checkout has none yet (dqx install --only hooks runs just that), and dqx adopt installs them when it brings an older project in: see older projects.
Copies and worktrees
dqx clone makes a running copy of a project beside the original, on its own URLs, for trying a branch or a pull request without touching your working copy. A checkout in the copy is either its own clone or a git worktree that shares the original's history: see copies of a project. An agent gets the same through dqx agent-env: see AI assistants.
Reviewing pull requests
A pull request on Bitbucket Cloud or GitHub.com gets four kinds of help, and they end in one post: nothing reaches the pull request until you pick it and press Post.
| what it does | who writes | Claude sees | |
|---|---|---|---|
| AI review | Claude reads the changed lines and proposes findings; you pick, edit and post them | Claude, edited by you | the pull request, read-only, in a worktree |
| Human review | you read the change in the order it runs (flows), open context and whole files, and comment on any line, removed lines included | you | nothing, unless you map the flows |
| Change map | the human review's Map view: the flows as lines of a metro map, with checked arrows, the unchanged code the change depends on, Find uses and ✨ traces | Claude, checked with git grep | the same as mapping the flows; one quick run per ✨ trace. Find uses and reviewed marks stay on your machine |
| Suggest with AI | on the lines you pick, Claude drafts an explanation and the replacement code into your comment editor | you, drafted by Claude | one file and the picked lines, no tools |
- Reviewing a pull request: the terminal, what Claude can and cannot do, profiles (
quick,thorough,security), settings and the Bitbucket token. - Reviewing pull requests in the dashboard: the review tab, choosing per run, editing and posting.
- Human review: read a pull request yourself, in the order the change runs rather than alphabetically, open context and whole files, and post your own comments together with the AI findings.
dqx review --mapprints the same flows in the terminal. - Suggesting a change with AI: a code change for the lines you pick, as a one-click suggestion on GitHub and a code block on Bitbucket.
Everything that sends code to Claude (a review, mapping the flows, a ✨ trace, a suggestion) asks once per repository first: only for repositories where the CTO approved Claude Code. A human review on its own sends nothing; the diff and whole files are read from your own checkout. What dqx posts says where it came from: an AI finding ends in (via dqx/claude), a comment you drafted with Claude in (drafted with dqx/claude) unless you untick it, and your own words carry no footer.
Logins
- Git itself (clone, fetch) uses your SSH key, as it always did.
- Bitbucket's API (pull requests, comments) needs a personal API token:
dqx auth --set bitbucketwalks you through it and keeps it in the Keychain. - GitHub goes through your own
gh auth login; dqx keeps no GitHub token.
Why the SSH key is not enough, and how the token is kept: see a Bitbucket token.
For agents
Through the MCP server an agent can read the pull requests and a finished review's findings, and work through them in the code. It can never start a review or post one: both stay with you. See AI assistants.