Configure @northguild/worktree
worktree stores your config in Git config under northguild.worktree.*. A repository can also carry its
team’s settings in a .worktree.yml file; see Project Configuration. For a
team key your git config wins over the file, and the file wins over the built-in default.
worktree config does not ask for a key the file sets, and worktree config --list marks each value
(.worktree.yml) or (overrides .worktree.yml: <file value>); see
config. worktree config --project writes the file from your git
config.
Common Keys
Section titled “Common Keys”defaultSourceBranch(example:origin/main) — see Default source branchopener(editor,herdr,rex,autoornone, defaulteditor)codeEditor(example:code)herdr.focus(trueorfalse, defaulttrue)herdr.agent(example:claude; the agent kind that starts in every new Herdr space. Unset means no agent, unlessworktree branchorcheckoutis given a brief, which falls back toagent.command’s program)rex.focus(trueorfalse, defaulttrue)agent.command(the command--agentruns to hand a brief to a coding agent, example:claude --bg)postCreate(a command to run in each new worktree, example:pnpm install; unset means the steps in.worktree.yml, else infer from the lockfile)github.token(GitHub personal access token, for github.com only; a GitHub Enterprise host’s token comes fromgh auth token --hostname <host>)github.autoAssign(trueorfalse, unset means you are asked, or assigned when non-interactive)github.repository(example:acme/widgets; the repository--githubreads and assigns in, only needed when it is not the repository found otherwise (upstreamwhen it is ongithub.host(github.comunless set), elseorigin), for example when your issues are on your fork and anupstreamremote exists; never asked for byworktree config) — see GitHub Integration Notesgithub.host(example:ghe.example.com; the GitHub host--githuband--assignuse, a bare hostname, defaultgithub.com; for GitHub Enterprise, which also needs a remote on that host; never asked for byworktree config) — see GitHub Integration Notesjira.host(Your Jira workspace URL, example:https://company.atlassian.net)jira.deployment(cloudordatacenter, defaultcloud; which Jirajira.hostis, Jira Cloud or Jira Data Center; a team key, also set underjira:in.worktree.yml; never asked for byworktree config)jira.email(Your Jira email address)jira.apiToken(Jira API token)jira.autoAssign(trueorfalse, unset means you are asked, or assigned when non-interactive)jira.autoStart(trueorfalse, unset means you are asked, or the issue is moved to in progress when non-interactive)linear.apiKey(Linear personal API key, example:lin_api_...)linear.autoStart(trueorfalse, unset means you are asked, or the issue is started when non-interactive)linear.agentToken(the Linear agent app’s OAuth token; secret; read byworktree linear-listen, which starts worktrees for a Linear agent; never asked for byworktree configand not allowed in.worktree.yml)linear.webhookSecret(the Linear agent app’s webhook signing secret; secret; read byworktree linear-listen; never asked for byworktree configand not allowed in.worktree.yml)linear.agentAllow(comma-separated Linear user ids allowed to delegate an issue to the agent, example:user-id-1,user-id-2; read byworktree linear-listen, and unset means only the owner oflinear.apiKey; never asked for byworktree config, and alinear.agentAllowin.worktree.ymlis ignored with a warning)linear.agentContext(trueorfalse, anything else is refused;trueletsworktree linear-listenadd the delegated issue’s text to the agent’s brief, fenced as untrusted and capped at 32 KiB, and unset meansfalse; never asked for byworktree config, and alinear.agentContextin.worktree.ymlis ignored with a warning)clickup.apiToken(ClickUp personal API token, example:pk_...)clickup.workspaceId(optional; your ClickUp Workspace id, a number, needed only for custom task ids likeDEV-123)clickup.autoAssign(trueorfalse, unset means you are asked, or assigned when non-interactive)clickup.autoStart(trueorfalse, unset means you are asked, or the task is moved to in progress when non-interactive)branchPrefix.feature(example:feature/)branchPrefix.bugfix(example:fix/)branchPrefix.chore(example:chore/)
Default source branch
Section titled “Default source branch”defaultSourceBranch answers two questions, and they are not the same one.
Where a new branch starts. worktree branch uses it when you pass no
--source, and prompts if it is unset.
What counts as unpushed work. list, cleanup and remove compare each
worktree against a base to decide how many commits it carries that nowhere else
has. That base is origin/HEAD — the repository’s own record of its default
branch — and defaultSourceBranch is the fallback when origin/HEAD is not
set, which happens in clones made before git began recording it.
It is also the base a merged branch is recognised against. Where a worktree is
ahead of it, the change it carries is compared with what that base already holds
— so a squash-merged or rebased branch, whose commits survive under new
identities, is seen for what it is rather than counted as work at risk. See
cleanup for what that does and does not permit.
origin/HEAD is asked first deliberately. If the config key were read first,
setting it to origin/develop because that is where you branch from would also
change which commits count as unpushed, and a worktree could be classified as
safe to delete on the strength of a preference about branching.
If neither resolves, no count is taken. Nothing is guessed and nothing is treated as zero, and a worktree whose directory still exists is never classified as safe to remove — an unanswered question is not an answer of “nothing”.
All three commands say so rather than leaving you to guess.
list and remove’s picker
name such a worktree as uncountable, and cleanup
reports it under its own heading rather than sweeping it or passing over it in
silence. To fix it, set the key or
restore the ref:
worktree config defaultSourceBranch origin/main# or, to let git record it again:git remote set-head origin --autoEditor Command
Section titled “Editor Command”codeEditor is a command line, not just a program name. It is split on whitespace: the first word is the
program to launch, and anything after it is passed to that program as arguments, ahead of the worktree
path.
worktree config codeEditor code # runs: code <worktree-path>worktree config codeEditor "code -n" # runs: code -n <worktree-path>The worktree path is always passed as a single argument, so a project or branch whose path contains a space opens correctly.
Quotes group, so an argument containing a space survives:
worktree config codeEditor 'open -a "Sublime Text"' # runs: open -a "Sublime Text" <worktree-path>Quote characters are removed by the split and never reach the program, except an apostrophe between two letters or digits (as in /Users/o'brien/bin/ed), which is kept. Nothing else is interpreted: no
shell parses this value, so ~ and $EDITOR are passed through literally rather than expanded, and a
backslash is an ordinary character rather than an escape. Quote an argument like this instead of writing
an\ argument\ like\ this, and use an absolute path or a wrapper script on your PATH rather than
relying on expansion.
Agent Command
Section titled “Agent Command”agent.command is the command that --agent runs to hand a brief to a coding agent. It does not start an agent on its own: without a brief, only herdr.agent does. The agent that starts in every new Herdr space, brief or not, is herdr.agent. With opener set to rex, the program it names is the session’s own command and the brief its last argument, with --bg and --background dropped, and a brief too long for Rex is refused before anything is created — see Rex Sessions. Like codeEditor it is a command
line, not just a program name, and it is split the same way: the first word is the program to launch, and
anything after it is passed to that program as leading arguments.
worktree config agent.command claudeworktree config agent.command "claude --bg"worktree config agent.command "codex exec"Only the first word is checked when you set the value, so flags and subcommands do not have to resolve to anything on their own:
worktree config agent.command "claude --bg" # accepted — only `claude` is looked upworktree config agent.command "nope --bg" # rejected — Command not found: nopeworktree does not assume a particular agent. There is no default value, and nothing about this key is
specific to one vendor — any agent CLI of the same shape works. A detached agent’s output is discarded, not
kept anywhere, so the shape includes keeping its own session record: claude --bg is reachable through
claude agents, and codex exec keeps its session files unless --ephemeral is passed. The same rules as
Editor Command apply: no shell parses the value, quotes group an argument containing a
space, and ~ and $VAR are not expanded.
The key is optional and unset by default.
When the worktree opens as a Herdr space, the agent is started by Herdr instead, and agent.command only
supplies what to start: its program is the agent kind when a brief is given and herdr.agent is unset, and its
arguments are reused when the program is that kind — without --bg or --background, which would take the
agent out of the pane. For claude, --name <repo>-<branch> is added. See
Agent Dispatch.
Post-create Command
Section titled “Post-create Command”postCreate is the command worktree branch runs in a new worktree to install its dependencies. It is a
command line, split and validated exactly like agent.command: the first word is the program and has to be
on PATH when you set it, quotes group an argument containing a space, and no shell parses the value.
worktree config postCreate "pnpm install"worktree config postCreate "make setup"When it is set it is used as written, and it also turns the install on for a terminal run. Because you set
it, it runs without asking. It wins over the postCreate steps in a repository’s
.worktree.yml, which run only after you approve them
(worktree config --approve, or the prompt branch shows on a terminal). When both are unset, branch infers the command from the new worktree’s lockfile (pnpm-lock.yaml, package-lock.json,
yarn.lock, then bun.lock) and installs by default only when non-interactive. --no-install skips it for
one run. See branch.
The key is optional and unset by default.
Platform Support
Section titled “Platform Support”worktree is developed and tested on macOS and Linux. Windows is not supported. Nothing here is
launched through a shell, so a .cmd or .bat shim — which is what code is on Windows — cannot be
started, and Herdr is a unix-socket application.
Commands
Section titled “Commands”worktree config --listworktree config --missingworktree config --missing --names defaultSourceBranch,github.tokenworktree config defaultSourceBranch origin/mainworktree config codeEditor codeworktree config opener herdrworktree config herdr.focus falseworktree config herdr.agent claudeworktree config opener rexworktree config rex.focus falseworktree config agent.command "claude --bg"worktree config postCreate "pnpm install"worktree config github.token <token>worktree config jira.host https://company.atlassian.networktree config jira.email your-email@example.comworktree config jira.apiToken <api-token>worktree config jira.deployment datacenter # Jira Data Center only; no jira.email thenworktree config linear.apiKey <api-key>worktree config clickup.apiToken <api-token>worktree config clickup.workspaceId <workspace-id>worktree config branchPrefix.feature feature/worktree config branchPrefix.bugfix fix/worktree config branchPrefix.chore chore/Opener Notes
Section titled “Opener Notes”openerselects where a finished worktree is handed off:editorusescodeEditor,herdropens it as a Herdr space — and closes that space again when the worktree is removed,rexopens it as a Rex session and kills it when the worktree is removed, andnoneopens nothing: the command printsWorktree created at <path>(Worktree already exists at <path>when it reopens an existing worktree) and stops.autopicks Herdr or Rex from the terminal the command runs in, when that tool’s CLI is installed: Herdr first if the terminal is inside both, otherwise the editor. A fixed value always overrides it, and unset still meanseditor, soautois something you opt into.removeandcleanupclose the space or session underautotoo: they ask every installed tool and close it wherever it is held, whatever terminal they run from.codeEditoris used by theeditoropener, and byautoas its fallback — see Editor Command above and the Editor Integration guide.herdr.focusandherdr.agentapply only whenopenerisherdr, orautopicks it. See Herdr Spaces.rex.focusapplies only whenopenerisrex, orautopicks it. See Rex Sessions. It is shown byworktree configonly when therexCLI can be found, by the same rule as the Herdr keys below: settingopenertorex, or a value already held, keeps it shown. The wizard offersrexas an answer only where it would be shown, and the answerrexasksrex.focus.herdr.focusandherdr.agentare shown byworktree configonly when theherdrCLI is on yourPATH. Without it they are neither listed nor prompted for, so the feature stays out of the way. This gating never hides a key that already holds a value, and settingopenertoherdrun-hides both whether or not the CLI is found.openeritself is always shown, but the wizard offersherdras its answer only where those keys would be shown; otherwise it offerseditorandnone(andrex, where therexCLI is found), andautowhereverherdrorrexis offered (or whenautois already your opener). The opener’s answer decides the follow-ups:editoraskscodeEditor,herdrasksherdr.focus,autoaskscodeEditorand the focus key of each of Herdr and Rex that is offered,noneasks nothing more.herdr.agentis asked with the coding agent: afteragent.command, whenopenerisherdr, a yes/no offers to set it to the command’s first word. Naming a key with--names, or setting it directly, always works, and--names openeroffers all five answers.autois preselected only when it is already your opener.
GitHub Integration Notes
Section titled “GitHub Integration Notes”worktree branch --github <issue-number>reads issues from the repository named bygithub.repository(git config, then.worktree.yml), else theupstreamremote when it is ongithub.host(github.comunless set), elseorigin. When that is notorigin’s repository, the output names it. To read a fork’s own issues, or any other repository, set the key. It is a team key, so it can live in.worktree.ymlundergithub:, and git config overrides it. See Forks.- Branch prefix keys are optional. If neither your git config nor
.worktree.ymlsets one,worktreeuses no prefix. - If
github.tokenis missing,worktreecan attempt to usegh auth token --hostname github.com(read each run and never saved; neitherGH_HOSTnor aghlogin to another host supplies it) and then prompt; only a typed or configured token is stored. On a GitHub Enterprise host (github.host) the stored token is never read or sent: the only source isgh auth token --hostname <host>, there is no prompt, and a missing token exits2naminggh auth login --hostname <host>. github.hostnames the one GitHub host that--githuband--assignuse, and the clone needs a remote on it as well (the remote the repository is read from, ororiginwhengithub.repositoryis set). Both have to agree, or the run fails before any request. See GitHub Enterprise.github.autoAssigndecides whether reading an issue also assigns it to you. It has three states, not two:truealways assigns,falsenever does, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it assigns and saves nothing. Assigning needs a token whose account has push access to the repository; a fine-grained token also needs the Issues (or Pull requests) permission set to Read and write.worktree branch --assignand--no-assignoverride the key for a single run, whatever it holds. Seebranch.- If you answered no and want the offer back, set the key directly:
worktree config github.autoAssign true- To pin the repository, for example when your issues are on your fork although an
upstreamremote exists:
worktree config github.repository <owner/repo>github.tokenandgithub.autoAssignare prompted together, only when you tick GitHub in the setup checklist, soworktree configasks nothing about GitHub issues of anyone who leaves it unticked.
Jira Integration Notes
Section titled “Jira Integration Notes”worktree branch --jira <issue-id>reads issues from your configured Jira workspace.- On Jira Cloud (
jira.deploymentunset orcloud) all three Jira connection keys (jira.host,jira.email,jira.apiToken) are required. Ondatacenteronlyjira.hostandjira.apiTokenare, and the token is a personal access token; see Jira Data Center. jira.autoAssignis optional and decides whether--jiraalso assigns the issue to you. It has three states, likegithub.autoAssign:truealways assigns unless someone else already holds the issue, which is then left with them,falsenever assigns, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it assigns and saves nothing.worktree branch --assignand--no-assignoverride the key for a single run. To set it directly:
worktree config jira.autoAssign truejira.autoStartis optional and decides whether--jiraalso moves the issue to in progress once the worktree exists. It has three states, likelinear.autoStart:truealways moves it (an issue already in progress or done, or with no single in-progress status to move to, is left where it is),falsenever does, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it moves the issue, under the same rule, and saves nothing, so a script that passes--jiraand must not change the issue’s status needs--no-startorjira.autoStart false. Moving an issue needs the Transition Issues permission; the branch command and the Jira guide say which statuses move. To set it directly:
worktree config jira.autoStart true- Branch prefix keys are optional. If neither your git config nor
.worktree.ymlsets one,worktreeuses no prefix. jira.hostcan come from.worktree.yml; a file’s host receives the credentials of everyone who uses it, so see Project Configuration.- The prefix a Jira issue gets comes from its issue type, unless
.worktree.ymlhas ajira.branchTypelist or a shared top-levelbranchTypelist. The built-in mapping applies only when no rules are set; see Branch-type rules. - Issue IDs are case-insensitive (both
dev-123andDEV-123work).
Linear Integration Notes
Section titled “Linear Integration Notes”worktree branch --linear <issue-id>reads issues from the Linear workspace your key belongs to.linear.apiKeyis required by--linearand bylinear-listen.cleanupandlist --issuesalso read it, to learn whether a linked issue is canceled; they do not require it, and make no Linear request without one.linear.autoStartis optional and decides whether--linearalso moves the issue to a started state once the worktree exists. It has three states, likejira.autoAssign:truealways starts (an issue that is already started, or is in triage, completed, canceled or duplicate, is left where it is),falsenever does, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it starts and saves nothing. Moving an issue needs a key with the Write permission and access to the issue’s team. See Moving the issue to started for the order the decision is made in. To set it directly:
worktree config linear.autoStart true- Linear has no issue type, so the branch prefix comes from the issue’s labels:
bugusesbranchPrefix.bugfix,choreormaintenanceusesbranchPrefix.chore, anything else usesbranchPrefix.feature. Alinear.branchTypelist, or a shared top-levelbranchTypelist, in.worktree.ymlreplaces this mapping; see Branch-type rules. - Issue identifiers are case-insensitive (both
eng-123andENG-123work).
ClickUp Integration Notes
Section titled “ClickUp Integration Notes”worktree branch --clickup <task-id>reads tasks from the ClickUp Workspace your token belongs to.clickup.apiTokenis required.clickup.workspaceIdis optional and is needed only when you pass a custom task id such asDEV-123; a short id such as9hxnever needs it. An empty answer to its prompt skips it.clickup.autoAssignis optional and decides whether--clickupalso assigns the task to you. It has three states, likejira.autoAssign:truealways assigns,falsenever does, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it assigns and saves nothing. It is a personal key: aclickup.autoAssignin.worktree.ymlis ignored with a warning. To set it directly:
worktree config clickup.autoAssign trueclickup.autoStartis optional and decides whether--clickupalso moves the task to in progress once the worktree exists. It has three states, likelinear.autoStart:truealways moves it (a task already in progress, or done, closed or in any other status type, is left where it is, as it is when the List has no in-progress status),falsenever does, and leaving it unset means you are asked, once, and your answer is saved to this key. A non-interactive run does not ask: it moves the task, under the same rule, and saves nothing. Moving a task needs edit access to it; the branch command and the ClickUp guide say which statuses move. To set it directly:
worktree config clickup.autoStart true- The branch prefix comes from the task’s tags:
bugusesbranchPrefix.bugfix,choreormaintenanceusesbranchPrefix.chore, anything else usesbranchPrefix.feature. Aclickup.branchTypelist, or a shared top-levelbranchTypelist, in.worktree.ymlreplaces this mapping; see Branch-type rules. - A short id is case-sensitive and typed without
CU-(9hx); the branch name gainsCU-. A custom id is sent to ClickUp as typed and upper-cased in the branch name (dev-123givesDEV-123in the name).
Recommended Team Baseline
Section titled “Recommended Team Baseline”Put the settings a team shares in a committed .worktree.yml instead of asking everyone to run the same
worktree config commands: the source branch, the branch prefixes, the Jira workspace URL and the ClickUp
workspace id. See Project Configuration. worktree config --project writes
the first version from your own git config.
Some things stay personal, so agree on them in conversation rather than in the file: the editor command, and
which labels (Linear) or tags (ClickUp) mean bug and chore work, since the label or tag decides the prefix,
unless the file’s linear.branchType, clickup.branchType or shared branchType rules say so for the whole team.