Skip to main content
Tutorials Last updated: 31 March 2026

Projects and branches

This tutorial shows you how to register repos as projects and run tasks on feature branches — all from Telegram, without touching a terminal. Jump between re...

This tutorial shows you how to register repos as projects and run tasks on feature branches — all from Telegram, without touching a terminal. Jump between repos from wherever you are; your machine handles the checkout.

What you’ll learn: How to target repos from anywhere with /<project-alias>, and run on branches with @branch.

The problem

So far, Untether runs in whatever directory you started it. If you want to work on a different repo, you have to:

  1. Stop Untether
  2. cd to the other repo
  3. Restart Untether

Projects fix this. Once you register a repo, you can target it from chat—even while Untether is running elsewhere.

Quick background: branches and worktrees

Already familiar with git branches? Skip to step 1.

A branch is a separate line of development in your code. Think of it like a draft — you can make changes on a branch without touching the main version. When the changes are ready, the branch gets merged back. Branches let your agent work on a feature (feat/new-login) or fix (fix/memory-leak) in isolation.

A worktree is a separate folder that checks out a branch. Normally, switching branches changes the files in your project directory. With worktrees, each branch gets its own folder — so the agent can work on feat/new-login in one folder while your main code stays untouched in another. Untether creates and manages worktrees for you automatically.

You don’t need to understand git deeply to use projects and branches. The key idea: prefixing a message with /<project> @branch runs the agent on that branch, in a separate folder, without disrupting anything.

See also: glossary for definitions of these and other terms.

1. Register a project

Navigate to the repo and run untether init:

cd ~/dev/happy-gadgets
untether init happy-gadgets

Output:

saved project 'happy-gadgets' to ~/.untether/untether.toml

This adds an entry to your config (Untether also fills in defaults like worktrees_dir, default_engine, and sometimes worktree_base):

=== “untether config”

```sh
untether config set projects.happy-gadgets.path "~/dev/happy-gadgets"
```

=== “toml”

```toml
[projects.happy-gadgets]
path = "~/dev/happy-gadgets"
```

Project aliases are also Telegram commands The alias becomes a /command you can use in chat. Keep them short and lowercase: myapp, backend, docs.

2. Target a project from chat

Now you can start Untether from another repo. If you don’t specify a project, Untether runs in the directory where you launched it.

cd ~/dev/your-project
untether

And target the project by prefixing your message:

You /happy-gadgets explain the authentication flow

Untether runs the agent in ~/dev/happy-gadgets, not your current directory.

The response includes a context footer:

Untether dir: happy-gadgets codex resume abc123

/<project> command and response with ctx: footer

That dir: line tells you which project is active. When you reply, Untether automatically uses the same project—you don’t need to repeat /happy-gadgets.

3. Set up worktrees

Worktrees let you run tasks on feature branches without touching your main checkout. Instead of git checkout, Untether creates a separate directory for each branch.

Add worktree config to your project:

=== “untether config”

```sh
untether config set projects.happy-gadgets.path "~/dev/happy-gadgets"
untether config set projects.happy-gadgets.worktrees_dir ".worktrees"
untether config set projects.happy-gadgets.worktree_base "main"
```

=== “toml”

```toml
[projects.happy-gadgets]
path = "~/dev/happy-gadgets"
worktrees_dir = ".worktrees"      # where branches go
worktree_base = "main"            # base for new branches
```

Ignore the worktrees directory Add .worktrees/ to your global gitignore so it doesn’t clutter git status:

echo ".worktrees/" >> ~/.config/git/ignore

4. Run on a branch

Use @branch after the project:

You /happy-gadgets @feat/new-login add rate limiting to the login endpoint

Untether:

  1. Checks if .worktrees/feat/new-login exists (and is a worktree)
  2. If the branch exists locally, it adds a worktree for it
  3. If the branch doesn’t exist, it creates it from worktree_base (or the repo default) and adds the worktree
  4. Runs the agent in that worktree

The response shows both project and branch:

Untether dir: happy-gadgets @feat/new-login codex resume xyz789

Replies stay on the same branch. Your main checkout is untouched.

5. Context persistence

Once you’ve set a context (via /<project-alias> @branch or by replying), it sticks:

You /happy-gadgets @feat/new-login add tests

Untether dir: happy-gadgets @feat/new-login

reply to the bot’s answer also add integration tests

Untether dir: happy-gadgets @feat/new-login

The dir: line in each message carries the context forward.

6. Set a default project

If you mostly work in one repo, set it as the default:

=== “untether config”

```sh
untether config set default_project "happy-gadgets"
```

=== “toml”

```toml
default_project = "happy-gadgets"
```

Now messages without a /<project-alias> prefix go to that repo:

You add a health check endpoint

Goes to happy-gadgets automatically.

Putting it together

Here’s a typical workflow:

untether

You /happy-gadgets review the error handling

You /happy-gadgets @feat/caching implement caching

Untether dir: happy-gadgets @feat/caching

You also add cache invalidation

You /backend @fix/memory-leak profile memory usage

You /happy-gadgets bump the version number

All from the same Telegram chat, without restarting Untether or changing directories.

Project config reference

Full options for [projects.<alias>]:

KeyDefaultDescription
path(required)Repo root. Expands ~.
worktrees_dir.worktreesWhere branch worktrees are created (relative to the project path).
worktree_basenullBase branch for new worktrees. If unset, Untether uses origin/HEAD, the current branch, or master/main (in that order).
default_enginenullEngine to use for this project (overrides global default).
chat_idnullBind a Telegram chat/group to this project.

Troubleshooting

“unknown project”

Run untether init <alias> in the repo first.

Branch worktree not created

Make sure the worktrees directory (default .worktrees) is writable. If you’ve customized worktrees_dir, verify that path exists or can be created.

Context not carrying forward

Make sure you’re replying to a message with a dir: line. If you send a new message (not a reply), context resets unless you have a default_project.

Worktree conflicts with existing branch

If the branch already exists locally, Untether uses it. For a fresh start, delete the worktree and the branch, or pick a new branch name.

Next

You’ve got projects and branches working. The final tutorial covers using multiple engines effectively.

Multi-engine workflows →

Was this helpful?

Related Articles