Skip to content

feat(repo): add graphical folder picker for adding local repositories - #324

Merged
chriswritescode-dev merged 3 commits into
chriswritescode-dev:mainfrom
thrawn-sh:feat/file-browser
Jul 27, 2026
Merged

feat(repo): add graphical folder picker for adding local repositories#324
chriswritescode-dev merged 3 commits into
chriswritescode-dev:mainfrom
thrawn-sh:feat/file-browser

Conversation

@thrawn-sh

@thrawn-sh thrawn-sh commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

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

  • Bug fix
  • New feature
  • Refactor
  • Documentation

Checklist

  • Code follows project style (no comments, named imports)
  • TypeScript types are properly defined
  • Tests added/updated (80% coverage target)
  • pnpm lint passes locally
  • pnpm typecheck passes locally

Summary by CodeRabbit

  • New Features

    • Added a local directory picker to browse and select folders when adding repositories.
    • Introduced a filesystem browsing API to list directories (with parent navigation) and indicate whether folders contain a Git repository.
    • Added shared types/schemas and a frontend API helper for directory browsing responses.
  • Bug Fixes

    • Enforced that browsing stays within the configured browse root and blocks restricted paths (including traversal attempts and escaping via symlinks).
    • Hidden directories are no longer shown during browsing.

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.
@chriswritescode-dev

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@chriswritescode-dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 936daf45-ad0a-4172-819d-3d577d4aa275

📥 Commits

Reviewing files that changed from the base of the PR and between 9610a80 and 22c935b.

📒 Files selected for processing (1)
  • frontend/src/components/repo/DirectoryPickerDialog.tsx
📝 Walkthrough

Walkthrough

Adds a protected filesystem browsing API, shared schemas and configuration, backend validation, and a frontend directory picker for selecting local or folder repositories.

Changes

Filesystem browsing

Layer / File(s) Summary
Browse contracts and configuration
shared/src/config/env.ts, shared/src/schemas/*, shared/src/types/index.ts
Adds browse-root resolution plus shared request, response, directory-entry schemas, and inferred types.
Browse service and protected route
backend/src/services/filesystem.ts, backend/src/routes/filesystem.ts, backend/src/index.ts
Lists permitted directories, detects Git repositories, enforces browse-root boundaries, maps errors to HTTP responses, and registers /api/filesystem.
Backend browse validation
backend/src/services/filesystem.test.ts, backend/src/routes/filesystem.test.ts
Tests listings, navigation, hidden directories, Git detection, invalid paths, missing configuration, and route responses.
Directory picker integration
frontend/src/api/filesystem.ts, frontend/src/components/repo/DirectoryPickerDialog.tsx, frontend/src/components/repo/AddRepoDialog.tsx
Adds directory browsing requests and a picker dialog wired to local and folder repository fields.

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a graphical folder picker for local repository selection.
Description check ✅ Passed The description matches the required template with Summary, Type of Change, and a completed Checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8d5102d and 76f3961.

📒 Files selected for processing (12)
  • backend/src/index.ts
  • backend/src/routes/filesystem.test.ts
  • backend/src/routes/filesystem.ts
  • backend/src/services/filesystem.test.ts
  • backend/src/services/filesystem.ts
  • frontend/src/api/filesystem.ts
  • frontend/src/components/repo/AddRepoDialog.tsx
  • frontend/src/components/repo/DirectoryPickerDialog.tsx
  • shared/src/config/env.ts
  • shared/src/schemas/filesystem.ts
  • shared/src/schemas/index.ts
  • shared/src/types/index.ts

Comment thread backend/src/services/filesystem.ts Outdated
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.
@chriswritescode-dev
chriswritescode-dev merged commit f8e361c into chriswritescode-dev:main Jul 27, 2026
2 checks passed
@thrawn-sh
thrawn-sh deleted the feat/file-browser branch July 28, 2026 19:27
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.

2 participants