Conversation
🔍 Multi-Model Consensus Review — PR #405 (Round 1)PR: feat: Add existing folder as repository Findings (consensus ≥ 2 models)🟡 F1: 🟡 F2: Bare catch { remoteUrl = ""; }
🟡 F3: Same bare catch { return false; }A cancelled 🟢 F4: Fragile state reset on form open (SessionSidebar.razor:341) ✅ Positive Notes
Test Coverage
Verdict:
|
F1 (test race): RunProcess now sets EnableRaisingEvents=true and subscribes Exited BEFORE Process.Start(), eliminating the window where a fast process (like 'git init') exits before the event handler is registered. F2 (bare catch swallows cancellation): AddRepositoryFromLocalAsync now uses 'catch (Exception ex) when (ex is not OperationCanceledException)' so cancellation propagates instead of yielding a misleading 'No origin remote' error. F3 (bare catch in IsGitRepositoryAsync): Added explicit re-throw for OperationCanceledException so cancellation propagates from the git rev-parse call instead of returning 'false' (not a git repo). F4 (stale form state on re-open): Replaced inline 'showAddRepo = true' handler with OpenAddRepoForm() method that resets addRepoFolderMode, addRepoFolderPath, addRepoError, confirmRepoReplace, confirmRepoName, and newRepoUrl on every open. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
F1 (test race): RunProcess now sets EnableRaisingEvents=true and subscribes Exited BEFORE Process.Start(), eliminating the window where a fast process (like 'git init') exits before the event handler is registered. F2 (bare catch swallows cancellation): AddRepositoryFromLocalAsync now uses 'catch (Exception ex) when (ex is not OperationCanceledException)' so cancellation propagates instead of yielding a misleading 'No origin remote' error. F3 (bare catch in IsGitRepositoryAsync): Added explicit re-throw for OperationCanceledException so cancellation propagates from the git rev-parse call instead of returning 'false' (not a git repo). F4 (stale form state on re-open): Replaced inline 'showAddRepo = true' handler with OpenAddRepoForm() method that resets addRepoFolderMode, addRepoFolderPath, addRepoError, confirmRepoReplace, confirmRepoName, and newRepoUrl on every open. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
775ea1b to
c4e2e56
Compare
Replace `Dictionary` with `ConcurrentDictionary` for `_cache` in ExternalSessionScanner. The cache is read/written from a Timer callback (ThreadPool thread) in `Scan()`, and `Dictionary` is not thread-safe for concurrent operations. Found during PR #405 code review — the scanner code is from PR #370. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skip dead event-stream disk fallback when SessionId is empty to avoid reading from an invalid path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allows users to add an already-cloned local folder as a managed repository in PolyPilot, giving it full feature parity with repos cloned through the app (worktrees, branch creation, PR checkout, squad discovery, sessions, etc.). ## RepoManager.AddRepositoryFromLocalAsync (enhanced) - Added Directory.Exists check with clear error message - Added IsGitRepositoryAsync helper (runs git rev-parse --git-dir) - Added origin-remote check with actionable error message - Added Action<string>? onProgress parameter forwarded to the clone step - All three validations throw InvalidOperationException with user-friendly messages ## CopilotService.AddRepoFromLocalFolderAsync (new) - Public surface that delegates to RepoManager then creates the repo group - Desktop (local) mode only — throws clearly when connected to a remote server ## SessionSidebar UI (updated) - '+ Repo' form now shows URL / Existing Folder tabs on desktop - Folder tab: path text input + Browse (…) button that opens FolderPickerService - Browse button is platform-gated (#if MACCATALYST || WINDOWS) - CloseAddRepoForm resets both modes; AddRepositoryFromFolder wires progress display - CSS: .add-repo-mode-tabs, .add-repo-tab, .add-repo-folder-row, .btn-repo-browse ## Tests (3 new in RepoManagerTests) - AddRepositoryFromLocal_NonExistentFolder_ThrowsWithClearMessage - AddRepositoryFromLocal_FolderWithNoGit_ThrowsWithClearMessage - AddRepositoryFromLocal_GitRepoWithNoOrigin_ThrowsWithClearMessage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0760d4d to
76ac48e
Compare
…into existing repo) Root cause of ongoing confusion: when the user added ~/Projects/maui3, the code extracted its origin (https://github.com/dotnet/maui) and merged it into the existing 'maui' group. The user saw no visible change. The user's expectation: adding a local folder should create a NEW, DISTINCT entry in the sidebar -- like '📁 maui3' -- that they can see and use. Fix: complete redesign of the local-folder flow: 1. SessionGroup.LocalPath (new field): marks a group as a 'pinned local folder'. IsLocalFolder computed property. Not linked to a PolyPilot bare clone. 2. GetOrCreateLocalFolderGroup() (new): creates a distinct group named after the folder (e.g. 'maui3'), idempotent by path. Un-collapses if already exists. 3. AddRepoFromLocalFolderAsync: now does path normalization (including ~ expansion) itself, validates the folder, ALSO registers the bare clone (for worktree support), then creates a LOCAL FOLDER GROUP -- not the existing repo group. 4. Sidebar rendering: local-folder groups show a 📁 icon, have their own '...' menu with 'New Session Here' (creates CWD=localPath session) and 'Remove Folder'. 5. QuickCreateSessionForFolder: creates a timestamped session in the local folder group, with working directory set to the folder path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Allows users to add an already-cloned local folder as a managed repository in PolyPilot. Once added, the folder gets full feature parity with repos cloned through the app: worktree creation, branch checkout, PR checkout, squad file discovery, and sessions.
How it works
The '+ Repo' button in the sidebar now shows two tabs on desktop:
For the folder flow, the user either types a path or clicks … to open the native folder picker. PolyPilot then:
git rev-parse --git-dir)originremote URLThe result is a
RepositoryInfoentry inrepos.jsonand a bare clone in~/.polypilot/repos/— identical to any other managed repo.Changes
RepoManager.AddRepositoryFromLocalAsync— enhanced with validation (folder exists, is git repo, has origin remote), progress callback, and clear error messagesCopilotService.AddRepoFromLocalFolderAsync— new public method, local mode onlySessionSidebar.razor— URL/Folder mode tabs, folder path input + browse button (platform-gated),CloseAddRepoFormhelperSessionSidebar.razor.css— styles for tabs, folder row, browse buttonAddRepositoryFromLocal_*tests covering non-existent folder, non-git folder, and git repo with no origin remote