Repository navigation
feat(desktop,host-service): add right-click import of untracked worktrees - #6097
Conversation
…rees - Project context menu gains 'Import untracked worktrees', opening a confirmation dialog listing each orphan worktree (branch + path) with an opt-in 'Run setup script' checkbox (off by default) - listProjectWorktrees now annotates entries with hasWorkspace (matched by branch or normalized path) and isMainWorktree - workspaces.create gains an optional runSetup flag; false skips the setup terminal (import default)
📝 WalkthroughWalkthroughThe dashboard sidebar now discovers untracked worktrees, displays them for confirmation, and imports selected worktrees as workspaces. Imports can optionally skip setup scripts. The host service identifies main and already tracked worktrees. ChangesWorktree import
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant Sidebar
participant ActionsHook
participant HostService
participant ImportDialog
participant WorkspaceService
User->>Sidebar: Select import action
Sidebar->>ActionsHook: Discover worktrees
ActionsHook->>HostService: listProjectWorktrees
HostService-->>ActionsHook: Return eligible worktrees
ActionsHook->>ImportDialog: Show candidates
User->>ImportDialog: Confirm import
ImportDialog->>ActionsHook: Submit candidates and runSetup
ActionsHook->>WorkspaceService: Create workspaces
WorkspaceService-->>ActionsHook: Return import outcomes
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/host-service/src/trpc/router/workspaces/workspaces.ts (1)
744-777: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winGuard concurrent imports before they run unlockeds create work. The desktop import flow submits every untracked worktree with
Promise.all, and those requests arrive at theinput.worktreePathbranch without a create lock. The PR path protects this withacquireWorkspaceCreateLock; this path needs the same or documented concurrency safety.Add a per-project or per-repo guard around the
worktreePathblock (ensureMainWorkspace,git.worktree prune,adoptExistingWorktree, andenablePushAutoSetupRemote), or throttle the desktop bulk submission to one in-flight request per project while the host-side path proves safe.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/host-service/src/trpc/router/workspaces/workspaces.ts` around lines 744 - 777, Protect concurrent worktree imports by adding the existing per-project or per-repository create guard around the host-side worktreePath flow in packages/host-service/src/trpc/router/workspaces/workspaces.ts, covering ensureMainWorkspace, git.worktree prune, adoptExistingWorktree, and enablePushAutoSetupRemote; preserve the guard through the entire operation and release it reliably. In apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts, the bulk submission site requires no direct change if the host-side guard is implemented; otherwise throttle submissions to one in-flight request per project.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts (1)
138-223: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding hook-level tests for the import flow.
handleImportWorktreesandconfirmImportWorktreescontain non-trivial async logic: filtering, concurrent submission, outcome aggregation, and re-entrancy guarding. No test file accompanies this hook. Adding tests for the untracked-filter, the partial-failure aggregation, and the concurrent-guard would reduce regression risk for this new flow.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts` around lines 138 - 223, The import flow lacks hook-level coverage for its key async behaviors. Add tests for useDashboardSidebarProjectSectionActions, covering handleImportWorktrees filtering only eligible untracked non-main worktrees, confirmImportWorktrees aggregating concurrent submission outcomes with partial failures, and both methods’ re-entrancy guards preventing duplicate work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts`:
- Around line 202-216: Update the partial-failure toast in the worktree import
action around the outcomes/error aggregation to surface more than errors[0].
Include the failure count or combine distinct error messages so users can see
details for all failed imports while preserving the existing success behavior.
---
Outside diff comments:
In `@packages/host-service/src/trpc/router/workspaces/workspaces.ts`:
- Around line 744-777: Protect concurrent worktree imports by adding the
existing per-project or per-repository create guard around the host-side
worktreePath flow in
packages/host-service/src/trpc/router/workspaces/workspaces.ts, covering
ensureMainWorkspace, git.worktree prune, adoptExistingWorktree, and
enablePushAutoSetupRemote; preserve the guard through the entire operation and
release it reliably. In
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts,
the bulk submission site requires no direct change if the host-side guard is
implemented; otherwise throttle submissions to one in-flight request per
project.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts`:
- Around line 138-223: The import flow lacks hook-level coverage for its key
async behaviors. Add tests for useDashboardSidebarProjectSectionActions,
covering handleImportWorktrees filtering only eligible untracked non-main
worktrees, confirmImportWorktrees aggregating concurrent submission outcomes
with partial failures, and both methods’ re-entrancy guards preventing duplicate
work.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5da38ee1-9717-4726-a63f-cedb8be2d355
📒 Files selected for processing (7)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/components/DashboardSidebarProjectContextMenu/DashboardSidebarProjectContextMenu.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/components/ImportWorktreesDialog/ImportWorktreesDialog.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/components/ImportWorktreesDialog/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.tspackages/host-service/src/trpc/router/workspace-creation/procedures/list-project-worktrees.tspackages/host-service/src/trpc/router/workspaces/workspaces.ts
| const errors = outcomes.flatMap((outcome) => | ||
| outcome.ok ? [] : [outcome.error], | ||
| ); | ||
| const imported = outcomes.length - errors.length; | ||
| if (errors.length > 0) { | ||
| toast.error( | ||
| `Imported ${imported} of ${untracked.length} worktrees: ${errors[0]}`, | ||
| ); | ||
| } else { | ||
| toast.success( | ||
| imported === 1 | ||
| ? "Imported 1 worktree as a workspace" | ||
| : `Imported ${imported} worktrees as workspaces`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Surface more than the first error on partial import failure.
When several worktrees fail for different reasons, errors[0] hides every failure except the first. Users cannot tell whether the other failures share the same cause.
Include the failure count or join distinct error messages instead of showing only the first one.
💬 Proposed fix to surface more failure detail
if (errors.length > 0) {
+ const uniqueErrors = [...new Set(errors)];
toast.error(
- `Imported ${imported} of ${untracked.length} worktrees: ${errors[0]}`,
+ `Imported ${imported} of ${untracked.length} worktrees: ${uniqueErrors.join("; ")}`,
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const errors = outcomes.flatMap((outcome) => | |
| outcome.ok ? [] : [outcome.error], | |
| ); | |
| const imported = outcomes.length - errors.length; | |
| if (errors.length > 0) { | |
| toast.error( | |
| `Imported ${imported} of ${untracked.length} worktrees: ${errors[0]}`, | |
| ); | |
| } else { | |
| toast.success( | |
| imported === 1 | |
| ? "Imported 1 worktree as a workspace" | |
| : `Imported ${imported} worktrees as workspaces`, | |
| ); | |
| } | |
| const errors = outcomes.flatMap((outcome) => | |
| outcome.ok ? [] : [outcome.error], | |
| ); | |
| const imported = outcomes.length - errors.length; | |
| if (errors.length > 0) { | |
| const uniqueErrors = [...new Set(errors)]; | |
| toast.error( | |
| `Imported ${imported} of ${untracked.length} worktrees: ${uniqueErrors.join("; ")}`, | |
| ); | |
| } else { | |
| toast.success( | |
| imported === 1 | |
| ? "Imported 1 worktree as a workspace" | |
| : `Imported ${imported} worktrees as workspaces`, | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts`
around lines 202 - 216, Update the partial-failure toast in the worktree import
action around the outcomes/error aggregation to surface more than errors[0].
Include the failure count or combine distinct error messages so users can see
details for all failed imports while preserving the existing success behavior.
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts:153">
P2: On hosts that predate the new `hasWorkspace`/`isMainWorktree` fields, `worktree.hasWorkspace === false` is `false` because the field is `undefined`, so the list is treated as fully tracked and users get the 'All of this project's worktrees are already tracked' toast. That message is misleading because the real cause is host-version incompatibility, not zero untracked worktrees. Consider detecting the old-host case (e.g., no worktree carries the new fields) and showing a dedicated 'update your host' message instead of a false 'all tracked' success toast.</violation>
</file>
Architecture diagram
sequenceDiagram
actor User
participant Sidebar as Sidebar UI
participant Menu as Project Context Menu
participant Dialog as Import Worktrees Dialog
participant Hook as useDashboardSidebarProjectSectionActions
participant Store as useWorkspaceCreates
participant Client as Host Service Client
participant Host as Host Service tRPC
participant Git as Git Worktree List
participant DB as Workspaces DB
participant Setup as Setup Terminal
User->>Sidebar: Right-click project
Sidebar->>Menu: Open context menu
User->>Menu: Click "Import untracked worktrees"
Menu->>Hook: handleImportWorktrees()
alt No serving host URL
Hook-->>User: Toast: host unreachable
else Host reachable
Hook->>Client: workspaceCreation.listProjectWorktrees.query({ projectId })
Client->>Host: tRPC query listProjectWorktrees
Host->>Git: listGitWorktrees()
Git-->>Host: worktree records
Host->>DB: SELECT workspaces WHERE projectId
DB-->>Host: workspace rows
Host->>Host: Annotate hasWorkspace by branch/path + isMainWorktree
Host-->>Client: { worktrees }
Client-->>Hook: worktrees
Hook->>Hook: Filter untracked worktrees
alt No untracked worktrees
Hook-->>User: Toast: all worktrees already tracked
else Untracked worktrees exist
Hook->>Dialog: Open with branch + path list
User->>Dialog: Optionally check "Run setup script"
User->>Dialog: Confirm import
Dialog->>Hook: onConfirm({ runSetup })
Hook->>Hook: Set importing state
loop For each untracked worktree
Hook->>Store: submit({ snapshot: { projectId, branch, worktreePath, runSetup } })
Store->>Client: workspaces.create mutation
Client->>Host: tRPC create workspace
Host->>DB: Create workspace row for worktreePath
alt runSetup !== false
Host->>Setup: Start setup terminal
Setup-->>Host: setup completes
else runSetup === false
Note over Host,Setup: Run setup skipped for already-set-up worktree
end
Host-->>Client: result
Client-->>Store: outcome
Store-->>Hook: completed
end
Hook->>Hook: Aggregate outcomes
alt All imports succeeded
Hook-->>User: Toast: Imported N worktrees
else Some imports failed
Hook-->>User: Toast: Imported X of Y + first error
end
Hook->>Dialog: Close
end
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| projectId: project.id, | ||
| }); | ||
| // `=== false` also skips hosts too old to report tracked-ness. | ||
| const untracked = worktrees.filter( |
There was a problem hiding this comment.
P2: On hosts that predate the new hasWorkspace/isMainWorktree fields, worktree.hasWorkspace === false is false because the field is undefined, so the list is treated as fully tracked and users get the 'All of this project's worktrees are already tracked' toast. That message is misleading because the real cause is host-version incompatibility, not zero untracked worktrees. Consider detecting the old-host case (e.g., no worktree carries the new fields) and showing a dedicated 'update your host' message instead of a false 'all tracked' success toast.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/hooks/useDashboardSidebarProjectSectionActions/useDashboardSidebarProjectSectionActions.ts, line 153:
<comment>On hosts that predate the new `hasWorkspace`/`isMainWorktree` fields, `worktree.hasWorkspace === false` is `false` because the field is `undefined`, so the list is treated as fully tracked and users get the 'All of this project's worktrees are already tracked' toast. That message is misleading because the real cause is host-version incompatibility, not zero untracked worktrees. Consider detecting the old-host case (e.g., no worktree carries the new fields) and showing a dedicated 'update your host' message instead of a false 'all tracked' success toast.</comment>
<file context>
@@ -124,6 +133,94 @@ export function useDashboardSidebarProjectSectionActions({
+ projectId: project.id,
+ });
+ // `=== false` also skips hosts too old to report tracked-ness.
+ const untracked = worktrees.filter(
+ (worktree) =>
+ worktree.hasWorkspace === false && worktree.isMainWorktree === false,
</file context>
* docs: weekly update for PRs merged 2026-07-29 to 2026-08-05 - tasks.mdx: Tasks and Pull requests are now separate sidebar destinations with independent persisted filters; GitHub Issues as a Tasks source (#5975) - workspaces.mdx: prompt history popover (#6083), typed-name-seeds-branch behavior (#6129), right-click import of untracked worktrees (#6097), bulk workspace actions section (#6021) - automations.mdx: inline Retry and Retry all for failed runs (#6139) - keyboard-shortcuts.mdx: Open Command Palette ⌘⇧K and Check Resources ⌘⇧U (#6037) - setup-teardown-scripts.mdx: experimental wait-for-setup-before-agents setting (#5925) * docs(recipes): add agent-driven Superset recipe for the shipped superset:* skills Covers the workflow from #6181: standup, automate, doctor, setup, feedback, and orchestrate driven from the agent you're already in. * docs(recipes): focus agent-driven recipe on orchestrate and automate, defer to skills page * docs(recipes): defer orchestrate teaching to its page, center recipe on the automate arc * docs: address review — branch name is slugified, retry scoping, Linear source wording
What
Adds an Import untracked worktrees action to the project right-click menu in the v2 sidebar. It lists every git worktree of the project's repo that has no workspace row and imports them as workspaces via the existing
workspaces.createadopt path (nogit worktree add).Clicking the menu item opens a confirmation dialog showing the count and the list (branch + path) of worktrees that will be imported, plus a Run setup script checkbox that defaults to off — imported worktrees are usually already set up.
How
workspaceCreation.listProjectWorktrees: entries now carryhasWorkspace(workspace row matches by branch — the canonical tracking key — or by realpath-normalized path, catching rows whose branch was renamed) andisMainWorktree(never offer the main repo checkout). Additive, so the v1→v2 importer callers are unaffected.workspaces.create: new optionalrunSetupinput;falseskips the setup terminal. All existing callers omit it, so behavior is unchanged for them. Old hosts strip the unknown key and keep today's behavior.ImportWorktreesDialog(borderless rows matching the V1 ImportRow pattern, truncation withtitletooltips,max-h-64scroll withoverscroll-contain); the menu action fetches the untracked list and opens it; confirm submits each viauseWorkspaceCreates().submitwithrunSetup, so optimistic sidebar rows, pane layout, and failure cleanup are handled by the existing pipeline. Re-running when everything is tracked toasts instead of opening the dialog; adoption is idempotent server-side.Verification
CDP-driven end-to-end against the dev app from this branch (instance ownership + session verified):
.superset/setup.shdid not run.bun run lint, monorepo typecheck, and host-service workspace-creation/workspaces suites (incl. the main-loop-blocking ratchet) all green.Summary by cubic
Add a right-click “Import untracked worktrees” action in the v2 desktop sidebar to adopt existing git worktrees as workspaces, with an optional setup step. This avoids creating new worktrees and makes adoption quick and safe.
desktop: Project context menu gains “Import untracked worktrees,” opening a dialog that lists each untracked worktree (branch + path) and a “Run setup script” checkbox (off by default). If none are untracked, a toast is shown; otherwise, each is created via the existing create flow with optimistic UI; main checkout is excluded.host-service:workspaceCreation.listProjectWorktreesnow includeshasWorkspace(matched by branch or normalized path) andisMainWorktreeto support filtering.host-service:workspaces.createaccepts optionalrunSetup; whenfalse, the setup terminal is skipped. Existing callers are unchanged; older hosts ignore unknown input.Written for commit ac905b7. Summary will update on new commits.
Summary by CodeRabbit