Skip to content

feat(launch): pick the host, the worktree base, or an existing checkout - #10

Open
alessandrocornacchia wants to merge 4 commits into
alysnnix:mainfrom
alessandrocornacchia:feat/remote-host-launch
Open

alessandrocornacchia wants to merge 4 commits into
alysnnix:mainfrom
alessandrocornacchia:feat/remote-host-launch

Conversation

@alessandrocornacchia

@alessandrocornacchia alessandrocornacchia commented Oct 12, 2026 •

Copy link
Copy Markdown

What this changes

Send to chat can now put the agent where the work for a card actually is. That might be another machine, a branch other than the default, or a worktree that already exists. Until now it only offered two options, both on the board's own host:

  • Local, the project's main checkout, on whatever branch it happens to have checked out; or
  • New worktree, always cut on a new branch from the default branch.

This PR adds three choices to the dialog. It also fixes the plugin failing to load on current Paseo apps.

  1. Which host runs the agent, when more than one Paseo host is connected.
  2. Where a new worktree starts: any branch of the repository, or a pull request's own branch. You also choose whether to cut a new branch from it or check that branch out as it is.
  3. Existing checkout: send the agent into a worktree or branch that is already on the host, without creating anything.

Why

The motivating case: you want an agent to continue a pull request on a remote machine where its branch, or a worktree for it, already exists. It might have been made by an earlier session, by hand, or by another tool. Today the dialog gives you two bad options:

  • Local drops the agent into the main checkout, which may be on an unrelated branch. You don't control which.
  • New worktree cuts another worktree on another branch, forked from the base. Now the same work lives on two branches: the one you had and a new fuzzy-otter branch the agent is committing to.

The new-worktree path has the same problem in a quieter form. Choosing a base branch always meant a new branch on top of it, never "just check this branch out". And reading the 0.11.1 daemon source turned up two cases where it silently forks instead of failing. This PR exists to avoid both:

You ask the daemon for… …and it does
a worktree checking out branch X, while X is already checked out in some worktree quietly creates X-1 from X and checks that out
a worktree for pull request #N, while a local branch with the PR's head name already exists checks the PR out as <head>-1

In both cases you end up with a duplicate branch you didn't ask for.

How it works

The dialog's first row now reads like this (a layout sketch, not a screenshot; see UI below):

[ host ▾ ]  [ New worktree ▾ ]  [ main ▾ ]  [ Same branch ▾ ]      ← new worktree from a branch
[ host ▾ ]  [ Existing checkout ▾ ]  [ feat/x ▾ ]                  ← join a checkout as it stands

Choose the host

  • The host chip appears only when more than one host is connected, so a single-host setup sees the same dialog as before.
  • The board's own host still launches through board.send-to-chat.
  • Any other online host is reached from the dialog with getPaseoClient(serverId). Its project is the one whose key is the card's repository.
  • Creating the workspace, the agent and the timeline row moved into one shared function, launchCard in shared/launch.ts, so both paths behave the same.

Choose where a new worktree starts

  • The base chip lists the repository's branches from GitHub (the default first, then the 100 most recently committed). On a pull request it also offers PR #N branch.
  • New branch (the old behaviour): the base is requested as refs/remotes/<remote>/<branch> and fetched first, so the agent starts at the remote tip, not at a stale local default branch. On the board's host the remote is whichever points at the card's repository, so a fork's upstream works.
  • Same branch (new): the daemon's checkout-branch action (action: "checkout" with a bare refName) checks the branch itself out, with nothing on top. If there is no local branch of that name yet, the board's host first creates it from the card's repository.
  • Guard against the silent forks:
    • If the branch is already checked out somewhere, the dialog refuses to send. It names the checkout to pick under Existing checkout. The board's host checks again with git worktree list when the send runs.
    • If a pull request's head branch already exists locally, the board's host checks out that branch instead of asking for a -1 copy.
    • To support this, board.list-branches now also returns the pull request's head branch. It returns null for a PR from a fork, where the head name says nothing about which local branch tracks it.

Join an existing checkout

  • Existing checkout is a third isolation choice, next to Local and New worktree. It lists the checkouts of the project on the chosen host, with the branch each one is on. It preselects the one on the pull request's head branch, or the main checkout otherwise.
  • Sending creates no worktree and no branch. If Paseo already has a workspace open on that folder, the agent joins that workspace (workspaces.ref(id)) instead of opening a second one beside it. Otherwise a directory workspace is opened on the folder.
  • On the board's host the list comes from git worktree list (new handler board.list-checkouts), so worktrees made by any tool appear. On other hosts the dialog can't run git, so it lists the checkouts Paseo has a workspace for, plus the project root.

Remembered choices

  • The last host is saved with the other launch defaults.
  • The last base, including Same/New branch, is saved per repository, separately for pull requests and for other cards.
  • New fields are optional when read back, so a settings.json written by an older build still parses.

Fix: the plugin failed to load

The sidebar item, settings screen, command and timeline row used the Github Lucide icon, which newer Paseo apps no longer ship. Loading failed with Unknown Lucide icon: Github. They now use GitBranch.

Known limits (documented in the README)

These all apply to hosts other than the board's own:

  • The project's origin must be the card's repository.
  • A base branch must have been fetched there at least once, or the dialog says to run git fetch.
  • Existing checkout lists only the checkouts Paseo has a workspace for. A worktree another tool made there appears once Paseo has opened it.
  • A pull request whose branch is already local but not checked out still becomes <head>-1, because the dialog can't see local branches there. Picking that branch with Same branch avoids it.

Checks

  • npm run typecheck passes
  • npm run lint passes, including the project rules in scripts/
  • npm test passes (70 tests; shared/launch.test.ts covers the start kinds, joining or opening an existing checkout, parsing git worktree list, matching checkouts to workspaces, and the conflict guard)
  • paseo plugin reload github-integration loads cleanly against a real daemon (0.11.1)

Also run against real GitHub and git:

  • the new board.list-branches query: a same-repo PR returns its head branch; a fork PR and an issue return null;
  • git worktree list parsing on a real checkout;
  • branch listing, picking upstream and fetching an unseen branch in a fork-style clone, and saving and reading back remembered bases.

The daemon's behaviour for each start kind, including the two silent forks above, was read from the 0.11.1 and 0.8.0 daemon sources. No end-to-end send has been made against a live daemon yet.

UI

  • Not applicable
  • Checked on desktop (web export, V8)
  • Checked on iOS / Android (Hermes)

Not checked visually yet, and there are no screenshots. All new controls reuse the dialog's existing ControlChip and ChoicePopover. One thing worth a look on compact: a row can now hold four chips (host, isolation, base, branch mode). Screenshots of the base chip, the Same/New branch chip, the Existing checkout menu and the "already checked out" message will be added after a manual pass on desktop and mobile.

Paseo version

The plugin still declares >=0.8.0.

  • Host picker: useHosts and getPaseoClient first appear in Paseo 0.9.0. They are read off the SDK module object, so on 0.8 the chip is simply absent and everything else works.
  • Base branch, PR checkout, Same branch: the worktree action, refName and checkoutSource fields are in the 0.8.0 protocol, and the 0.8.0 daemon already resolves a bare refName checkout to checkout-branch.
  • Existing checkout: workspaces.list({ filter: { projectId } }), workspaces.ref(id) and the workspace's gitRuntime.currentBranch are all in the 0.8.0 client and protocol.
  • Dev dependencies move to @getpaseo/* 0.11.1 for the typings.

🤖 Generated with Claude Code

alessandrocornacchia and others added 4 commits October 12, 2026 09:17
- Newer Paseo apps ship a Lucide set without brand icons, so the plugin
  failed to load with "Unknown Lucide icon: Github"
- Use GitBranch for the sidebar item, settings screen, command and
  timeline row

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The multi-host client API (useHosts, getPaseoClient) is not in the
  0.8 typings

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Add a host chip to the launch dialog when more than one host is
  connected; other hosts are reached with getPaseoClient and matched
  by project key, falling back gracefully on Paseo before 0.9.0
- Move workspace and agent creation into shared launchCard, used by
  the daemon handler and by the dialog for remote hosts
- Add a base chip for new worktrees: a branch read from GitHub, or a
  pull request's own branch checked out by the daemon
- Request the base as a remote-tracking ref so it is fetched first;
  on the board's host, fetch it from the remote for the card's repo
- Remember the last host, and the last base per repository
- Document both in the README and CHANGELOG

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Add Existing checkout, a third isolation that sends the agent into a
  worktree or branch already on the host, as it stands: no new worktree,
  no new branch. The agent joins the Paseo workspace already open on that
  directory, or a directory workspace is opened on it
- List checkouts with git worktree list on the board's host, so worktrees
  made by other tools show up, and from Paseo's workspace list elsewhere;
  preselect the one on the pull request's head branch
- Add a Same branch / New branch chip to a branch base: Same branch checks
  the branch itself out with the daemon's checkout-branch action, creating
  the local branch from the card's repository when it is missing
- Refuse a worktree on a branch some checkout already holds, which the
  daemon would otherwise fork into <branch>-1, and check a pull request
  out as its local head branch rather than as <head>-1
- Return the pull request's head branch from board.list-branches
- Document both in the README and CHANGELOG

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alessandrocornacchia alessandrocornacchia changed the title feat(launch): choose the host and worktree base feat(launch): pick the host, the worktree base, or an existing checkout Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant