Skip to content

[Feature]: Add-project path autocomplete omits symlinked directories and macOS Finder aliases #2135

Description

@sak0a

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I am describing a concrete problem or use case, not just a vague idea.

Area

apps/server

Problem or use case

Typing a path in the add-project autocomplete (e.g. ~/) hides two kinds of valid directory targets:

  1. Directory symlinks — common on every platform (e.g. ~/Dropbox -> ~/Library/CloudStorage/Dropbox created by Dropbox, stow-managed dotfiles,
    /opt/homebrew shortcuts).
  2. macOS Finder aliases — binary bookmark files created via Make Alias or Option+Cmd drag.

Root cause in apps/server/src/workspace/Layers/WorkspaceEntries.ts → browse(): it filters dirents on dirent.isDirectory(), but readdir uses lstat
semantics so symlinks are excluded, and Finder aliases are plain files that no fs call can follow.

Proposed solution

  1. In browse(), also consider isSymbolicLink() dirents and keep those whose stat() target is a directory.
  2. On macOS, also consider isFile() dirents, detect Finder aliases by their book… magic bytes, and batch-resolve via a single osascript call
    using Finder's original item of ….
  3. Add two optional flags to FilesystemBrowseEntry: isSymlink (icon hint, set for both) and isAlias (navigation hint, set when fullPath is a
    resolved target).
  4. In the command palette, show a distinct icon (e.g. lucide FolderSymlink) for isSymlink entries. For regular dirs and symlinks, keep the
    existing "append clicked name" navigation so ~/ style paths are preserved. For aliases, jump directly to the resolved fullPath — appending the
    alias name would point at a non-directory and readdir would fail.

Scope stays inside the interactive browse() path. The recursive workspace indexer in the same file is left alone to avoid cycles.

Why this matters

Common developer shortcuts are silently invisible, forcing users to paste resolved paths or fall back to the native picker — which defeats the
typed-path autocomplete's purpose. Electron's native folder dialog already handles both cases transparently, so the current gap is an
inconsistency, not a design choice.

Smallest useful scope

Just the symlink fix: detect symlinks, stat() them, include if the target is a directory. Single-function change, no contract change. Covers the
Dropbox and dotfiles cases. Finder alias support + contract additions + icon + alias navigation can ship as a follow-up.

Alternatives considered

  • Return resolved real paths for symlinks — rejected; the user typed ~/Dropbox, they should pick ~/Dropbox. Alias asymmetry is unavoidable since
    the alias file itself isn't a directory.
  • Parse the bookmark binary format in JS — rejected; format varies across macOS versions, osascript is simpler and battle-tested.
  • Add a native module for xattrs/bookmarks — rejected; not worth the dependency for one UI affordance.

Risks or tradeoffs

  • One extra stat() per symlink candidate — negligible, interactive only.
  • One osascript subprocess per browse call with alias candidates on macOS — ~50–200 ms, short timeout so a hung Finder can't block the UI; on
    failure entries drop silently.
  • macOS may prompt once for Finder automation permission the first time an alias is resolved.
  • Symlinks keep the typed path, aliases jump to the resolved path — the distinct icon mitigates the asymmetry.

Examples or references

  • Repro (symlinks): on a default macOS + Dropbox install, ls -la ~/Dropbox shows … Dropbox -> /Users//Library/CloudStorage/Dropbox. Type ~/
    in the add-project picker — Dropbox is missing.
  • Repro (aliases): create a Finder alias (Cmd+L) somewhere under ~/. file reports MacOS Alias file, xxd shows book…mark… magic. Type ~/ in the
    picker — the alias is missing.

Contribution

  • I would be open to helping implement this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementRequested improvement or new capability.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions