fix: allow v2 project setup on selected remote host#4665
Conversation
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
📝 WalkthroughWalkthroughThis PR extends multi-host project management across the desktop application and CLI. The desktop workflow now routes host context through project settings navigation, resolves target host details in settings components, and handles remote project import and clone operations with conditional UI and conflict detection. The CLI gains per-host project status reporting and flexible project ID input via optional flags. ChangesMulti-host project management via desktop settings and CLI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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 |
|
Ready to review this PR? Stage has broken it down into 4 individual chapters for you:
Chapters generated by Stage for commit 3bb34e3 on May 17, 2026 2:13pm UTC. |
Greptile SummaryThis PR threads the selected remote host context from the New Workspace modal into v2 project settings, so that clicking "Set up project…" on a remote host opens the correct host-aware settings page. The CLI gains
Confidence Score: 3/5The desktop changes are safe; the CLI list change breaks bare invocations when the host service is not running. The desktop side correctly threads host context end-to-end and handles remote vs local UI branching well. The CLI setup refactor is clean. However, projects list now unconditionally contacts the local host service manifest — any environment where the host service is not running (CI, scripting, a fresh machine) will receive a hard error from a command that previously worked offline. That regression is the main concern in an otherwise solid PR. packages/cli/src/commands/projects/list/command.ts — the host query should be conditional on explicit --host/--local flags
|
| Filename | Overview |
|---|---|
| packages/cli/src/commands/projects/list/command.ts | Adds per-host setup status columns but unconditionally calls resolveHostTarget even without --host/--local flags, breaking bare invocations when the local host service is not running |
| packages/cli/src/commands/projects/setup/command.ts | Adds --project flag and --path alias for --import; changes requireHostTarget to resolveHostFilter so host defaults to local; conflict-detection and error messages look correct |
| apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx | Threads draft.hostId into the project settings navigation so the correct remote host context is preserved when opening setup from the workspace modal |
| apps/desktop/src/renderer/routes/_authenticated/settings/projects/$projectId/page.tsx | Adds validateSearch for hostId query param and passes it through to V2ProjectSettings; correct and straightforward |
| apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx | Resolves target host URL/name from the passed hostId and passes it downstream; logic for local vs remote distinction and query key updating looks correct |
| apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx | Remote path input UI and import/clone handlers look correct; conflict-dialog Open project navigation loses hostId context |
Sequence Diagram
sequenceDiagram
participant Modal as New Workspace Modal
participant Page as /settings/projects/$projectId
participant V2Settings as V2ProjectSettings
participant PLS as ProjectLocationSection
participant Host as Host Service (local or relay)
Modal->>Page: "navigate({ hostId })"
Page->>Page: validateSearch to hostId
Page->>V2Settings: V2ProjectSettings hostId
V2Settings->>V2Settings: useHostUrl(hostId) to targetHostUrl
V2Settings->>Host: project.get.query
V2Settings->>PLS: hostUrl, hostName, isRemoteTarget
alt isRemoteTarget
PLS->>PLS: show text inputs
PLS->>Host: project.findBackfillConflict.query
PLS->>Host: project.setup.mutate
else local
PLS->>PLS: show native directory picker
PLS->>Host: project.findBackfillConflict.query
PLS->>Host: project.setup.mutate
end
Comments Outside Diff (1)
-
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx, line 373-384 (link)Conflict-dialog "Open project" navigation drops host context
When
handleImport(remote path) finds a conflict and the user clicks "Open project", the navigation navigates to the conflicting project's settings page without asearch: { hostId }param. This means the conflicting project opens in local/default host context. If the user was configuring a remote host, they now need to manually re-select that host in the opened project's settings, which is confusing and inconsistent with the flow that brought them here.Prompt To Fix With AI
This is a comment left during a code review. Path: apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx Line: 373-384 Comment: **Conflict-dialog "Open project" navigation drops host context** When `handleImport` (remote path) finds a conflict and the user clicks "Open project", the navigation navigates to the conflicting project's settings page without a `search: { hostId }` param. This means the conflicting project opens in local/default host context. If the user was configuring a remote host, they now need to manually re-select that host in the opened project's settings, which is confusing and inconsistent with the flow that brought them here. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/cli/src/commands/projects/list/command.ts:28-33
**`list` now always requires a running host service**
`resolveHostTarget` unconditionally reads the local host manifest and throws `CLIError("Host service for this machine isn't running")` when the service is not up. Before this PR, `superset projects list` only contacted the API and worked regardless of host state. Any user who runs `superset projects list` without `--host`/`--local` and without a running host service will now get a confusing error even though they only wanted a project listing. The `SET UP` and `PATH` columns should only be populated when a host filter is explicitly requested, keeping the no-flag path purely API-backed.
### Issue 2 of 2
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx:373-384
**Conflict-dialog "Open project" navigation drops host context**
When `handleImport` (remote path) finds a conflict and the user clicks "Open project", the navigation navigates to the conflicting project's settings page without a `search: { hostId }` param. This means the conflicting project opens in local/default host context. If the user was configuring a remote host, they now need to manually re-select that host in the opened project's settings, which is confusing and inconsistent with the flow that brought them here.
Reviews (1): Last reviewed commit: "fix: allow project setup on selected hos..." | Re-trigger Greptile
| const target = resolveHostTarget({ | ||
| requestedHostId: hostId, | ||
| organizationId, | ||
| userJwt: ctx.bearer, | ||
| }); | ||
| const hostProjects = await target.client.project.list.query(); |
There was a problem hiding this comment.
list now always requires a running host service
resolveHostTarget unconditionally reads the local host manifest and throws CLIError("Host service for this machine isn't running") when the service is not up. Before this PR, superset projects list only contacted the API and worked regardless of host state. Any user who runs superset projects list without --host/--local and without a running host service will now get a confusing error even though they only wanted a project listing. The SET UP and PATH columns should only be populated when a host filter is explicitly requested, keeping the no-flag path purely API-backed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/cli/src/commands/projects/list/command.ts
Line: 28-33
Comment:
**`list` now always requires a running host service**
`resolveHostTarget` unconditionally reads the local host manifest and throws `CLIError("Host service for this machine isn't running")` when the service is not up. Before this PR, `superset projects list` only contacted the API and worked regardless of host state. Any user who runs `superset projects list` without `--host`/`--local` and without a running host service will now get a confusing error even though they only wanted a project listing. The `SET UP` and `PATH` columns should only be populated when a host filter is explicitly requested, keeping the no-flag path purely API-backed.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx (1)
26-33:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve target host context when opening a conflicting project.
The conflict action navigates without
hostId, so remote setup conflicts can reopen project settings on the wrong (local) host context.🔧 Proposed fix
interface ProjectLocationSectionProps { projectId: string; currentPath: string | null; repoCloneUrl: string | null; + hostId: string | null; hostUrl: string | null; hostName: string; isRemoteTarget: boolean; onChanged?: () => void; } export function ProjectLocationSection({ projectId, currentPath, repoCloneUrl, + hostId, hostUrl, hostName, isRemoteTarget, onChanged, }: ProjectLocationSectionProps) { @@ navigate({ to: "/settings/projects/$projectId", params: { projectId: target.id }, + search: { hostId: hostId ?? undefined }, });Also pass
hostId={targetHostId ?? null}fromV2ProjectSettingswhen renderingProjectLocationSection.Also applies to: 36-44, 380-383
🤖 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/settings/v2-project/`$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx around lines 26 - 33, The ProjectLocationSection currently lacks a hostId prop so conflict navigation loses remote host context; update the ProjectLocationSectionProps interface to include hostId: string | null, update the ProjectLocationSection component to accept and use that prop when constructing the conflict navigation action, and when V2ProjectSettings renders <ProjectLocationSection> pass hostId={targetHostId ?? null} (and likewise where else ProjectLocationSection is instantiated around the indicated ranges) so the conflict handler preserves hostId during navigation.
🧹 Nitpick comments (1)
packages/cli/src/commands/projects/list/command.ts (1)
23-45: ⚡ Quick winGracefully degrade when the host project lookup fails.
Line 33 makes
projects listfail hard if the selected host is offline or unreachable, even though the organization projects were already fetched on Line 23. Catching that lookup and rendering setup/path as unknown would preserve the old listing behavior while still enriching the happy path.🤖 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/cli/src/commands/projects/list/command.ts` around lines 23 - 45, The host-side project lookup (target.client.project.list.query invoked after resolveHostTarget) can throw and should be caught so the command still returns the organization projects; wrap the host lookup in a try/catch (around the call to target.client.project.list.query and subsequent hostProjects mapping) and on error set hostProjects to an empty array (or keep hostProjectById empty) and optionally log/debug the error; ensure the final returned mapping in the projects.map still runs and uses "unknown"/"-" or the existing "no"/"-" semantics (setUp: "no" or "unknown" and path: "-" or "unknown") when hostProject is missing so the list gracefully degrades when the host is unreachable.
🤖 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/settings/v2-project/`$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx:
- Around line 365-369: The AlertDialogDescription text in ProjectLocationSection
is not selectable due to global user-select: none; update the
AlertDialogDescription element (in the ProjectLocationSection component) to
include explicit selectable classes (e.g., add "select-text cursor-text" or your
project's equivalent class names) so the conflict/error message text can be
copied by users; ensure the class is applied to the same element rendering the
string "{conflict?.name ?? ""}" so the entire message becomes selectable.
In `@packages/cli/src/commands/projects/setup/command.ts`:
- Around line 27-38: The current validation allows both options.project and
args.id to be provided if they match; change the logic in the project ID
validation so that if both sources are present (options.project and args.id) you
always throw a CLIError indicating duplicate project ID sources (regardless of
equality), ensuring exactly one source is used; keep the existing projectId
extraction (const projectId = (options.project ?? args.id) as string |
undefined) and replace the conditional that checks options.project && args.id &&
options.project !== args.id with a simple options.project && args.id check that
throws the same "Project ID specified twice" CLIError.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/`$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx:
- Around line 26-33: The ProjectLocationSection currently lacks a hostId prop so
conflict navigation loses remote host context; update the
ProjectLocationSectionProps interface to include hostId: string | null, update
the ProjectLocationSection component to accept and use that prop when
constructing the conflict navigation action, and when V2ProjectSettings renders
<ProjectLocationSection> pass hostId={targetHostId ?? null} (and likewise where
else ProjectLocationSection is instantiated around the indicated ranges) so the
conflict handler preserves hostId during navigation.
---
Nitpick comments:
In `@packages/cli/src/commands/projects/list/command.ts`:
- Around line 23-45: The host-side project lookup
(target.client.project.list.query invoked after resolveHostTarget) can throw and
should be caught so the command still returns the organization projects; wrap
the host lookup in a try/catch (around the call to
target.client.project.list.query and subsequent hostProjects mapping) and on
error set hostProjects to an empty array (or keep hostProjectById empty) and
optionally log/debug the error; ensure the final returned mapping in the
projects.map still runs and uses "unknown"/"-" or the existing "no"/"-"
semantics (setUp: "no" or "unknown" and path: "-" or "unknown") when hostProject
is missing so the list gracefully degrades when the host is unreachable.
🪄 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
Run ID: dd9bd28e-71ed-4f79-b80b-6b8a83790330
📒 Files selected for processing (6)
apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsxapps/desktop/src/renderer/routes/_authenticated/settings/projects/$projectId/page.tsxapps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsxapps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsxpackages/cli/src/commands/projects/list/command.tspackages/cli/src/commands/projects/setup/command.ts
| <AlertDialogDescription> | ||
| This repository is already linked to project " | ||
| {conflict?.name ?? ""}" in this organization. Open that project to | ||
| set it up on this device. | ||
| set it up on {hostName}. | ||
| </AlertDialogDescription> |
There was a problem hiding this comment.
Make conflict/error dialog text selectable.
Add explicit selectable text classes to the dialog error description so users can copy it.
🎯 Proposed fix
- <AlertDialogDescription>
+ <AlertDialogDescription className="select-text cursor-text">
This repository is already linked to project "
{conflict?.name ?? ""}" in this organization. Open that project to
set it up on {hostName}.
</AlertDialogDescription>As per coding guidelines, apps/desktop/**/*.{tsx,jsx}: Error text must be selectable by users with explicit select-text cursor-text classes; renderer sets user-select: none on body.
🤖 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/settings/v2-project/`$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsx
around lines 365 - 369, The AlertDialogDescription text in
ProjectLocationSection is not selectable due to global user-select: none; update
the AlertDialogDescription element (in the ProjectLocationSection component) to
include explicit selectable classes (e.g., add "select-text cursor-text" or your
project's equivalent class names) so the conflict/error message text can be
copied by users; ensure the class is applied to the same element rendering the
string "{conflict?.name ?? ""}" so the entire message becomes selectable.
| const projectId = (options.project ?? args.id) as string | undefined; | ||
| if (!projectId) { | ||
| throw new CLIError( | ||
| "Project ID required", | ||
| "Pass --project <projectId>, or provide the project ID as the first argument.", | ||
| ); | ||
| } | ||
| if (options.project && args.id && options.project !== args.id) { | ||
| throw new CLIError( | ||
| "Project ID specified twice", | ||
| "Use either --project <projectId> or the positional project ID, not both.", | ||
| ); |
There was a problem hiding this comment.
Reject duplicate project IDs even when both values match.
Line 34 only errors when the two inputs differ, so --project <id> <id> still passes even though this command now requires exactly one project ID source. That leaves the new validation incomplete.
Proposed fix
const projectId = (options.project ?? args.id) as string | undefined;
if (!projectId) {
throw new CLIError(
"Project ID required",
"Pass --project <projectId>, or provide the project ID as the first argument.",
);
}
- if (options.project && args.id && options.project !== args.id) {
+ if (options.project && args.id) {
throw new CLIError(
"Project ID specified twice",
"Use either --project <projectId> or the positional project ID, not both.",
);
}🤖 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/cli/src/commands/projects/setup/command.ts` around lines 27 - 38,
The current validation allows both options.project and args.id to be provided if
they match; change the logic in the project ID validation so that if both
sources are present (options.project and args.id) you always throw a CLIError
indicating duplicate project ID sources (regardless of equality), ensuring
exactly one source is used; keep the existing projectId extraction (const
projectId = (options.project ?? args.id) as string | undefined) and replace the
conditional that checks options.project && args.id && options.project !==
args.id with a simple options.project && args.id check that throws the same
"Project ID specified twice" CLIError.
There was a problem hiding this comment.
1 issue found across 6 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="packages/cli/src/commands/projects/list/command.ts">
<violation number="1" location="packages/cli/src/commands/projects/list/command.ts:24">
P1: `projects list` hard-fails on host resolution/RPC errors, regressing previously available API-only listing behavior. The command now unconditionally calls `resolveHostTarget()` and `target.client.project.list.query()` with no fallback. If the local host service isn't running (no manifest/stale PID) or the remote host is unreachable, the entire command throws instead of returning the cloud project list. The `setUp`/`path` metadata is additive and should be gracefully omitted when host data is unavailable.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Re-trigger cubic
|
|
||
| return ctx.api.v2Project.list.query({ organizationId }); | ||
| const projects = await ctx.api.v2Project.list.query({ organizationId }); | ||
| const hostId = resolveHostFilter({ |
There was a problem hiding this comment.
P1: projects list hard-fails on host resolution/RPC errors, regressing previously available API-only listing behavior. The command now unconditionally calls resolveHostTarget() and target.client.project.list.query() with no fallback. If the local host service isn't running (no manifest/stale PID) or the remote host is unreachable, the entire command throws instead of returning the cloud project list. The setUp/path metadata is additive and should be gracefully omitted when host data is unavailable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/commands/projects/list/command.ts, line 24:
<comment>`projects list` hard-fails on host resolution/RPC errors, regressing previously available API-only listing behavior. The command now unconditionally calls `resolveHostTarget()` and `target.client.project.list.query()` with no fallback. If the local host service isn't running (no manifest/stale PID) or the remote host is unreachable, the entire command throws instead of returning the cloud project list. The `setUp`/`path` metadata is additive and should be gracefully omitted when host data is unavailable.</comment>
<file context>
@@ -1,20 +1,47 @@
- return ctx.api.v2Project.list.query({ organizationId });
+ const projects = await ctx.api.v2Project.list.query({ organizationId });
+ const hostId = resolveHostFilter({
+ host: options.host ?? undefined,
+ local: options.local ?? undefined,
</file context>
|
Thanks for this @rifqi2320 adding a follow up PR with some handling on the settings page UI as well for multiple host |
) * feat(desktop): polish v2 project settings to Linear-style layout Restructures the v2 project settings page to match the canonical SettingsRow pattern used in OrganizationSettings: flat list of rows with a uniform w-96 control column, no inner borders or card chrome. - Move project icon thumbnail into the page header beside the title; clicking the thumbnail opens a dropdown menu (Upload / Use GitHub icon / Remove) instead of inline buttons. - Host scope chip rendered inline in the header (single-host hidden). - Repository and Location each render a single w-96 field with a ghost icon button absolutely positioned inside it: FaGithub + tooltip "Open in GitHub" for Repository, LuFolderOpen + tooltip "Change location" for Location. - Location path display uses overflow-x-auto whitespace-nowrap so long paths scroll horizontally inside the field instead of breaking mid-word; ClickablePath gains an opt-in truncate prop (kept for v1 callers). - Scripts editor uses underline-style tabs (no pill chrome, no gap, px-3 per trigger, hover transition), a skeleton-shaped loading state matching the rendered layout, and a tucked-in "Import" button inside the textarea corner. - All inputs normalized to text-sm; mono fields keep font-mono. - Delete project row aligned to py-2.5 / gap-8 rhythm without a separator above. * fix: address review feedback on PR #4665 - cli/projects list: gracefully degrade when the host service can't be reached and the user did not explicitly pass --host/--local. Falls back to showing organization projects with setUp="?" and path="-" instead of throwing, restoring the previously API-only listing behavior. Still throws when --host or --local was requested explicitly, so the user gets a clear error in that case. (P1 flagged by greptile-apps and cubic-dev-ai.) - cli/projects setup: reject "Project ID specified twice" whenever both --project and the positional argument are passed, even when the values match. (Flagged by coderabbitai.) - v2 project settings: add `select-text cursor-text` to both AlertDialogDescription blocks in ProjectLocationSection so users can copy the conflict message and the From/To paths into bug reports. Aligns with the renderer-wide "error text must be selectable" rule in apps/desktop/AGENTS.md. (Flagged by coderabbitai.) * refactor(desktop): tighten v2 Location empty state UI The remote empty state previously rendered three side-by-side blocks (a decorative "Not set up on X" input chrome, plus two stacked input+button rows for Import and Clone) — busy and repeated the host name three times. Replace with a single inline form: mode Select (Clone / Import) + path input + "Set up" button. Mode defaults to Clone when a GitHub remote is linked, otherwise Import. The Clone option in the dropdown disables and shows a tooltip when no repo is linked. Local empty state drops the decorative left container too and just shows the two outline buttons that trigger the native folder picker. No behavior change: handleImport / handleClone still drive the same setup logic and conflict precheck. * feat(desktop): move v2 project setup form into a modal Replace the inline empty-state setup form (mode select + path input + button) with a single "Set up project…" button that opens a new SetupProjectModal. The empty Location row now reads: Not set up on {host}. [Set up project…] The modal has Clone / Import existing tabs, a Repository readout on the Clone tab, and a path input. For local-host setup the input is paired with a browse button (native folder picker); for remote-host setup the input is text-only with a hostName-qualified placeholder. Conflict precheck is preserved: on import, if the path is already linked to another project, the modal closes and the existing "Repository already linked" AlertDialog opens via the parent. ProjectLocationSection drops the now-unused remoteImportPath / remoteCloneParentDir / remoteMode state and the inline submit handlers; runSetup / runClone live in the modal. The Change… flow and the Relocate confirmation dialog are untouched. * feat(desktop): add RemotePathPicker for browsing host filesystem When setting up a project on a remote host you couldn't previously pick a directory — only type the absolute path blind. Add a reusable folder browser that talks to the host service over tRPC. Backend: - New filesystem.browseHost protected query on the host-service router. Takes an optional absolute or ~-prefixed path (defaults to homedir()), normalizes it, and returns { path, parentPath, homePath, entries[] } with entries sorted dotfiles-last and folders-first. Hidden files filtered by default; pass includeHidden:true to see them. Unlike the existing listDirectory endpoint, this is not scoped to a workspace — used for setup flows where no workspace exists yet on the host. Path must be absolute or start with ~ for safety; relative paths are rejected. Frontend (renderer/components/RemotePathPicker): - Reusable component that opens a Dialog with a path input (~-aware), Up / Home / Refresh icon buttons, and a scrollable list of subfolders. Single-click highlights a folder in the path input; double-click descends into it. "Use this folder" picks the current path and closes. Reusable across any future flow that needs to pick a path on a host (workspace settings, script paths, etc.). Integration: - SetupProjectModal: the browse button now opens RemotePathPicker for remote hosts and the existing native Electron picker for local hosts, behind the same LuFolderOpen icon. Picked path fills the Clone parent-directory or Import existing-repo input. * refactor(desktop): redesign RemotePathPicker with Linear design Replace the four-button toolbar + editable path input with a clean breadcrumb-driven path picker: - Breadcrumb at top doubles as path display and navigation. Clicking any earlier segment jumps to that level. Last segment is bold. If the path lives under $HOME it starts with a "Home" segment; long paths collapse middle segments with BreadcrumbEllipsis. - Folders below in a borderless scroll area. Single click on a folder descends into it (replaces the earlier double-click pattern, which was easy to miss). Hover state on each row, no outer border. - Refresh moves into a quiet icon button next to the breadcrumb. - Symlink indicator: small LuExternalLink at the row end instead of a text "link" badge. - Empty state shows a muted folder glyph plus "Empty folder" / "No subfolders" message. - Loading state renders skeleton rows that match the rendered row layout (icon + label). - Borders only where they carry weight: under the breadcrumb row and above the footer. The dialog itself drops outer padding and gains per-section padding so the borders span the full dialog width. - Drops the Up / Home / Edit-path toolbar buttons; up-navigation happens via breadcrumb segments and home-navigation via the first "Home" segment. The current path is always visible in the breadcrumb, so no separate read-only path readout is needed. * fix(desktop): address review feedback on PR #4675 Functional fixes: - ProjectLocationSection: the "Change location" pencil button used to open the local native folder picker even when the project was set up on a remote host, which would have produced an invalid relocation path. Now: on local targets it still opens the native picker; on remote targets it opens the RemotePathPicker against the remote host's filesystem. The relocate flow (conflict precheck + relocate confirm dialog) is unchanged. - V2ScriptsEditor/ScriptField: add `pb-8 pr-20` to the textarea so the absolute-positioned Import button at the bottom-right can no longer overlap typed content. - ClickablePath: the truncate span needs `min-w-0` when its parent is a flex container, otherwise the ellipsis never kicks in. Structural fixes (one-component-per-file rule from AGENTS.md): - Extract `SettingsRow` from V2ProjectSettings.tsx into its own SettingsRow/ folder with an index re-export. V2ProjectSettings.tsx imports it. - Extract `ScriptField` from V2ScriptsEditor.tsx into a co-located components/ScriptField/ folder. V2ScriptsEditor.tsx imports it. (Co-located under V2ScriptsEditor since it's only used by that editor.)
) * feat(desktop): polish v2 project settings to Linear-style layout Restructures the v2 project settings page to match the canonical SettingsRow pattern used in OrganizationSettings: flat list of rows with a uniform w-96 control column, no inner borders or card chrome. - Move project icon thumbnail into the page header beside the title; clicking the thumbnail opens a dropdown menu (Upload / Use GitHub icon / Remove) instead of inline buttons. - Host scope chip rendered inline in the header (single-host hidden). - Repository and Location each render a single w-96 field with a ghost icon button absolutely positioned inside it: FaGithub + tooltip "Open in GitHub" for Repository, LuFolderOpen + tooltip "Change location" for Location. - Location path display uses overflow-x-auto whitespace-nowrap so long paths scroll horizontally inside the field instead of breaking mid-word; ClickablePath gains an opt-in truncate prop (kept for v1 callers). - Scripts editor uses underline-style tabs (no pill chrome, no gap, px-3 per trigger, hover transition), a skeleton-shaped loading state matching the rendered layout, and a tucked-in "Import" button inside the textarea corner. - All inputs normalized to text-sm; mono fields keep font-mono. - Delete project row aligned to py-2.5 / gap-8 rhythm without a separator above. * fix: address review feedback on PR #4665 - cli/projects list: gracefully degrade when the host service can't be reached and the user did not explicitly pass --host/--local. Falls back to showing organization projects with setUp="?" and path="-" instead of throwing, restoring the previously API-only listing behavior. Still throws when --host or --local was requested explicitly, so the user gets a clear error in that case. (P1 flagged by greptile-apps and cubic-dev-ai.) - cli/projects setup: reject "Project ID specified twice" whenever both --project and the positional argument are passed, even when the values match. (Flagged by coderabbitai.) - v2 project settings: add `select-text cursor-text` to both AlertDialogDescription blocks in ProjectLocationSection so users can copy the conflict message and the From/To paths into bug reports. Aligns with the renderer-wide "error text must be selectable" rule in apps/desktop/AGENTS.md. (Flagged by coderabbitai.) * refactor(desktop): tighten v2 Location empty state UI The remote empty state previously rendered three side-by-side blocks (a decorative "Not set up on X" input chrome, plus two stacked input+button rows for Import and Clone) — busy and repeated the host name three times. Replace with a single inline form: mode Select (Clone / Import) + path input + "Set up" button. Mode defaults to Clone when a GitHub remote is linked, otherwise Import. The Clone option in the dropdown disables and shows a tooltip when no repo is linked. Local empty state drops the decorative left container too and just shows the two outline buttons that trigger the native folder picker. No behavior change: handleImport / handleClone still drive the same setup logic and conflict precheck. * feat(desktop): move v2 project setup form into a modal Replace the inline empty-state setup form (mode select + path input + button) with a single "Set up project…" button that opens a new SetupProjectModal. The empty Location row now reads: Not set up on {host}. [Set up project…] The modal has Clone / Import existing tabs, a Repository readout on the Clone tab, and a path input. For local-host setup the input is paired with a browse button (native folder picker); for remote-host setup the input is text-only with a hostName-qualified placeholder. Conflict precheck is preserved: on import, if the path is already linked to another project, the modal closes and the existing "Repository already linked" AlertDialog opens via the parent. ProjectLocationSection drops the now-unused remoteImportPath / remoteCloneParentDir / remoteMode state and the inline submit handlers; runSetup / runClone live in the modal. The Change… flow and the Relocate confirmation dialog are untouched. * feat(desktop): add RemotePathPicker for browsing host filesystem When setting up a project on a remote host you couldn't previously pick a directory — only type the absolute path blind. Add a reusable folder browser that talks to the host service over tRPC. Backend: - New filesystem.browseHost protected query on the host-service router. Takes an optional absolute or ~-prefixed path (defaults to homedir()), normalizes it, and returns { path, parentPath, homePath, entries[] } with entries sorted dotfiles-last and folders-first. Hidden files filtered by default; pass includeHidden:true to see them. Unlike the existing listDirectory endpoint, this is not scoped to a workspace — used for setup flows where no workspace exists yet on the host. Path must be absolute or start with ~ for safety; relative paths are rejected. Frontend (renderer/components/RemotePathPicker): - Reusable component that opens a Dialog with a path input (~-aware), Up / Home / Refresh icon buttons, and a scrollable list of subfolders. Single-click highlights a folder in the path input; double-click descends into it. "Use this folder" picks the current path and closes. Reusable across any future flow that needs to pick a path on a host (workspace settings, script paths, etc.). Integration: - SetupProjectModal: the browse button now opens RemotePathPicker for remote hosts and the existing native Electron picker for local hosts, behind the same LuFolderOpen icon. Picked path fills the Clone parent-directory or Import existing-repo input. * refactor(desktop): redesign RemotePathPicker with Linear design Replace the four-button toolbar + editable path input with a clean breadcrumb-driven path picker: - Breadcrumb at top doubles as path display and navigation. Clicking any earlier segment jumps to that level. Last segment is bold. If the path lives under $HOME it starts with a "Home" segment; long paths collapse middle segments with BreadcrumbEllipsis. - Folders below in a borderless scroll area. Single click on a folder descends into it (replaces the earlier double-click pattern, which was easy to miss). Hover state on each row, no outer border. - Refresh moves into a quiet icon button next to the breadcrumb. - Symlink indicator: small LuExternalLink at the row end instead of a text "link" badge. - Empty state shows a muted folder glyph plus "Empty folder" / "No subfolders" message. - Loading state renders skeleton rows that match the rendered row layout (icon + label). - Borders only where they carry weight: under the breadcrumb row and above the footer. The dialog itself drops outer padding and gains per-section padding so the borders span the full dialog width. - Drops the Up / Home / Edit-path toolbar buttons; up-navigation happens via breadcrumb segments and home-navigation via the first "Home" segment. The current path is always visible in the breadcrumb, so no separate read-only path readout is needed. * fix(desktop): address review feedback on PR #4675 Functional fixes: - ProjectLocationSection: the "Change location" pencil button used to open the local native folder picker even when the project was set up on a remote host, which would have produced an invalid relocation path. Now: on local targets it still opens the native picker; on remote targets it opens the RemotePathPicker against the remote host's filesystem. The relocate flow (conflict precheck + relocate confirm dialog) is unchanged. - V2ScriptsEditor/ScriptField: add `pb-8 pr-20` to the textarea so the absolute-positioned Import button at the bottom-right can no longer overlap typed content. - ClickablePath: the truncate span needs `min-w-0` when its parent is a flex container, otherwise the ellipsis never kicks in. Structural fixes (one-component-per-file rule from AGENTS.md): - Extract `SettingsRow` from V2ProjectSettings.tsx into its own SettingsRow/ folder with an index re-export. V2ProjectSettings.tsx imports it. - Extract `ScriptField` from V2ScriptsEditor.tsx into a co-located components/ScriptField/ folder. V2ScriptsEditor.tsx imports it. (Co-located under V2ScriptsEditor since it's only used by that editor.)
Fixes the remote/headless host setup gap where the New Workspace modal detects that a project is not registered on the selected host, but project setup opens local-only settings.
This change:
superset projects setup --project <projectId> --path <path>while keeping existing positional/--importusagesuperset projects listwith per-host setup status and pathVerification:
bunx biome check packages/cli/src/commands/projects/list/command.ts packages/cli/src/commands/projects/setup/command.ts apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx apps/desktop/src/renderer/routes/_authenticated/settings/projects/\$projectId/page.tsx apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/\$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/\$projectId/components/V2ProjectSettings/components/ProjectLocationSection/ProjectLocationSection.tsxbun run --cwd packages/cli typecheckbun run --cwd apps/desktop typecheckgit diff --checkNote: initial
bun installfailed rebuilding nativenode-ptybecausenode-gypis not installed in this environment; reranbun install --ignore-scriptsto install JS tooling for typecheck.Summary by cubic
Fixes v2 project setup so a selected remote/headless host is honored end-to-end. The New Workspace flow now opens project settings for that host and runs setup, conflict checks, and scripts on that host.
Bug Fixes
hostIdfrom New Workspace to v2 project settings and resolve target host URL/name.New Features
superset projects setupsupports--project <id>and--path <path>(alias for--import), with stricter argument validation; existing positional ID and--parentDirstill work.superset projects listadds per-host setup status and path columns and supports--host <machineId>or--localfilters.Written for commit 3bb34e3. Summary will update on new commits. Review in cubic
Summary by CodeRabbit