worktree branch: create a git worktree for a new branch
Create a new branch in a dedicated worktree.
worktree branch <name>worktree branch <name> --source <remote-branch>worktree branch --github <issue-number>worktree branch --github <issue-number> --assignworktree branch --github <issue-number> --no-assignworktree branch --jira <issue-id>worktree branch --jira <issue-id> --assignworktree branch --jira <issue-id> --startworktree branch --jira <issue-id> --no-startworktree branch --linearworktree branch --linear <issue-id>worktree branch --linear <issue-id> --startworktree branch --linear <issue-id> --no-startworktree branch --clickup <task-id>worktree branch --clickup <task-id> --assignworktree branch --clickup <task-id> --startworktree branch --clickup <task-id> --no-startworktree branch --jira <issue-id> --dry-runworktree branch <name> --agent "<prompt>"worktree branch <name> --agent-file <path>worktree branch <name> --agent-stdinworktree branch <name> --no-agentworktree branch <name> --no-openworktree branch <name> --installworktree branch <name> --no-installworktree branch <name> --jsonExamples
Section titled “Examples”worktree branch feature/add-bulk-actionsworktree branch feature/add-bulk-actions --source origin/release/1.4worktree branch --github 42worktree branch --github "#42"worktree branch --github 42 --assignworktree branch --github 42 --no-assignworktree branch --jira DEV-123worktree branch --jira dev-123worktree branch --linearworktree branch --linear ENG-123worktree branch --linear eng-123worktree branch --linear ENG-123 --no-startworktree branch --clickup 9hxworktree branch --clickup DEV-123worktree branch --jira DEV-123 --dry-runworktree branch --github 42 --dry-run --jsonworktree branch feature/add-bulk-actions --agent "add bulk actions to the table"worktree branch --github 42 --agent "implement the issue"worktree branch --github 42 --agent-file brief.mdcat brief.md | worktree branch --github 42 --agent-stdinworktree branch --github 42 --no-agentworktree branch feature/add-bulk-actions --no-openworktree branch feature/add-bulk-actions --installworktree branch feature/add-bulk-actions --no-installworktree branch --github 42 --json --agent-file brief.mdGitHub Issue Branching
Section titled “GitHub Issue Branching”Use the --github flag to create a suggested branch name from an issue. The issue is read from the
repository named by github.repository, else the upstream remote when it is on github.host (github.com unless set), else origin; in a
fork that means the original’s issues, and the output names the repository whenever it is not origin’s. See
Forks.
For a GitHub Enterprise host, see GitHub Enterprise.
- The issue number can be passed as
42or"#42", or as the issue’s URL,https://github.com/<owner>/<repo>/issues/42(www.github.comworks too, and a pull request’s/pull/42URL reaches the pull request refusal below). A trailing slash, query string and fragment such as#issuecomment-1are ignored. The URL’s repository must be the one this run reads, compared ignoring case; otherwise the run exits2withinvalid_value, naming both repositories and pointing atgithub.repository, before the issue is fetched or anything is created. The URL’s host must begithub.host(github.comwhen unset; for a GitHub Enterprise host the URL ishttps://<host>/<owner>/<repo>/issues/42), compared ignoring case, and it is never contacted. Any other URL is refused the same way. - A pull request number is refused as a usage error, since the pull request already has a branch. The message names
worktree checkout <head-branch>when that branch is onoriginand its name is only letters, digits,_,.,/and-; otherwise, a fork’s orupstream’s branch included, it says to runworktree checkoutand pick one. - A closed issue gets a warning. An interactive run asks before continuing, and answering no ends the run with exit
1and no worktree; a non-interactive run carries on, and--jsonreports the warning inwarnings. - The generated name is pre-filled in the prompt and still editable. A non-interactive run takes it as it is.
- If issue types are configured in the repository, these optional prefixes are applied:
branchPrefix.featurefor issue typeFeaturebranchPrefix.bugfixfor issue typeBugbranchPrefix.chorefor issue typeTask
- An issue with no type, or any other type, gets no prefix. A
github.branchTypelist, or a shared top-levelbranchTypelist, in.worktree.ymlreplaces that mapping (matching the issue’sTypeandLabels), and- otherwise: featureat its end opts an unmatched issue in to a prefix. See Branch-type rules.
If no prefix is configured, worktree falls back to an empty prefix and still generates a valid branch name.
By default the name is <prefix><number>-<slug>; a branchName template in .worktree.yml changes its shape (see Branch name template). The title becomes a lowercase slug of letters, digits and dashes, cut to at most 48 characters at the last
dash, so the name ends on a whole word. A single word longer than 48 characters is cut at 48. The name is the
same whether or not you are asked, so one issue never gives two different branches.
GitHub authentication on github.com is resolved in this order:
northguild.worktree.github.tokenfrom local git config.gh auth token --hostname github.comif GitHub CLI is installed and authenticated. It is read on each run and not saved, and neitherGH_HOSTnor aghlogin to another host supplies it.- Interactive token prompt, which is then saved in local git config. Only a typed or
worktree config-set token is stored.
On a GitHub Enterprise host (github.host) the stored token is never read or sent, and there is no prompt: the token comes only from gh auth token --hostname <host>, and without one the run exits 2 naming gh auth login --hostname <host>. See GitHub Enterprise.
On github.com, a non-interactive run has no token prompt. It exits 2 with one line on stderr, which never includes a token:
worktree: no default for a GitHub token; pass `worktree config github.token <token>` or run `gh auth login`Assigning the issue to yourself
Section titled “Assigning the issue to yourself”--assign assigns the issue to the authenticated user as part of creating the branch, and --no-assign
skips it for that one run. It works with --github, --jira and --clickup; --linear does not assign
yet. The decision is made in this order:
--assignor--no-assign.- The provider’s key:
github.autoAssignfor--github,jira.autoAssignfor--jira,clickup.autoAssignfor--clickup.trueassigns,falsenever does. The keys are independent. - A non-interactive run assigns. The default is not saved, so one unattended run does not settle the key.
- Otherwise you are asked once, and your answer is saved to the provider’s key so the question is not repeated.
A configured false is respected in every mode; pass --assign to override it for one run.
worktree branch --github 42 --assignworktree branch --github 42 --no-assignworktree branch --jira DEV-123 --assignworktree branch --clickup 9hx --assignworktree config github.autoAssign true # always assign, stop askingworktree config jira.autoAssign trueworktree config clickup.autoAssign true- For
--github, 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. Without that permission GitHub refuses the call (403 Resource not accessible by personal access token) andworktreewarns with GitHub’s message followed by the sentence above. Reading an issue needs neither, so a public repository that--githubhas always worked against without a token may ask for one the first time you assign — the resolution order above still applies, so an authenticatedghsupplies it silently. - A Jira issue that someone else holds is left alone. Jira keeps one assignee, so assigning would take the
issue off a colleague.
worktreeleaves it, warns with their name, and the document saysassigned: false;--assigndoes not override this, so reassign it in Jira if that is what you want. An issue that is already yours is reported as such and nothing is written. Assigning in Jira needs the Browse Projects and Assign Issues permissions on the issue’s project; without themworktreewarns and carries on. See Jira Issue Integration. - A ClickUp assignment only adds you. ClickUp keeps several assignees, so you join the task alongside whoever is already on it and nobody is removed; a task that is already yours is reported as such and nothing is written. Assigning needs permission to edit the task, so a token that can read it but not change it gets a warning: with ClickUp’s status line and that hint, or the authentication or not-found message when ClickUp answers 401, 403 or 404. See the ClickUp guide.
- For
--github, success is confirmed against the API response rather than assumed. If the issue comes back without you on it,worktreesays so instead of reporting success — GitHub documents that it accepts an assignee change and ignores it when the account lacks push access. That case has not been tested against GitHub, and neither has whether an account with only the triage role can assign. - A failed assignment never stops the branch. The worktree is created either way; you get a warning, not an error.
- The assignment happens before the worktree is created, so the question is never buried behind creation output or a launched editor. Assigning is idempotent — GitHub does not replace existing assignees, a Jira issue that is already yours is not written again, and a ClickUp assignment only adds you and skips a task that already lists you — so retrying after a failed creation is harmless.
--assignand--no-assignboth require one of--github,--jira,--linearor--clickup; on their own there is no issue to act on.--assign --linearwarns that assignment is not supported for Linear yet and carries on.
Previewing a branch name
Section titled “Previewing a branch name”--dry-run shows what branch would name a branch, and why, without making it. Pass it with one of
--github, --jira, --linear or --clickup: it fetches that issue, applies the
branchType rules and the
branchName template, and stops after naming. On its own
it is refused with exit 2, because with no issue there is nothing to evaluate the rules against. A bare --linear is
refused the same way: choosing an issue is a question, and a dry run asks nothing, so give it the identifier.
worktree branch --jira DEV-123 --dry-runDry run: nothing was created, assigned or saved.Branch name: chore/DEV-123-tidy-the-billing-jobRule: rule 1 of jira.branchType: when: { customfield_10359: { id: "10301" } }, type: choreFields with a value (2), each with a line to paste into jira.branchType: Work Category (id customfield_10359) Maintenance (id 10301) - when: { customfield_10359: { id: "10301" } } # Work Category = Maintenance Issue Type (id issuetype) Task (id 10009) - when: { issuetype: { id: "10009" } } # Issue Type = Task- Nothing is changed. A dry run creates no worktree, assigns no issue, writes no git config, runs no
install or
postCreatestep, moves no issue, starts no agent, opens nothing and asks no question. A closed--githubissue is a warning instead of a prompt.--agent,--agent-file,--agent-stdin,--source,--assign,--start,--installand--no-openflags are accepted and ignored. - The
Rule:line names the rule that chose the prefix by its position in the list and its text, and names the list:jira.branchType, orbranchTypewhen the shared list decided. It readsbuilt-in mappingwhen the file has neither a rule list for that provider nor a sharedbranchType, andnone matchedwhen it has rules and none matched, so the name has no prefix. - Every field the issue has a value for is listed with its id and its name as your account sees it, and
each of its values with its id where it has one. Empty fields are left out. Each value comes with a line to
paste into a
branchTypelist; see Writing rules with a dry run. - Provider-only fields are marked when the shared list decided. Any field but
TypeandLabels, in either case, is that provider’s own, so the dry run names<provider>.branchTypebeside it as the list to paste its lines into: a shared rule naming it never matches the other providers and does not warn on a real run. A provider’s own list replaces the shared one for that provider, so copy across any shared rules you still want there. - Jira field names come from
?expand=nameson the issue request (?expand=names,schemaon Jira Data Center), which a dry run always asks for. The paste-ready lines name ids, except thatTypeandLabelsare by name when the sharedbranchTypelist decided, because an id belongs to one provider.
With --json a dry run prints one document, not the branch document:
{"dryRun":true,"branch":"chore/DEV-123-tidy-the-billing-job","issue":{"provider":"jira","key":"DEV-123","url":"https://acme.atlassian.net/browse/DEV-123"}, "kind":"chore","match":{"source":"rule","position":1,"rule":"when: { customfield_10359: { id: \"10301\" } }, type: chore"}, "fields":[{"id":"customfield_10359","name":"Work Category","pasteInto":"jira.branchType","values":[{"id":"10301","name":"Maintenance","when":"- when: { customfield_10359: { id: \"10301\" } } # Work Category = Maintenance"}]}], "warnings":[]}| Key | Value |
|---|---|
dryRun |
always true |
branch, issue |
the name branch would give, and the issue it came from, as in the branch document |
kind |
feature, bugfix or chore; null when no prefix applies |
match |
{source:"rule", position, rule} for the rule that matched, {source:"built-in"} or {source:"none"} |
fields |
the non-empty fields: {id, name, pasteInto, values:[{id, name, when}]}. pasteInto is the list the field’s lines belong in; id or name is null where the value has none (a GitHub label has both; a ClickUp tag has a name only); when is the paste-ready line |
warnings |
the same as in the branch document, such as a rule naming a field the issue does not have |
Installing dependencies
Section titled “Installing dependencies”After the worktree exists and its env files are copied, branch can install its dependencies, before
anything is opened or any agent is started, so neither lands in a tree without them.
-
The command is
postCreatewhen it is set in your git config. Otherwise it is the list of steps underpostCreatein the repository’s.worktree.yml, run in order. With neither, it is inferred from the first lockfile found in the new worktree:Lockfile Command pnpm-lock.yamlpnpm install --frozen-lockfilepackage-lock.jsonnpm ciyarn.lockyarn installbun.lockbun install --frozen-lockfilebun.lockbis not recognised.yarn installhas no lock flag because classic and berry disagree on it. -
When it runs: a non-interactive run installs by default. A terminal run installs only when
postCreateis set or--installis given.--no-installswitches it off for the run, whatever else is set. -
Approval: the steps from
.worktree.ymlmust be approved by you first, and this is checked before anything is created. On a terminalbranchlists every step and asks; no exits1. A non-interactive run that has not approved them exits2withmissing_value, namingworktree config --approve(run it in a terminal) and--no-install. ApostCreateyou set yourself,--no-installand--dry-runneed no approval. See Approving the steps. -
How it runs: each step by argv in the new worktree, never through a shell, with its output on stderr and a 10 minute limit per step. The first failing step stops the rest.
-
Nothing to run: with no
postCreateand no lockfile, or when the program of your ownpostCreateor of the lockfile command is not onPATH,branchsays why on stderr and carries on. -
A failed install (a step exits non-zero, or the limit is reached; a file step whose program is not on
PATHfails like any other step) keeps the worktree, opens nothing, starts no agent, and exits1. Nothing is rolled back. Fix the install in the worktree and runworktree open <branch>, or runworktree remove <branch> -fand re-runbranchwith--no-install. Re-runningbranchas it was does not create anything: the branch name is taken, so it reopens the worktree (on a terminal) or refuses — see A branch name that is already taken.
checkout has no install step.
Jira Issue Branching
Section titled “Jira Issue Branching”Use the --jira flag to create a suggested branch name from an issue in your Jira workspace.
- The issue ID can be passed in any case (
dev-123,DEV-123, etc.), or as the issue’s URL,https://<host>/browse/DEV-123. A trailing slash, query string and fragment are ignored. The URL’s base, everything before/browse/(a context path such as/jiraincluded), must be yourjira.host, compared ignoring case; otherwise the run exits2withinvalid_valuenaming both, and the URL’s site is never contacted, so your credentials are only ever sent tojira.host. A key may contain_on Jira Data Center. Pass the key, or check that the URL is from your Jira site. Any other URL is refused the same way. - The generated name is pre-filled in the prompt and still editable.
- If issue types are configured in the repository, these optional prefixes are applied:
branchPrefix.featurefor issue types likeFeature,Story, and most non-bug/chore typesbranchPrefix.bugfixfor issue typeBugbranchPrefix.chorefor issue typeTask
- That mapping applies only when no
jira.branchType(or sharedbranchType) rules are set. With rules in.worktree.yml, the rules decide the kind, and an issue no rule matches gets no prefix unless the list ends withotherwise. See Branch-type rules.
If no prefix is configured, worktree falls back to an empty prefix and still generates a valid branch name.
Jira authentication requires three configuration keys on Jira Cloud, and two on Jira Data Center:
northguild.worktree.jira.host(orjira.hostin.worktree.yml, see Project Configuration) - Your Jira workspace URL (e.g.,https://company.atlassian.net)northguild.worktree.jira.email- Your Jira email address (Cloud only; not asked and not read on Data Center)northguild.worktree.jira.apiToken- Your Jira API token, or on Data Center a personal access token sent as a Bearer token
Set jira.deployment to datacenter for Jira Data Center or Server; see Jira Data Center.
Moving the Jira issue to in progress
Section titled “Moving the Jira issue to in progress”With --jira, --start and --no-start decide in the same order, with
jira.autoStart as the key, and the question and its saved answer name
that key. The write happens at the same point: after the worktree exists, before the env files, the install and the
opener, and never when reopening an existing worktree. A dry run ignores --start, asks nothing and moves nothing.
worktree branch --jira DEV-123 --startworktree branch --jira DEV-123 --no-startworktree config jira.autoStart true # always start, stop asking- Only an issue in the To Do category moves. Jira puts every status in a category, and the move goes by that,
never by a status name. An issue in To Do goes through the transition Jira offers from its current status that
leads to an In Progress category status. An issue already in that category is reported as already started and not
moved, so
startedistrue. An issue in the Done category, or any other, is a warning and is left where it is;startedisfalse. - It will not guess between statuses. When the transitions available lead to more than one in-progress status,
or to none, nothing is written and
startedisfalse: with several, the warning names the statuses it found, and you move the issue in Jira. With none, the warning says the workflow may have none or the account may lack the Transition Issues permission, because Jira answers the same way for both. - A non-interactive run with the key unset moves the issue, without saving anything, so a script that passes
--jiranow changes the issue’s status.falseor--no-startstill skips it. - The worktree is the deliverable. A move that fails (no network, a transition that needs a field set in Jira,
an account without the Transition Issues permission) warns with the reason and carries on with the exit code the
run would have had. The warning appears in
warningsandstartedisfalse. Nothing but the status is written. - It reads the issue’s status and the transitions Jira offers from it, never its description or comments. See the Jira guide.
Linear Issue Branching
Section titled “Linear Issue Branching”Use the --linear flag (short: -l) to create a suggested branch name from an issue in your Linear workspace. It
cannot be combined with --github, --jira or --clickup. See the Linear guide.
- The issue identifier can be passed in any case (
eng-123,ENG-123, etc.), or as the issue’s URL,https://linear.app/<workspace>/issue/ENG-123, with or without the title slug Linear appends. A trailing slash, query string and fragment are ignored. The URL’s workspace is checked against the workspace of the issue Linear returns, ignoring case, after the one fetch and before anything is created; another workspace exits2withinvalid_value. When Linear returns no URL for the issue, the workspace cannot be confirmed, so the run warns and carries on. Any other URL is refused the same way. - With no identifier (
worktree branch --linear), a menu lists your own unstarted and started issues, each as identifier, title and state name, and the one you pick is used exactly as if you had typed it. Only one page of up to 50 is fetched; when more match, the prompt says so and names the way out, which is to quit and pass the identifier. If none is assigned to you the command exits1and says to pass an identifier. See Picking an issue. - The generated name is pre-filled in the prompt and still editable.
- Linear has no issue type, so the issue’s labels pick the optional prefix, by exact name and ignoring case:
branchPrefix.bugfixfor abuglabelbranchPrefix.chorefor achoreormaintenancelabel, when there is nobuglabelbranchPrefix.featurefor anything else, including no label
- That mapping applies only when no
linear.branchType(or sharedbranchType) rules are set; with rules, the rules decide and an unmatched issue gets no prefix unless the list ends withotherwise.
If no prefix is configured, worktree falls back to an empty prefix and still generates a valid branch name.
Linear authentication requires one configuration key, northguild.worktree.linear.apiKey, your Linear personal
API key. Without it the command offers to run worktree config, and when there is still no key (or the run is non-interactive) exits 2 with missing_value. Assigning the issue is not supported yet; moving it to started is.
Picking a Linear issue
Section titled “Picking a Linear issue”--linear with no identifier, last on the line or followed by another flag, asks you to pick one of your own
unstarted and started Linear issues. It sends one request for them, after your key is checked, and the menu shows
each issue’s identifier, title and state name in words. Titles and state names are written by anyone in your
workspace, so control and formatting characters in them are shown as escapes such as \x1b.
A pick needs a person at a terminal. --json, --yes, --non-interactive, CI and a missing terminal exit 2
with missing_value (no default for the Linear issue; pass --linear <identifier>) before the fetch and before
any request, and so does --dry-run, which asks nothing. Ctrl-C at the menu, or Esc and then Yes to “Quit now?”, ends the run quietly with nothing
created.
worktree branch --linearworktree branch --linear --no-startMoving the issue to started
Section titled “Moving the issue to started”--start moves the Linear issue to its team’s first started state once the worktree exists, and --no-start
skips it for that one run. It works with --linear, with --clickup (see
Moving the ClickUp task to in progress) and with --jira (see
Moving the Jira issue to in progress); --start with --github warns that
it is not supported and carries on, and --no-start there warns nothing. On its own either flag exits 2, as
--assign does, because there is no issue to act on. The decision is made in this order:
--startor--no-start.linear.autoStart(orclickup.autoStartfor--clickup,jira.autoStartfor--jira):truestarts,falsenever does.- A non-interactive run starts. The default is not saved, so one unattended run does not settle the key.
- Otherwise you are asked “Move this issue to started?” once, and your answer is saved to
linear.autoStart(clickup.autoStartfor--clickup,jira.autoStartfor--jira) so the question is not repeated.
A configured false is respected in every mode; pass --start to override it for one run.
worktree branch --linear ENG-123 --startworktree branch --linear ENG-123 --no-startworktree config linear.autoStart true # always start, stop asking- Only issues that have not begun move. An issue in a backlog or unstarted state goes to the team’s started state with the lowest position (usually In Progress). An issue that is already started (In Review, say) is reported and left where it is, as are triage, completed, canceled and duplicate issues, and a team with no started state. Each outcome is said in words, with the state names, and a left-alone issue is a warning that names its state.
- It happens after the worktree exists, and before the env files, the install and the opener. A failed creation moves nothing, and reopening an existing worktree moves nothing. The question is asked before the worktree is created, so it is never buried behind creation output or a launched editor.
- The worktree is the deliverable. A move that fails (no network, a rate limit, a key without the Write
permission or without access to the issue’s team) warns with Linear’s message and carries on with the exit
code the run would have had. The warning appears in
warningsandstartedisfalse. - It reads and writes the issue’s workflow state (names, types and positions), never its description or comments. Moving an issue needs a key with the Write permission; see the Linear guide.
ClickUp Task Branching
Section titled “ClickUp Task Branching”Use the --clickup flag (short: -c) to create a suggested branch name from a task in your ClickUp Workspace. It
cannot be combined with --github, --jira or --linear. See the ClickUp guide.
- Pass the task id in either form: the short id in the task’s URL and ID field (
9hx, typed withoutCU-; case matters), or a custom id (DEV-123, sent to ClickUp as typed and upper-cased in the branch name) when your Workspace has custom task ids switched on. Anything else, includingCU-9hx, exits2withinvalid_valuebefore any request. - A custom id needs
clickup.workspaceId; without it the command exits2withmissing_value. - A short id becomes
CU-<id>in the branch name (feature/CU-9hx-fix-login); a custom id is upper-cased (fix/DEV-123-fix-login). - The generated name is pre-filled in the prompt and still editable.
- ClickUp’s tags pick the optional prefix, by exact name and ignoring case:
branchPrefix.bugfixfor abugtagbranchPrefix.chorefor achoreormaintenancetag, when there is nobugtagbranchPrefix.featurefor anything else, including no tag
- That mapping applies only when no
clickup.branchType(or sharedbranchType) rules are set; with rules, the rules decide and an unmatched task gets no prefix unless the list ends withotherwise.
ClickUp authentication requires one configuration key, northguild.worktree.clickup.apiToken, your ClickUp personal
API token. Without it the command offers to run worktree config, and when there is still no token (or the run is
non-interactive) exits 2 with missing_value. Assigning the task and moving it to in progress both work.
Moving the ClickUp task to in progress
Section titled “Moving the ClickUp task to in progress”With --clickup, --start and --no-start decide in the same order, with
clickup.autoStart as the key, and the question and its saved
answer name that key. The write happens at the same point: after the worktree exists, before the env files, the
install and the opener, and never when reopening an existing worktree. A dry run ignores --start, asks nothing and
moves nothing.
worktree branch --clickup 9hx --startworktree branch --clickup 9hx --no-startworktree config clickup.autoStart true # always start, stop asking- Only a task in an open status moves. ClickUp tells statuses apart by type. A task whose status type is
open(To Do, say) goes to the List’s firstcustomstatus by position, which is the nearest ClickUp has to “in progress”. A task already in acustomstatus (In Progress, In Review) is reported as already started and not moved, sostartedistrue. A task in adoneorclosedstatus, or any other type, is a warning and is left where it is, and so is a List with nocustomstatus;startedisfalsefor those. Each outcome is said in words, with the status names. - A non-interactive run with the key unset moves the task, without saving anything.
falseor--no-startstill skips it. - The worktree is the deliverable. A move that fails (no network, a rate limit, a refused write, an answer that
does not show the new status) warns with the reason and carries on with the exit code the run would have had.
The warning appears in
warningsandstartedisfalse. Moving a task needs edit access to it. - It reads the task’s status and its List’s statuses, never its description or comments. See the ClickUp guide.
Agent Dispatch
Section titled “Agent Dispatch”Use the --agent flag (short: -a) to hand the new worktree to a coding agent with a prompt, called the
brief. The brief can come from one of three places, and they are mutually exclusive:
| Flag | The brief is |
|---|---|
--agent "<text>" |
the flag’s value |
--agent-file <path> |
the whole of a file, which has to exist and be a regular file |
--agent-stdin |
the whole of piped stdin; a terminal on stdin is refused rather than waited on |
worktree branch feature/add-bulk-actions --agent "add bulk actions to the table"worktree branch --github 42 --agent-file brief.mdAn empty brief, or one over 131,071 bytes (the most a single argument can carry on Linux), is rejected before anything is created. The brief is delivered verbatim, so quotes, apostrophes, semicolons and newlines in it reach the agent unchanged and no shell interprets it.
There is one agent per worktree, started one of three ways:
- When the worktree opens as a Herdr space (
openerherdr, orautoin a Herdr terminal withherdrinstalled), the agent is started in the space’s pane withherdr agent start, and the brief is then submitted to it withherdr agent prompt, once, after the agent is ready. The brief is never part of the start command.- The agent kind is
herdr.agent, or else the programagent.commandnames. With a brief and neither, and Herdr as the opener and installed, the run exits2before the worktree is created, withworktree: no default for the agent kind; pass `worktree config herdr.agent <kind>`. - When
agent.commandnames the same program, its arguments are passed to the agent, minus--bgand--background(a note says so on stderr): Herdr runs the agent in the pane, so a background flag would take it out of sight. - For
claude,--name <session>is added. The session name is<repo>-<branch>, lowercased, with every run of characters outsidea-z,0-9and-turned into-, and never shortened. It is printed on stderr. Herdr’s own handle for the agent is separate and capped at 32 characters. - If starting the agent fails, or the brief cannot be delivered, a warning says so (a failed delivery names the brief by its size, never its text), the worktree and
space are kept, and the run exits
1.
- The agent kind is
- When the worktree opens as a Rex session (
openerrexwith therexCLI found, orautoin a Rex terminal withrexinstalled and no Herdr taking it first), the agent is the session’s own command:rex newis given the programagent.commandnames, its arguments, and the brief as the last argument, so nothing is typed into a prompt and no ready signal is waited for. Without a brief the session is a plain shell.- With a brief and no
agent.command, the run exits2before the worktree is created, withworktree: no default for the agent command; pass `worktree config agent.command "<command>"`. - A brief whose request Rex would refuse, over 65,536 encoded bytes, also exits
2before the worktree is created.worktreeestimates the request from the command, the brief and Rex’s envelope, counting each quote, backslash, newline and tab as two bytes,<,>,&and other control characters as six, and other non-ASCII characters as their UTF-8 length, so the most a brief can be is about 62,000 bytes of plain text and less when it is full of those characters (code with generics or&&counts for more than its length). The message names the limit and the brief’s size, never its text. --bgand--backgroundare dropped fromagent.command’s arguments (a note says so on stderr): a program that detaches would end the session’s only process.- A worktree that already has a Rex session is selected and not opened again, and no agent is started in it: the
run exits
1with a warning that the brief was not delivered. agent.promptedistruewhen the session was created with the command and the brief. Rex reports nothing that says the agent read it.
- With a brief and no
- Otherwise (editor opener,
autowith nothing to open in,openernone, or Herdr or Rex failing to open)agent.commandis launched detached with the brief as its last argument and the new worktree as its working directory. It is started and left running;worktreedoes not wait for it and its output does not appear here. That output is not kept anywhere either, so name an agent that keeps its own session record, such asclaude --bg(reachable throughclaude agents) orcodex exec(unless--ephemeralis passed). With a brief, noagent.commandand a run Herdr will not open (editor,none,--no-open, orherdrnot on yourPATH), the run exits2before the worktree is created, withworktree: no default for the agent command; pass `worktree config agent.command "<command>"`. If Herdr was expected to open and failed, and there is noagent.commandto fall back to, the worktree is kept and the run exits1. If the program inagent.commandcannot be started (it is not installed, say), a warning says so, the worktree is kept, noAgent startedline is printed,agentisnull, and the run exits1.
--no-agent opens the worktree as usual but starts no agent. --no-open creates the worktree and prints
Worktree created at <path> without calling Herdr, Rex or the editor, for this run only, like opener none;
an agent is still started, detached, when a brief is given, unless --no-agent says otherwise.
checkout takes --agent and follows the same rules.
Non-interactive runs
Section titled “Non-interactive runs”A run is non-interactive when stdin is not a terminal, CI is set (and is not empty, 0 or false),
--non-interactive or --yes (-y) is given, or --json is given; every command accepts the two flags. It never prompts and never
animates; progress goes to stderr as plain lines.
- A prompt with a default takes it: the issue-derived branch name. An unset
github.autoAssign,jira.autoAssignorclickup.autoAssignassigns the issue (without saving the key);falseor--no-assignstill skips it. An unsetlinear.autoStartmoves a--linearissue to started the same way, an unsetclickup.autoStartmoves a--clickuptask to in progress, and an unsetjira.autoStartmoves a--jiraissue to in progress;falseor--no-startstill skips it. - A prompt with none fails at once, exit
2, with one line on stderr:worktree: no default for <value>; pass <flag>. Here that is the branch name (<branchName>), and a--sourcewithout theorigin/prefix (--source origin/<branch>), and a--linearwith no identifier (--linear <identifier>), which is refused before the fetch and before any request. - Dependencies are installed by default, from
postCreateor the lockfile;--no-installskips it. Steps from.worktree.ymlthat you have not approved stop the run with exit2andmissing_valuebefore anything is created; the message namesworktree config --approveand--no-install. - A branch name that is already taken is refused, exit
2withinvalid_value, naming the existing worktree’s path andworktree open <branch>; it is never opened unasked. - Missing config is not offered to
config:defaultSourceBranchunset in both your git config and.worktree.ymlwarns and falls back toorigin/main.
What It Does
Section titled “What It Does”With --dry-run, steps 2 to 10 do not happen: the command stops after naming, see
Previewing a branch name.
- Fetches from
originonce, then reads the team’s.worktree.ymlfrom the default branch. A file that cannot be used stops the command here, with exit2, before anything is created. - Assigns the issue to you, when creating from
--github,--jiraor--clickupand assignment is enabled. - Creates a branch from your configured source branch (
--source, else yourdefaultSourceBranch, else the file’s, elseorigin/main). - Adds a worktree under
<repo>.worktrees/<branch-name>. - Records the issue the branch was made for, when creating from
--linear: one git config entry,branch.<branch-name>.worktreeIssueset tolinear:<identifier>(for examplelinear:ENG-123), written after the worktree exists and whether or not the issue is moved to started. Git carries the entry along when the branch is renamed and drops it when the branch is deleted. A failed write warns, sayingcleanupwill not see the branch’s Linear state, and the run carries on. A reopened branch and the other providers write nothing. - Moves the issue to started, when creating from
--linear,--clickupor--jiraand starting is enabled — see Moving the issue to started, Moving the ClickUp task to in progress and Moving the Jira issue to in progress. A failure warns and the run carries on. - Copies the gitignored env files into the new worktree, and the gitignored files the project file lists
under
copy— see Environment Files and Copy files. - Installs dependencies in it, when enabled. A failure stops here with exit
1. - Opens the worktree in your configured editor (if configured), or as a Herdr space when
openerisherdr, or as a Rex session when it isrex; withauto, in the Herdr or Rex the command runs inside (Herdr first if both, and only when its CLI is installed), else the editor; withopenernoneor--no-open, opens nothing and printsWorktree created at <path>. - Starts the coding agent and delivers the brief (with
--agent,--agent-fileor--agent-stdin), unless--no-agent— see Agent Dispatch.
A branch name that is already taken
Section titled “A branch name that is already taken”branch checks the final branch name before it assigns an issue, resolves a source or creates anything.
Git’s own a branch named … already exists does not say which of four states you are in, so branch
tells them apart:
| State | On a terminal | Non-interactive (and --json) |
|---|---|---|
The branch has a worktree under <repo>.worktrees/ |
asks whether to open it; y opens it, n exits 1 |
refuses, exit 2 (invalid_value), naming the path and worktree open <branch> |
| The branch exists but has no worktree | refuses, exit 2, saying to choose another name or delete the branch |
the same |
| The branch is checked out elsewhere, such as the main checkout | refuses, exit 2, naming the path |
the same |
| A worktree record exists but its directory was deleted | refuses, exit 2, naming the path and git worktree prune |
the same |
Opening an existing worktree creates nothing, so it copies no env files, installs nothing and assigns or moves no
issue, and --source, --assign, --start and --install are ignored. It passes --no-open, --no-agent and any
brief through to the same opener as a new worktree. Where that opener opens nothing (--no-open, opener none, or no codeEditor; auto is resolved the same way as for a new worktree), the last line says the worktree already exists rather than that it was created — Worktree already exists at <path>, or ✔ Worktree already exists at <path> with no codeEditor — and still ends on the path. With a brief, the question says that y starts a new
agent there, beside any agent already running in it. It prints no JSON document; a
non-interactive run refuses instead, so --json never reaches it.
JSON output
Section titled “JSON output”--json makes stdout carry exactly one JSON document and nothing else, on one line, for a script or
another agent to parse. It implies a non-interactive run. Everything meant for a
person goes to stderr instead: the spinner lines, the env-file list, notes and warnings.
{"path":"/abs/repo.worktrees/70-headless-agent-mode","branch":"70-headless-agent-mode","source":"origin/main", "issue":{"provider":"github","number":70,"url":"https://github.com/acme/demo/issues/70"},"assigned":true,"started":null, "envFilesCopied":["docs/.env.local"], "installed":{"ran":true,"command":"pnpm install --frozen-lockfile","steps":["pnpm install --frozen-lockfile"],"from":"lockfile","inferred":true,"ok":true}, "herdr":{"space":"w5","pane":"w5:p1","agent":"wt-70-headless-agent-mode"}, "rex":null, "agent":{"name":"demo-70-headless-agent-mode","kind":"claude","command":["claude","--name","demo-70-headless-agent-mode"],"prompted":true}, "warnings":[]}| Key | Value |
|---|---|
path, branch, source |
the worktree’s absolute path, its branch, and the ref it was created from |
issue |
{provider:"github", number, url}, {provider:"jira", key, url}, {provider:"linear", key, url} or {provider:"clickup", key, url}; null when no issue was used. url is null when it is not known |
assigned |
true once the issue is yours (assigned, or already yours); false when it is not (the attempt failed, or a Jira issue is held by someone else); null when none was (no issue, --no-assign, or a configured false) |
started |
true once the Linear issue, ClickUp task or Jira issue is in a started state (moved, or already there); false when it is not (left alone, no started state to move to, or the attempt failed); null when none was attempted (none of --linear, --clickup or --jira, --no-start, a configured false, or a declined prompt) |
envFilesCopied |
the env files and copy files copied, relative to the repository root; [] when there were none |
installed |
{ran:true, command, steps, from, inferred, ok}, plus reason when ok is false; {ran:false, reason} when the step was skipped. steps are the commands that ran, in order, and command is the one that failed, else the last that ran. from is "git-config" (your postCreate), "project" (the steps in .worktree.yml) or "lockfile" |
herdr |
{space, pane, agent} for the Herdr space that was opened; null when Herdr was not opened. agent is Herdr’s handle, null when no agent was started in the pane |
rex |
{session, block} for the Rex session that was opened; null when no Rex session was opened. block is the session’s first terminal as rex new reported it, null when the session was found instead: one that was already open, or one whose rex new timed out and turned up after. Whether an agent started is agent’s to say |
agent |
{name, kind, command, prompted} for the agent that was started; null when none was. command leaves out the brief. name is the session name, null where worktree named nothing (a detached start or a Rex session). prompted is the receipt for the brief: with Herdr the agent was given it, with Rex the session was created with it |
warnings |
what went wrong without stopping the run: a failed assignment or start, a Herdr, Rex or agent failure, missing config, a closed --github issue |
A failed install still prints the document, with installed.ok false and the message in warnings, and
exits 1: the worktree exists, so its path is reported rather than left out of an error. A brief that was
given and reached no agent is reported the same way, with agent null or agent.prompted false —
agent.prompted is the receipt that the agent has the brief (with a Rex session, that it was created with the brief).
A failure otherwise prints a different document, still one line on stdout, and exits non-zero. A line describing it goes to stderr as usual:
{"error":{"code":"missing_value","message":"no default for the branch name; pass <branchName>","details":{"value":"the branch name","flag":"<branchName>"}}}code |
Means | Exit |
|---|---|---|
missing_value |
a value with no default was not given, such as --linear with no identifier in a run that cannot ask or with --dry-run; details names it and the flag |
2 |
invalid_value |
a value or flag combination was refused: a bad branch name, an empty brief, a malformed --github, --jira, --linear or --clickup issue id, a --github, --jira or --linear URL for another repository or GitHub host, Jira site or Linear workspace or of a shape not accepted, a --github number that is a pull request, a branch name that is already taken |
2 |
not_found |
something named does not exist, such as the --source branch |
2 |
timeout |
a call that is bounded did not answer in time | 1 |
failed |
anything else | 1 |
No token or password is ever printed: URL credentials and token-shaped strings are masked in every message.