feat(repo): add graphical folder picker for adding local repositories - #324
Conversation
Previously, adding a local repository or scanning a folder in the
"Add Repository" dialog required typing an absolute filesystem path by
hand. This adds a navigable directory browser so users can select a
folder graphically instead.
Backend
- New shared Zod schemas (DirectoryEntry, BrowseDirectoryResponse,
BrowseDirectoryRequest) exported via shared/types and shared/schemas.
- New env var REPO_BROWSE_ROOT plus getBrowseRootPath() helper. It is
intentionally NOT defaulted to WORKSPACE_PATH: folder browsing is
opt-in and only works when an administrator explicitly configures a
root directory.
- New filesystem service (browseDirectory) that lists only directories
(hidden entries filtered), flags Git repositories, and confines all
access to the configured root:
- 501 when REPO_BROWSE_ROOT is not configured
- 403 on path traversal outside the root
- 404 for missing directories, 400 when the target is a file
- New authenticated route GET /api/filesystem/browse, registered under
the protected API group.
Frontend
- New api client browseDirectory() (uses fetchWrapper, so auth cookies
are sent).
- New DirectoryPickerDialog: navigate into subfolders, go up a level,
Git repos marked with an icon, and a "Select This Folder" action.
Retries are disabled so a configuration error is not polled
repeatedly.
- When browsing is disabled (501), the dialog shows a clear, actionable
message telling the user to set REPO_BROWSE_ROOT and to enter the
path manually in the meantime, instead of a generic error.
- AddRepoDialog gains a "Browse" button next to both the Local and
Folder path inputs; the chosen path is written back into the active
field.
Tests
- Service and route tests covering listing, Git-repo detection, hidden
filtering, parent navigation, and the 400/403/404/501 error paths.
Security note: because there is no default root, the picker is inert
until REPO_BROWSE_ROOT is set, and it can never escape that root.
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a protected filesystem browsing API, shared schemas and configuration, backend validation, and a frontend directory picker for selecting local or folder repositories. ChangesFilesystem browsing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
actor User
participant AddRepoDialog
participant DirectoryPickerDialog
participant FilesystemAPI
participant FilesystemService
User->>AddRepoDialog: Open local or folder picker
AddRepoDialog->>DirectoryPickerDialog: Set picker open
DirectoryPickerDialog->>FilesystemAPI: Browse current path
FilesystemAPI->>FilesystemService: GET /api/filesystem/browse
FilesystemService-->>FilesystemAPI: Directory entries
FilesystemAPI-->>DirectoryPickerDialog: Render directory list
User->>DirectoryPickerDialog: Select folder
DirectoryPickerDialog-->>AddRepoDialog: Return selected path
AddRepoDialog->>AddRepoDialog: Set localPath or folderPath
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@backend/src/services/filesystem.ts`:
- Around line 22-24: Update the path validation in the filesystem browse method
surrounding resolved and root to canonicalize both paths with fs.realpath(),
then use path.relative() to allow only the root itself or descendants without
upward traversal, including when the root is /. Perform this validation before
fs.stat or fs.readdir so symlink targets outside the root are rejected, and add
a regression test covering a symlink escape.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 90d8fa54-4003-48d8-8d75-24fcb9e6ae25
📒 Files selected for processing (12)
backend/src/index.tsbackend/src/routes/filesystem.test.tsbackend/src/routes/filesystem.tsbackend/src/services/filesystem.test.tsbackend/src/services/filesystem.tsfrontend/src/api/filesystem.tsfrontend/src/components/repo/AddRepoDialog.tsxfrontend/src/components/repo/DirectoryPickerDialog.tsxshared/src/config/env.tsshared/src/schemas/filesystem.tsshared/src/schemas/index.tsshared/src/types/index.ts
Canonicalize root and requested path with fs.realpath() and gate on path.relative() so symlinks inside REPO_BROWSE_ROOT pointing outside are rejected with 403 before fs.stat/fs.readdir follow them. Handles root '/' correctly. Adds regression test for symlink escape.
Replace grid content-start packing with explicit grid-rows tracks so the directory list grows to fill the dialog and scrolls while the header, path, and footer stay fixed. Drops the ineffective flex-1 from the grid child on the scroll container.
f8e361c
into
chriswritescode-dev:main
Summary
Previously, adding a local repository or scanning a folder in the
"Add Repository" dialog required typing an absolute filesystem path by
hand. This adds a navigable directory browser so users can select a
folder graphically instead.
Security note: because there is no default root, the picker is inert
until REPO_BROWSE_ROOT is set, and it can never escape that root.
Type of Change
Checklist
pnpm lintpasses locallypnpm typecheckpasses locallySummary by CodeRabbit
New Features
Bug Fixes