Skip to content

Improve Raycast discovery and workspace setup UX - #137

Merged
tonythethompson merged 15 commits into
masterfrom
codex/raycast-discovery-ux
Jul 29, 2026
Merged

Improve Raycast discovery and workspace setup UX#137
tonythethompson merged 15 commits into
masterfrom
codex/raycast-discovery-ux

Conversation

@tonythethompson

@tonythethompson tonythethompson commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • make Discover Git Repos quick-add use the same full command, companion, remote, and dev-server seed as review
  • surface nonpersistent command suggestions directly below command fields
  • add uncapped typed repository search, exact-path probing, and a searchable Switch Branch flow
  • document the Raycast behavior and add regression coverage

Validation

  • npm test (247 tests passed)
  • npm run lint
  • npm run build
  • git diff --cached --check before commit

Summary by cubic

Improves Raycast repo discovery and workspace setup for faster, more reliable project adds. Quick Add now builds a full seed (commands, companion, remote, dev server), and typed search goes past the result cap, probes absolute paths, and stops mid-scan when cancelled.

  • New Features

    • Discover Git Repos Quick Add uses the full seed for parity with Review, reading the origin remote from .git when discovery didn’t provide one.
    • Typed search: debounced and independent, uncapped results, absolute‑path probing, bounded by scan/visit and depth, cancellable mid‑scan, merges with cached results, and temporarily hides repos you just added.
    • Renamed flow to “Switch Branch…” with an auto‑loaded, searchable local‑branch dropdown, smart default (previous target if available), retry on failure, and manual entry fallback.
    • Workspace form adds a nonpersistent, searchable “Command suggestions” dropdown under command fields; docs and tests added for discovery, exact‑path probing, full‑seed creation, and targeted‑scan cancellation.
  • Bug Fixes

    • Completing repo add even when the discovery refresh fails, with error feedback but no interruption to the add flow.
    • Disables and guards Quick Add for a repo while the add is in progress to prevent duplicate adds.
    • Reconciles hidden‑after‑add suppression after refresh so repos reappear when the cache updates (applies to both cached and targeted results).
    • Honors discovery limits under concurrency by reserving capacity before stat calls, capping waves by remaining scan/visit budget, ignoring expected filesystem lookup failures, and surfacing unexpected stat errors.

Written for commit d8bec5b. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Repository discovery adds targeted query/path searches with cancellation and bounded traversal. Discovered repositories now produce fully seeded workspaces, while branch selection, command suggestions, and related documentation reflect the updated flows.

Discovery and workspace flow

Layer / File(s) Summary
Targeted repository discovery
QuickShell.Raycast/src/lib/git-repo-discovery.ts, QuickShell.Raycast/src/components/discover-git-repos-view.tsx, QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts, docs/architecture/git-and-discover.md
Discovery adds normalized query filtering, exact-path probing, abort handling, scan/visit budgets, and debounced targeted results merged with cached results.
Full discovered-workspace construction
QuickShell.Raycast/src/lib/discovered-workspace-seed.ts, QuickShell.Raycast/src/components/discover-git-repos-view.tsx, QuickShell.Raycast/src/__tests__/discovered-workspace-seed.test.ts
Repository metadata, tasks, companion settings, and remote URLs are converted into normalized workspaces, including a fallback blank launch.
Branch and command form updates
QuickShell.Raycast/src/components/set-target-branch-form.tsx, QuickShell.Raycast/src/components/workspace-form.tsx, QuickShell.Raycast/src/open-workspace.tsx, docs/architecture/forms.md, docs/architecture/git-and-discover.md
Local branches load automatically with retry and fallback states, command suggestions use a dropdown, and the branch action is labeled “Switch Branch…”.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Pipeline Stage Enum Ordering ✅ Passed PASS: The PR diff only touches git-repo-discovery code/tests; repo-wide searches found no SessionWorkflowStage enum or comparisons, so the check is not applicable.
Gpu/Cpu Runtime Boundary ✅ Passed Only two Raycast TypeScript files changed; no inference/, requirements, main.py, or C# diarization files were modified, and no boundary keywords appeared.
Managed Host Restart Safety ✅ Passed PR touches discovery/workspace UI/docs/tests only; no ManagedVenvHostManager/Containerized* readiness or restart/stop paths are modified.
Title check ✅ Passed The title accurately summarizes the main Raycast discovery and workspace setup UX changes.
Description check ✅ Passed The description is clearly related to the changeset and matches the discovery, workspace, and branch-flow updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/raycast-discovery-ux
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch codex/raycast-discovery-ux

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The new debounced targeted search in DiscoverGitReposView never resets targetedLoadingQuery on search text change, so the loading state can briefly reflect stale queries; consider clearing targetedLoadingQuery at the start of the effect when query changes to avoid flickering or incorrect loading indicators.
  • The git-repo-discovery tests now import buildSearchRoots, listDefaultRootCandidates, and searchRootsFromWorkspaces from git-repo-discovery, but those helpers are defined in git-repo-search-roots; either re-export them from git-repo-discovery or point the tests back at git-repo-search-roots to keep the test suite aligned with the actual module boundaries.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new debounced targeted search in DiscoverGitReposView never resets `targetedLoadingQuery` on search text change, so the loading state can briefly reflect stale queries; consider clearing `targetedLoadingQuery` at the start of the effect when `query` changes to avoid flickering or incorrect loading indicators.
- The git-repo-discovery tests now import `buildSearchRoots`, `listDefaultRootCandidates`, and `searchRootsFromWorkspaces` from `git-repo-discovery`, but those helpers are defined in `git-repo-search-roots`; either re-export them from `git-repo-discovery` or point the tests back at `git-repo-search-roots` to keep the test suite aligned with the actual module boundaries.

Fix all in Cursor


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2dae5252d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread QuickShell.Raycast/src/lib/discovered-workspace-seed.ts Outdated
Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
@qodo-code-review

qodo-code-review Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 44 rules


🔴 Action Required

1. Added repo still listed ✓ Resolved 🐞 Bug ≡ Correctness
Description
DiscoverGitReposView records added directories in addedDirectoryKeys to hide them immediately after
adding, but it only applies that suppression to targetedMatches and not to cachedMatches. Until
revalidate() refreshes data, the just-added repo can still appear in the cached list and can be
added again, creating duplicate workspaces pointing at the same directory.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R150-168]

+    const cachedMatches = cached.filter(
      (repo) =>
        repo.name.toLowerCase().includes(query) ||
        repo.directory.toLowerCase().includes(query) ||
        (repo.remoteUrl ?? "").toLowerCase().includes(query),
    );
-  }, [data, searchText]);
+    const targetedMatches = targetedSearch?.query === searchText.trim() ? targetedSearch.repos : [];
+    const seen = new Set(cachedMatches.map((repo) => repo.directory.toLowerCase()));
+    return [
+      ...cachedMatches,
+      ...targetedMatches.filter((repo) => {
+        const key = repo.directory.toLowerCase();
+        if (addedDirectoryKeys.has(key) || seen.has(key)) {
+          return false;
+        }
+        seen.add(key);
+        return true;
+      }),
+    ];
Relevance

●●● Strong

Team often accepts Raycast UX/correctness fixes in discovery/storage flows (e.g., PR #55 follow-ups;
PR #51 correctness tweaks).

PR-#55
PR-#51
PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
finishAdd adds the workspace directory to addedDirectoryKeys before revalidating, but filtered()
returns cachedMatches without checking addedDirectoryKeys (only targetedMatches are filtered), so
the just-added repo can remain visible until the cached scan revalidates. A repeated add persists a
second workspace because ids are random and storage upserts by id only (no directory uniqueness
validation).

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[144-176]
QuickShell.Raycast/src/lib/storage.ts[391-430]
QuickShell.Raycast/src/lib/ids.ts[1-15]
QuickShell.Raycast/src/lib/validation.ts[15-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`addedDirectoryKeys` is meant to hide repos immediately after a successful add, but the current `filtered` computation only checks it for `targetedMatches`. Cached results (`data`) can still show the added repo until `revalidate()` completes, allowing a second add.

### Issue Context
A second click on **Add Workspace** creates a new workspace with a new random id, and `upsertWorkspace` only dedupes by id (not directory), so duplicates are persisted.

### Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[144-173]

### Suggested fix
Apply `addedDirectoryKeys` filtering to cached results as well (both the empty-query path and the query path), e.g.:
- Build `cachedVisible = cached.filter(repo => !addedDirectoryKeys.has(repo.directory.toLowerCase()))`
- Use `cachedVisible` for both `return cachedVisible` (no query) and `cachedMatches = cachedVisible.filter(...)` (query case)

This makes the “hide immediately” behavior consistent for both cached and targeted results.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Repo URL no longer seeded ✓ Resolved 🐞 Bug ≡ Correctness
Description
createWorkspaceFromDiscoveredGitRepo sets repoUrl only from seed.remoteUrl, but discovery candidates
are created with remoteUrl: null, so discovered workspaces lose repoUrl and the Open Workspace "Open
Repository" action disappears. Because the full-seed WorkspaceForm skips applyDirectorySuggestions
when seed commands exist, Review Before Adding also won’t backfill repoUrl from .git/config.
Code

QuickShell.Raycast/src/lib/discovered-workspace-seed.ts[R46-52]

+    command: null,
+    runAsAdmin: false,
+    repoUrl: seed.remoteUrl ?? null,
+    devServerUrl: seed.devServerUrl ?? null,
+    // normalizeWorkspace supplies a usable blank launch when discovery finds no tasks.
+    launches,
+    companionApps: seed.companionSeed
Relevance

●● Moderate

No historical evidence about repoUrl seeding for discovered repos; unclear if treated as regression.

PR-#51

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The discovered seed writes repoUrl directly from seed.remoteUrl, but discovery always emits
candidates with remoteUrl: null, so repoUrl stays null. Open Workspace only shows "Open
Repository" when repoUrl is truthy, and WorkspaceForm’s full-seed mount path does not call
applyDirectorySuggestions (which would populate repoUrl) when seeded commands exist, so Review
Before Adding also won’t populate the remote.

QuickShell.Raycast/src/lib/discovered-workspace-seed.ts[21-52]
QuickShell.Raycast/src/lib/git-repo-discovery.ts[239-263]
QuickShell.Raycast/src/components/workspace-form.tsx[155-170]
QuickShell.Raycast/src/open-workspace.tsx[1121-1130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Discovered workspaces are created with `repoUrl: null` because `createWorkspaceFromDiscoveredGitRepo` only uses `seed.remoteUrl`, while `git-repo-discovery` always produces `remoteUrl: null`. Since `WorkspaceForm` intentionally avoids re-running `applyDirectorySuggestions` when seed commands exist, the Review flow doesn’t backfill the remote either.

### Issue Context
This breaks downstream behavior that depends on `workspace.repoUrl` (e.g., the "Open Repository" action in Open Workspace).

### Fix Focus Areas
- QuickShell.Raycast/src/lib/discovered-workspace-seed.ts[21-52]
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[29-40]

### Suggested fix
- Import `tryGetGitRemoteUrl` and set `repoUrl` using a fallback:
 - `const repoUrl = seed.remoteUrl ?? tryGetGitRemoteUrl(directory) ?? null;`
 - Use that in the normalized workspace.
- Alternatively (or additionally), resolve the remote in `buildWorkspaceFromRepo()` when `remoteUrl` is falsy and pass the resolved value into the seed function.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



🟡 Remediation Recommended

3. Quick-add disable race ✓ Resolved 🐞 Bug ☼ Reliability ⭐ New
Description
DiscoverGitReposView uses a single pendingQuickAddKey for in-flight quick adds; starting a second
add overwrites the key and whichever add finishes first clears it, re-enabling Add actions even
while another add is still running. This can lead to overlapping build/upsert flows (and potentially
duplicate workspaces if the same directory is submitted twice before the UI disables it) because
upsertWorkspace only dedupes by workspace id and workspace ids are randomly generated.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R203-208]

+    setPendingQuickAddKey(directory.toLowerCase());
    try {
-      const workspace = buildLightWorkspaceFromRepo(directory, name, remoteUrl);
+      const workspace = await buildWorkspaceFromRepo(directory, name, remoteUrl);
      await storage.upsertWorkspace(workspace);
      await showToast({
        style: Toast.Style.Success,
Relevance

●●● Strong

PR #133 shows strong appetite for concurrency/race hardening; similar “overlapping mutations” issues
were fixed there.

PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The component stores only one pending key and clears it unconditionally on completion, so concurrent
adds cannot be represented correctly and the disabled state will be wrong. Since storage upserts by
id (not directory) and IDs are randomly generated, repeated submissions can persist duplicates
rather than overwriting.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[89-90]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[202-217]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[253-258]
QuickShell.Raycast/src/lib/storage.ts[391-426]
QuickShell.Raycast/src/lib/ids.ts[9-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`pendingQuickAddKey` is a single shared string representing the “currently pending quick add”. If a user triggers a second quick add while the first is still in progress, the second call overwrites the key and the first repo’s Add button becomes enabled again. Additionally, each async call unconditionally sets the key back to `null` in `finally`, so the first completion re-enables *all* Add actions even if another add is still running.

### Issue Context
This causes overlapping add flows (multiple `buildWorkspaceFromRepo` + `storage.upsertWorkspace` + refresh/toasts) and removes the intended guardrail against repeated submissions.

### Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[90-90]
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[203-216]
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[257-257]

### Suggested fix
- Replace `pendingQuickAddKey: string | null` with `pendingQuickAddKeys: Set<string>` (or a ref-counted `Map<string, number>`).
- In `handleQuickAdd`, add the directory key to the set before awaiting.
- In `finally`, remove *only that directory key* from the set.
- Update `disabled` to `pendingQuickAddKeys.has(repo.directory.toLowerCase())`.
- Optionally add a handler-level guard (`if (pendingQuickAddKeys.has(key)) return;`) to prevent double-submit if Raycast dispatches the action twice before re-render.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Blanket catch in worker() ✓ Resolved 📘 Rule violation ☼ Reliability
Description
discoverGitReposAsync swallows all exceptions from fs.stat() via catch { ... }, which can hide
unexpected failures and make discovery behavior non-auditable. The compliance rule requires
catching/handling only expected exception conditions and surfacing unexpected ones.
Code

QuickShell.Raycast/src/lib/git-repo-discovery.ts[R113-117]

      try {
        isDirectory = (await fs.stat(work.directory)).isDirectory();
      } catch {
+        scanned -= 1;
        continue;
Relevance

●●● Strong

They accepted narrowing/removing blanket catch {} in PR #133 (PowerShell focus restore). Likely
apply same rule here.

PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2392968 forbids blanket catch blocks and requires handling only expected exception
types/conditions. The updated discovery worker still uses catch { ... } when calling
fs.stat(work.directory), silently handling any thrown error.

Rule 2392968: Do not use blanket catch blocks; catch only expected exception types
QuickShell.Raycast/src/lib/git-repo-discovery.ts[113-117]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`discoverGitReposAsync` uses a blanket `catch {}` around `fs.stat(...)`, which hides unexpected failures. Per compliance, only expected failure modes should be handled; unexpected errors should be rethrown (or otherwise explicitly surfaced).

## Issue Context
This block was recently modified (adding scan-capacity reservation and decrement), so it’s in scope to adjust error handling as part of the same change.

## Fix Focus Areas
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[113-117]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Cleanup skipped on refresh error ✓ Resolved 🐞 Bug ☼ Reliability
Description
finishAdd() adds the directory key to addedDirectoryKeys, but the key is only removed afterward
without a try/finally. If the refresh step rejects, the key removal and the remaining completion
steps (onWorkspaceAdded, pop) are skipped, leaving the just-added repo hidden for the lifetime
of the mounted view and the add flow incomplete.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R179-183]

+    setAddedDirectoryKeys((current) => {
+      const next = new Set(current);
+      next.delete(addedKey);
+      return next;
+    });
Relevance

●●● Strong

Team often accepts try/finally cleanup to avoid stuck UI/state (toast hide, restore platform) in PRs
#133/#112.

PR-#133
PR-#112

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code adds an addedKey to state, then relies on a later state update to delete it; without
a finally, a rejected refresh will exit the function before cleanup and before the
completion/navigation steps. Elsewhere in the repo, revalidate() is treated as fallible and is
commonly wrapped in try/catch, reinforcing that this path should be robust.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-188]
QuickShell.Raycast/src/open-workspace.tsx[786-793]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`finishAdd()` temporarily hides the newly-added repo by inserting `addedKey` into `addedDirectoryKeys`, but the removal happens only after awaited work and is not protected by `try/finally`. Any rejection during the awaited refresh step can leave the key stuck and also prevent `onWorkspaceAdded` and `pop()` from running.

### Issue Context
This affects the new `addedDirectoryKeys` lifecycle introduced in `finishAdd`.

### Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-188]

### Suggested fix
Wrap the refresh portion in `try/finally` so `addedKey` is always removed. Optionally catch refresh errors so a refresh failure doesn’t block `onWorkspaceAdded` / navigation (the workspace was already persisted by the caller).

Example shape:
```ts
setAddedDirectoryKeys(...add...);
try {
 await revalidate();
} finally {
 setAddedDirectoryKeys(...delete...);
}
await onWorkspaceAdded?.(workspace);
if (popOnAdd) pop();
```

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (8)
6. Stale repo suppression ✓ Resolved 🐞 Bug ≡ Correctness
Description
DiscoverGitReposView now hides cached repos using addedDirectoryKeys, but that set only ever grows
and is never cleared/pruned. If the view stays mounted (popOnAdd=false) and a workspace is later
removed, the repo can remain incorrectly hidden until the component unmounts.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R146-151]

+    const cachedVisible = cached.filter((repo) => !addedDirectoryKeys.has(repo.directory.toLowerCase()));
    const query = searchText.trim().toLowerCase();
    if (!query) {
-      return data;
+      return cachedVisible;
    }
-    return data.filter(
+    const cachedMatches = cachedVisible.filter(
Relevance

●●● Strong

Team accepted prior “clear stale state” UX bugs (stale launches on directory change) in PRs #51/#52.

PR-#51
PR-#52

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new cachedVisible filter depends on addedDirectoryKeys, but the state is only initialized and
appended to; there is no code path that clears/prunes it, so suppression can outlive the actual
workspace state.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[85-99]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[144-170]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-175]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`addedDirectoryKeys` is used to filter cached discovery results, but keys are only added and never removed. In long-lived sessions (notably when `popOnAdd=false`), this can leave repositories hidden even after the corresponding workspace is deleted, until the component unmounts.

## Issue Context
The set exists to immediately hide a just-added repo before `revalidate()` refreshes the cached scan results. After refresh (or when storage state changes), the set should be reconciled so it doesn’t become a persistent, stale filter.

## Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[144-170]
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-175]

## Suggested fix approach
- After a successful `revalidate()` in `finishAdd`, clear `addedDirectoryKeys` (or remove only the directory you added) so the suppression is strictly temporary.
- Alternatively (more robust): add an effect that prunes `addedDirectoryKeys` against the current persisted workspace directories (or against the latest `data`) so directories become visible again if the workspace is removed while the view remains mounted.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Targeted search blocked on cache ✓ Resolved 🐞 Bug ☼ Reliability
Description
DiscoverGitReposView skips running discoverGitReposForQueryAsync whenever the cached scan data
is still undefined (loading) or failed, so typed path/name probing cannot run until the baseline
scan completes successfully. This undermines the new “type to targeted search” behavior and can make
discovery unusable after a baseline scan error.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R95-101]

+  useEffect(() => {
+    const query = searchText.trim();
+    setTargetedLoadingQuery(null);
+    if (!query || !data) {
+      setTargetedSearch(null);
+      return;
+    }
Relevance

●●● Strong

Team fixes undefined-data gating; PR133 accepted requiring data loaded before actions to avoid
broken UX.

PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The effect currently returns early when data is undefined, even though usePromise can have
data undefined while loading or when an error occurs; this prevents the targeted async search from
ever starting in those states.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[85-93]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[95-101]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[138-140]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DiscoverGitReposView` currently short-circuits targeted searches when `data` is falsy, which prevents typed targeted discovery during baseline scan loading and after baseline scan failures.

### Issue Context
Targeted search (`discoverGitReposForQueryAsync`) is independent of the cached scan and should be able to run even when the cached scan is still loading or has errored.

### Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[95-101]
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[138-165]

### Suggested fix
- Change the `useEffect` gate from `if (!query || !data)` to `if (!query)`.
- In the `filtered` `useMemo`, treat cached results as `const cached = data ?? []` so merging works even when `data` is undefined.
- Keep the existing de-dupe logic and abort handling intact.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Blanket catch in repoCandidateFromPathQuery ✓ Resolved 📘 Rule violation ☼ Reliability
Description
repoCandidateFromPathQuery uses catch { return null; }, which hides unexpected exceptions (e.g.,
permission or argument issues) instead of handling only expected failure modes. This violates the
requirement to avoid blanket catches.
Code

QuickShell.Raycast/src/lib/git-repo-discovery.ts[R279-285]

+  try {
+    if (!statSync(candidate).isDirectory()) {
+      candidate = path.dirname(candidate);
+    }
+  } catch {
+    return null;
+  }
Relevance

●●● Strong

Avoiding blanket catch is enforced historically (PR #133 accepted changes away from catch {}).

PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2392968 forbids blanket catch blocks. The new/modified path-probing logic wraps
statSync with catch { return null; }, swallowing all exception types without discrimination.

Rule 2392968: Do not use blanket catch blocks; catch only expected exception types
QuickShell.Raycast/src/lib/git-repo-discovery.ts[279-285]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`repoCandidateFromPathQuery` uses a blanket `catch { return null; }` around `statSync`, which can silently hide unexpected failures.

## Issue Context
The compliance rule requires catching only expected exception types. In Node/TS, a practical approach is:
- `catch (error)`
- narrow to `NodeJS.ErrnoException`
- only treat specific `error.code` values as expected (e.g., `ENOENT`, `ENOTDIR`), otherwise rethrow.

## Fix Focus Areas
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[279-285]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Blanket catch in targeted search ✓ Resolved 📘 Rule violation ☼ Reliability
Description
discover-git-repos-view.tsx swallows all exceptions with a blanket catch {} during targeted repo
discovery, which can hide unexpected failures and make debugging/auditing error handling harder. The
compliance rule requires catching only expected exception types/conditions.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R117-120]

+        } catch {
+          if (!cancelled) {
+            setTargetedSearch({ query, repos: [] });
+          }
Relevance

●●● Strong

Team accepted narrowing/avoiding blanket catches (replace catch {} request accepted in PR #133).

PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2392968 forbids blanket catch blocks. The targeted search effect uses `catch { ...
} without capturing/narrowing the error, and continues on the fallback path (repos: []`) for any
thrown exception.

Rule 2392968: Do not use blanket catch blocks; catch only expected exception types
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[107-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The targeted discovery flow uses a blanket `catch {}` which swallows unexpected exceptions.

## Issue Context
Compliance requires catching only expected exception types/conditions. In TypeScript, this usually means `catch (error)` plus explicit narrowing/handling for known error shapes (e.g., `NodeJS.ErrnoException` codes), and rethrowing anything unexpected.

## Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[107-125]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Refresh failure allows duplicates ✓ Resolved 🐞 Bug ≡ Correctness
Description
In finishAdd(), the just-added directory is removed from addedDirectoryKeys in a finally block
even when revalidate() fails, so the stale cached discovery list can show the repo again and let
the user add it twice. Storage upsertWorkspace() only upserts by workspace id, so a second add
will persist a duplicate workspace pointing at the same directory.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R178-187]

+    try {
+      await revalidate();
+    } catch (refreshError) {
+      await showStorageFailure("Refresh git repositories", refreshError);
+    } finally {
+      setAddedDirectoryKeys((current) => {
+        const next = new Set(current);
+        next.delete(addedKey);
+        return next;
+      });
Relevance

●● Moderate

No direct precedent on deduping by directory; team often accepts duplicate-prevention/correctness
fixes (PRs 51,133).

PR-#51
PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
filtered is computed from cached data and only hides candidates via addedDirectoryKeys; if
revalidate() fails, data may remain unchanged, and the unconditional deletion in finally
re-exposes the repo. A subsequent Add action creates a new workspace ID and upsertWorkspace() will
append another workspace entry rather than dedupe by directory.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[144-170]
QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-188]
QuickShell.Raycast/src/lib/storage.ts[391-426]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`finishAdd()` always deletes `addedKey` from `addedDirectoryKeys` in `finally`, even when `revalidate()` throws. When refresh fails, `data` can remain stale and still contains the just-added repo; removing the suppression makes it visible again and allows another Add action, which will persist a duplicate workspace.

## Issue Context
- The list is derived from `data ?? []` and is only temporarily filtered by `addedDirectoryKeys`.
- `upsertWorkspace()` only merges by `id`, not by `directory`, so re-adding generates a second workspace record.

## Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[172-188]

### Suggested fix approach
- Only remove `addedKey` from `addedDirectoryKeys` after a *successful* `revalidate()` (e.g., set a `refreshOk` flag, or move deletion out of `finally`).
- Keep the rest of the flow (`onWorkspaceAdded`, `pop`) executing even if refresh fails (as it does today).
- Optional: if you want eventual cleanup, clear the suppression on the next successful `revalidate()` or when unmounting, but avoid re-showing the repo while the cached list is stale.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Depth wastes visit budget ✓ Resolved 🐞 Bug ➹ Performance
Description
discoverGitReposAsync counts each dequeued directory against visited before the MAX_DEPTH
guard, and it still enqueues children at depth === MAX_DEPTH (creating MAX_DEPTH + 1 items that
are immediately skipped). With the new maxVisited cap used by targeted searches, deep/broad trees
can burn the visit budget on guaranteed-to-be-skipped nodes and reduce scan coverage.
Code

QuickShell.Raycast/src/lib/git-repo-discovery.ts[R98-107]

  async function worker(): Promise<void> {
-    while (results.length < MAX_REPOS && scanned < MAX_SCANNED) {
+    while (!signal?.aborted && results.length < maxRepos && scanned < maxScanned && visited < maxVisited) {
      const work = queue.shift();
      if (!work) {
        return;
      }
+      visited += 1;
      if (work.depth > MAX_DEPTH) {
        continue;
      }
Relevance

●● Moderate

No direct historical evidence for depth/visited budget optimization; repo-discovery perf changes
have mixed priority (e.g., PR51).

PR-#51

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The worker loop both (1) stops when visited reaches maxVisited and (2) increments visited
before depth filtering; combined with unconditional child enqueue, this creates many depth>MAX_DEPTH
queue entries that will be popped, counted, and immediately skipped.

QuickShell.Raycast/src/lib/git-repo-discovery.ts[98-106]
QuickShell.Raycast/src/lib/git-repo-discovery.ts[143-154]
QuickShell.Raycast/src/lib/git-repo-discovery.ts[158-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new `visited/maxVisited` budget can be consumed by work items that are guaranteed to be skipped due to `MAX_DEPTH`.

### Issue Context
- `visited` is used as a hard stop condition for targeted scans (`discoverGitReposForQueryAsync` sets a finite default `maxVisited`).
- The worker currently enqueues children even when `work.depth === MAX_DEPTH`, which creates `depth === MAX_DEPTH + 1` work items that will be discarded but still count toward `visited`.

### Fix Focus Areas
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[98-107]
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[143-155]
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[158-167]

### Suggested fix
- Do not enqueue children when `work.depth >= MAX_DEPTH` (e.g., guard the `readdir`/enqueue block with `if (work.depth < MAX_DEPTH)`), while still allowing the `.git` check at `depth === MAX_DEPTH`.
- Optionally, consider moving `visited += 1` to after the `work.depth > MAX_DEPTH` early-continue so skipped-over-depth items don’t consume budget.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Targeted scan not cancellable ✓ Resolved 🐞 Bug ➹ Performance
Description
DiscoverGitReposView’s debounced targeted search only cancels state updates; once started,
discoverGitReposForQueryAsync continues scanning even after the query changes. Rapid typing can
therefore run multiple overlapping targeted scans (up to 20k directories each), causing avoidable
disk/CPU load and UI slowdown.
Code

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[R95-126]

+  useEffect(() => {
+    const query = searchText.trim();
+    if (!query || !data) {
+      setTargetedSearch(null);
+      setTargetedLoadingQuery(null);
+      return;
+    }
+
+    let cancelled = false;
+    const timer = setTimeout(() => {
+      setTargetedLoadingQuery(query);
+      void (async () => {
+        try {
+          const existing = await storage.getWorkspaces();
+          const existingDirs = new Set(existing.map((workspace) => workspace.directory.toLowerCase()));
+          const extraRoots = searchRootsFromWorkspaces(existing.map((workspace) => workspace.directory));
+          const repos = (await discoverGitReposForQueryAsync(query, extraRoots)).filter(
+            (repo) => !existingDirs.has(repo.directory.toLowerCase()),
+          );
+          if (!cancelled) {
+            setTargetedSearch({ query, repos });
+          }
+        } catch {
+          if (!cancelled) {
+            setTargetedSearch({ query, repos: [] });
+          }
+        } finally {
+          if (!cancelled) {
+            setTargetedLoadingQuery(null);
+          }
+        }
+      })();
Relevance

●● Moderate

No precedent for aborting in-flight scans; prior discovery perf refactors often rejected (PR #51).

PR-#51

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The effect’s cleanup only clears the debounce timer and flips a local boolean, but the async
discovery call has no abort mechanism, so scans keep running after a query changes. The discovery
implementation performs filesystem traversal in a loop until caps are reached, which can overlap
across multiple in-flight queries.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx[95-133]
QuickShell.Raycast/src/lib/git-repo-discovery.ts[67-148]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The targeted search effect cancels the debounce timer and prevents stale `setState`, but it cannot stop the underlying filesystem scan. If a user types multiple queries quickly (each exceeding the 250ms debounce), multiple scans can run concurrently.

### Issue Context
`discoverGitReposForQueryAsync()` delegates to `discoverGitReposAsync()` which loops over filesystem stats/readdir calls with no cancellation checks.

### Fix Focus Areas
- QuickShell.Raycast/src/components/discover-git-repos-view.tsx[95-133]
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[67-148]
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[154-176]

### Suggested fix
- Introduce an `AbortSignal` (or equivalent cancellation token) parameter to `discoverGitReposForQueryAsync` / `discoverGitReposAsync`.
- In the React effect, create an `AbortController` per search; abort it in the cleanup.
- In `discoverGitReposAsync`, periodically check `signal.aborted` (before/after `stat`, before `readdir`, and inside loops) and return early when aborted.
- Optionally ensure only one targeted scan runs at a time (e.g., serialize targeted scans) to prevent I/O storms.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Branch load errors disable form ✓ Resolved 🐞 Bug ☼ Reliability
Description
SetTargetBranchForm ignores usePromise errors; if branch loading throws (e.g., LocalStorage read
failure), branchChoices stays undefined and the submit action is disabled with no error message or
retry path. This can leave users unable to set/switch a branch target from the form.
Code

QuickShell.Raycast/src/components/set-target-branch-form.tsx[R24-30]

+  const { data: branchChoices, isLoading } = usePromise(async () => {
+    const branches = await listLocalBranches(directory);
+    const worktreeKey = await resolveWorktreeKey(directory);
+    const target = worktreeKey ? await storage.getBranchTarget(worktreeKey) : null;
+    return { branches, target };
+  }, [directory]);
+
Relevance

●● Moderate

No direct precedent for handling usePromise error state in forms; evidence insufficient.

PR-#112
PR-#133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The form only reads data and isLoading from usePromise, so rejected loads are silent and leave
branchChoices undefined. The submit action is disabled based on `branchChoices?.branches.length ??
0`, which evaluates to 0 when branchChoices is undefined, preventing any submit in that state.

QuickShell.Raycast/src/components/set-target-branch-form.tsx[24-30]
QuickShell.Raycast/src/components/set-target-branch-form.tsx[102-124]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new branch preloading uses `usePromise` but does not read or display `error`. When the promise rejects, `branchChoices` remains undefined and the form’s submit action is disabled, leaving no recovery.

### Issue Context
`listLocalBranches` itself returns `[]` on git failures, but `resolveWorktreeKey` / storage reads can still reject, and Raycast LocalStorage can error.

### Fix Focus Areas
- QuickShell.Raycast/src/components/set-target-branch-form.tsx[24-30]
- QuickShell.Raycast/src/components/set-target-branch-form.tsx[102-124]

### Suggested fix
- Destructure and handle `error` from `usePromise`:
 - Show a `Form.Description` (or toast) explaining that branches couldn’t be loaded.
 - Provide a retry action (e.g., `revalidate`) or reintroduce a manual fallback input (TextField) when `error` is set.
- Consider changing the Submit disabled condition to allow manual entry in error states (rather than hard-disabling when `branchChoices` is undefined).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



ℹ️ Informational

14. Unlimited repos may overrun budget ✓ Resolved 🐞 Bug ➹ Performance
Description
discoverGitReposForQueryAsync sets maxRepos to Infinity, but the scan budget (maxScanned) only
increments for non-repo directories; repo directories (including ones filtered out by the query) do
not consume the budget. In repo-dense trees this can make targeted searches inspect far more
directories than the MAX_TARGETED_SCANNED limit suggests and can return extremely large result sets.
Code

QuickShell.Raycast/src/lib/git-repo-discovery.ts[R169-175]

+  return discoverGitReposAsync(extraRoots, {
+    concurrency: options?.concurrency,
+    maxRepos: Number.POSITIVE_INFINITY,
+    maxScanned: options?.maxScanned ?? MAX_TARGETED_SCANNED,
+    query,
+    rootDirectories: options?.rootDirectories,
+  });
Relevance

● Weak

Typed search intentionally uncaps repo results; discovery performance change requests were rejected
(PR #51).

PR-#51

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Targeted search explicitly removes the repo-result cap (maxRepos: Infinity). The scan loop only
increments scanned after the .git check, so repo directories don’t contribute to the scan
budget, meaning repo-heavy trees can do more work than the maxScanned option suggests.

QuickShell.Raycast/src/lib/git-repo-discovery.ts[112-120]
QuickShell.Raycast/src/lib/git-repo-discovery.ts[154-176]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Targeted searches intentionally remove the 50-result cap by setting `maxRepos: Infinity`, but the `maxScanned` counter only advances when a directory is *not* a repo. In directories containing many repos, the scan can do a lot more work than the `MAX_TARGETED_SCANNED` name implies.

### Issue Context
This may be acceptable by design (comment says non-repo directories consume scan budget), but it can surprise callers because `maxScanned` reads like a global traversal cap.

### Fix Focus Areas
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[112-120]
- QuickShell.Raycast/src/lib/git-repo-discovery.ts[154-176]

### Suggested fix
Choose one:
- If `maxScanned` is intended as a total-work cap, increment it for every visited directory (including repos and query-filtered repos) or introduce a separate `visited` counter.
- If the current semantics are intended, rename/document the option to something like `maxNonRepoScanned` and consider adding a soft `maxRepos` cap (e.g., 500/1000) to prevent huge result lists.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Powered by Qodo

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
Comment thread QuickShell.Raycast/src/lib/git-repo-discovery.ts
Comment thread QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx
Comment thread QuickShell.Raycast/src/components/set-target-branch-form.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

🍒 Ready to be cherry-picked — ✅ Merged (0) · ☑ Fixed (5)


🔗 Fix PR: #138

This fix PR was closed automatically. Its branch is preserved so you can cherry pick the changes into the original PR.

Prompt for coding agent

This is an automated fix prepared on a separate branch (#138). It is NOT applied to this PR.
To use it: review Fix PR #138 (https://github.com/tonythethompson/QuickShell/pull/138), evaluate each change critically against your local context, and cherry-pick the changes that are correct into this branch. Do not accept them blindly.
Process — 5 fixed
  • ☑ Fixed: Repo URL no longer seeded
  • ☑ Fixed: Blanket catch in repoCandidateFromPathQuery
  • ☑ Fixed: Blanket catch in targeted search
  • ☑ Fixed: Targeted scan not cancellable
  • ☑ Fixed: Branch load errors disable form

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx
Comment thread QuickShell.Raycast/src/lib/git-repo-discovery.ts
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d6a42fb

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 37e55f3

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

173-186: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Quick Add is no longer instant — give the user feedback.

buildWorkspaceFromRepo now runs the full seed, and resolveWorkspaceSetupSuggestions reaches out to the Suggest CLI before falling back to local heuristics. That's a multi-second blocking gap between pressing Add Workspace and the success toast, with nothing on screen. The Review path at Line 47 gets a "Preparing workspace…" state; quick add gets silence.

⏳ Proposed fix
   async function handleQuickAdd(directory: string, name: string, remoteUrl?: string | null) {
+    const preparing = await showToast({
+      style: Toast.Style.Animated,
+      title: "Preparing workspace…",
+      message: name,
+    });
     try {
       const workspace = await buildWorkspaceFromRepo(directory, name, remoteUrl);
       await storage.upsertWorkspace(workspace);
       await showToast({
         style: Toast.Style.Success,
         title: "Workspace added",
         message: workspace.name,
       });
       await finishAdd(workspace);
     } catch (addError) {
       await showStorageFailure("Add workspace", addError);
+    } finally {
+      preparing.hide();
     }
   }
🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx` around lines
173 - 186, The handleQuickAdd flow provides no feedback while
buildWorkspaceFromRepo performs its potentially long-running setup. Add the same
“Preparing workspace…” progress state used by the Review path before awaiting
buildWorkspaceFromRepo, and ensure it is dismissed or replaced appropriately
when the operation completes or fails before showing the existing success or
failure feedback.
🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 95-136: Extract the shared workspace-derived setup currently
duplicated in the targeted-search effect and the usePromise loader: computing
existing workspace directory keys and search roots. Reuse this helper in both
paths, including the existing-directory filtering, so the definition of
already-added repositories remains consistent.

In `@QuickShell.Raycast/src/components/set-target-branch-form.tsx`:
- Around line 126-137: Update the branch-selection conditional in the set-target
form so the dropdown fallback is rendered only after branchChoices has resolved
and contains branches; while branchChoices is still undefined, preserve the
loading/manual-entry flow instead of mounting an item-less Form.Dropdown. Keep
the existing error and empty-branches handling unchanged.

In `@QuickShell.Raycast/src/lib/git-repo-discovery.ts`:
- Around line 322-328: Update the error handling around statSync in
discoverGitReposForQueryAsync to treat EACCES and EPERM like ENOENT and ENOTDIR,
returning null so discovery continues normally. Preserve rethrowing for all
other unexpected errors.

---

Outside diff comments:
In `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 173-186: The handleQuickAdd flow provides no feedback while
buildWorkspaceFromRepo performs its potentially long-running setup. Add the same
“Preparing workspace…” progress state used by the Review path before awaiting
buildWorkspaceFromRepo, and ensure it is dismissed or replaced appropriately
when the operation completes or fails before showing the existing success or
failure feedback.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e876b9f6-7b10-4afe-895e-0d46529f7664

📥 Commits

Reviewing files that changed from the base of the PR and between fcb419e and 37e55f3.

📒 Files selected for processing (10)
  • QuickShell.Raycast/src/__tests__/discovered-workspace-seed.test.ts
  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/components/workspace-form.tsx
  • QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
  • QuickShell.Raycast/src/open-workspace.tsx
  • docs/architecture/forms.md
  • docs/architecture/git-and-discover.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/open-workspace.tsx
  • QuickShell.Raycast/src/__tests__/discovered-workspace-seed.test.ts
  • QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/workspace-form.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🪛 LanguageTool
docs/architecture/git-and-discover.md

[grammar] ~102-~102: Ensure spelling is correct
Context: ...paces point at same path. 2. Baseline discover is best-effort and capped — not a full-...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔍 Remote MCP

Additional review context for PR #137

  • PR #137 is open and titled “Improve Raycast discovery and workspace setup UX”; its stated scope is full-seed discover/add parity, searchable command suggestions, uncapped typed repo search with exact-path probing, and a searchable Switch Branch flow. citeturn0search_pull_requeststurn0search_pull_requests
  • createWorkspaceFromDiscoveredGitRepo now centralizes discovered-workspace creation: it trims directory/name, falls back to deriveNameFromDirectory, converts suggestion tasks into launch rows, falls back to tryGetGitRemoteUrl(directory) when remoteUrl is missing, copies devServerUrl if present, and maps a companion seed with openOnLaunch: true. It returns normalizeWorkspace, which the new test relies on to supply a usable blank launch when no tasks exist. citeturn0pull_request_read
  • discoverGitReposForQueryAsync adds targeted discovery behavior: it normalizes query text by trimming, stripping quotes, and lowercasing; returns immediately for an already-aborted signal; bypasses tree scanning for an exact absolute-path repo match by walking ancestors; and otherwise runs a larger targeted scan with maxRepos = Infinity but bounded maxScanned/maxVisited budgets. discoverGitReposAsync also now accepts DiscoveryOptions including rootDirectories, query, and signal. citeturn0pull_request_read
  • The Discover Git Repos UI now runs a debounced targeted search with AbortController, merges cached and targeted results with directory de-duplication, and uses the full workspace seed for Quick Add instead of the former light builder. citeturn0pull_request_read
  • The branch picker now auto-loads local branches and the saved target, defaults to the saved target if it still exists (otherwise the first branch), shows a searchable dropdown when branches are available, and falls back to manual entry plus retry/error UI when loading fails. The parent action label was renamed from “Set Target Branch…” to “Switch Branch…”. citeturn0pull_request_read
  • Docs were updated to describe the new Raycast behavior: full-seed forms use a searchable, nonpersistent Command suggestions dropdown, Raycast discover/search now distinguishes capped baseline discovery from typed searches, and the branch picker is documented as a searchable local-branch dropdown. citeturn0pull_request_read
  • Related context: PR #112 introduced the Raycast-local branchTargets / blockDirtyBranchSwitch gate, and PR #120 added Raycast macOS discovery support; both overlap with the areas changed here. citeturn0search_pull_requeststurn0search_pull_requests
🔇 Additional comments (23)
QuickShell.Raycast/src/lib/git-repo-discovery.ts (5)

43-57: LGTM!


72-104: LGTM!

Also applies to: 115-149


161-213: LGTM!


253-255: LGTM!


278-303: LGTM!

QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts (3)

1-14: LGTM!


126-149: LGTM!


111-124: 📐 Maintainability & Code Quality

No change needed The Raycast test job runs on windows-latest and macos-latest, so these cases already stay within the supported platform gate.

			> Likely an incorrect or invalid review comment.
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (2)

3-14: LGTM!

Also applies to: 29-40


138-163: LGTM!

Also applies to: 194-209

docs/architecture/git-and-discover.md (1)

71-71: LGTM!

Also applies to: 80-81, 102-102

QuickShell.Raycast/src/lib/discovered-workspace-seed.ts (2)

37-65: LGTM!


25-35: 🗄️ Data Integrity & Integration

No issue here same-as-previous is a supported persisted launch value and is resolved later in launch selection/execution, so this path should keep it intact.

			> Likely an incorrect or invalid review comment.
QuickShell.Raycast/src/__tests__/discovered-workspace-seed.test.ts (2)

8-30: LGTM!

Also applies to: 44-59


32-42: 🎯 Functional Correctness

No issuederiveNameFromDirectory already splits on both \ and /, so this path resolves to empty-repo regardless of host platform.

			> Likely an incorrect or invalid review comment.
docs/architecture/forms.md (1)

32-33: LGTM!

QuickShell.Raycast/src/components/set-target-branch-form.tsx (3)

2-3: LGTM!

Also applies to: 24-34, 36-36


96-105: LGTM!


107-125: LGTM!

QuickShell.Raycast/src/components/workspace-form.tsx (3)

46-46: LGTM!

Also applies to: 698-714


725-735: LGTM!


715-722: 🎯 Functional Correctness

No cross-half encodePillKey collision here. The seed/leftover split already removes seed-command matches from the leftover list, so this render path can’t produce the same taskType + command pill from both halves. Any duplicate pills would have to come from upstream input, not this merge.

			> Likely an incorrect or invalid review comment.
QuickShell.Raycast/src/open-workspace.tsx (1)

1149-1153: LGTM!

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx
Comment thread QuickShell.Raycast/src/components/set-target-branch-form.tsx Outdated
Comment thread QuickShell.Raycast/src/lib/git-repo-discovery.ts
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 35e8833

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
QuickShell.Raycast/src/components/set-target-branch-form.tsx (1)

108-114: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Match the required “Switch Branch…” flow label.

The submit action currently says Switch Branch; the PR objective specifies Switch Branch….

- <Action.SubmitForm title="Switch Branch" onSubmit={handleSubmit} disabled={isLoading} />
+ <Action.SubmitForm title="Switch Branch…" onSubmit={handleSubmit} disabled={isLoading} />
🤖 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 `@QuickShell.Raycast/src/components/set-target-branch-form.tsx` around lines
108 - 114, Update the Action.SubmitForm title in the Form actions to use the
required “Switch Branch…” label, preserving its existing onSubmit and disabled
behavior.
QuickShell.Raycast/src/lib/git-repo-discovery.ts (1)

179-201: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Apply the platform gate before direct path probing.

An absolute-path query returns a repository on unsupported platforms because it reaches repoCandidateFromPathQuery before discoverGitReposAsync applies its Windows/macOS gate. Return [] at the start of this function on unsupported platforms.

Proposed fix
 export async function discoverGitReposForQueryAsync(
   searchText: string,
   extraRoots: string[] = [],
   options?: {
@@
   },
 ): Promise<GitRepoCandidate[]> {
+  if (!isWindowsPlatform() && !isMacPlatform()) {
+    return [];
+  }
   if (options?.signal?.aborted) {
     return [];
   }
🤖 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 `@QuickShell.Raycast/src/lib/git-repo-discovery.ts` around lines 179 - 201,
Update discoverGitReposForQueryAsync to return an empty result immediately on
unsupported platforms, before normalizeDiscoveryQuery or
repoCandidateFromPathQuery runs. Reuse the same Windows/macOS platform-gating
condition used by discoverGitReposAsync.

Source: MCP tools

QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

155-166: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove newly added directories from targeted results.

finishAdd refreshes only cached discovery. For the active query, targetedSearch.repos is still merged here, so hub mode can continue showing a repository immediately after it was added.

Track added directory keys locally and exclude them from targetedMatches (or invalidate/cancel the active targeted result set when adding).

🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx` around lines
155 - 166, Update the result-merging logic in the discover view to track
directory keys added by finishAdd and exclude those keys from targetedMatches
before merging. Preserve the existing cachedMatches deduplication, and ensure
newly added repositories no longer appear for the active query.
🤖 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.

Outside diff comments:
In `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 155-166: Update the result-merging logic in the discover view to
track directory keys added by finishAdd and exclude those keys from
targetedMatches before merging. Preserve the existing cachedMatches
deduplication, and ensure newly added repositories no longer appear for the
active query.

In `@QuickShell.Raycast/src/components/set-target-branch-form.tsx`:
- Around line 108-114: Update the Action.SubmitForm title in the Form actions to
use the required “Switch Branch…” label, preserving its existing onSubmit and
disabled behavior.

In `@QuickShell.Raycast/src/lib/git-repo-discovery.ts`:
- Around line 179-201: Update discoverGitReposForQueryAsync to return an empty
result immediately on unsupported platforms, before normalizeDiscoveryQuery or
repoCandidateFromPathQuery runs. Reuse the same Windows/macOS platform-gating
condition used by discoverGitReposAsync.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74282abf-1c03-4cb5-aa55-e99c03603635

📥 Commits

Reviewing files that changed from the base of the PR and between 37e55f3 and 35e8833.

📒 Files selected for processing (3)
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: Analyze Raycast TypeScript with CodeQL
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
🔍 Remote MCP DeepWiki, GitHub Copilot

Additional review context

  • Launch-row semantics changed across hosts: related PR #114 establishes that null commands represent explicit “Open in terminal” rows, while blank command drafts are discarded. Suggestion pills must therefore target only Command rows and never overwrite terminal-only rows.

  • Workspace seed behavior is covered by new tests: discovered seeds preserve task commands/types, companion metadata, dev-server URL, and repository URL; empty task lists rely on normalization to create one command: null launch; missing remotes are read from .git/config.

  • Discovery now has two distinct modes: the cached baseline remains capped, while targeted search uses maxRepos = Infinity, finite scan/visit budgets, cancellation, and direct ancestor probing for absolute paths. The tests explicitly verify 55 results beyond the normal cap, aborted searches returning no results, and maxVisited: 11 yielding 10 repositories.

  • Related PR #120 introduced the platform gate used by this discovery stack: discovery is enabled only on Windows/macOS, with platform-specific search roots.

  • DeepWiki could not provide repository context: tonythethompson/QuickShell was not indexed/found.

🔇 Additional comments (3)
QuickShell.Raycast/src/lib/git-repo-discovery.ts (1)

48-95: LGTM!

Also applies to: 312-341

QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

42-96: LGTM!

Also applies to: 101-141

QuickShell.Raycast/src/components/set-target-branch-form.tsx (1)

24-105: LGTM!

Also applies to: 126-137

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 278b325

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/lib/git-repo-discovery.ts (1)

127-130: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Reserve scan capacity before asynchronous classification.

At Line 99, workers are admitted while scanned < maxScanned, but scanned is incremented only after await fs.stat(...) at Line 111. With concurrency greater than one, multiple workers can pass the guard and later increment the counter, exceeding the configured scan budget. Reserve an in-flight scan slot before the await, release it for repositories/files, and consume it only for non-repository directories.

🤖 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 `@QuickShell.Raycast/src/lib/git-repo-discovery.ts` around lines 127 - 130,
Update the worker admission and classification flow in git-repo-discovery so
scan capacity is reserved before the asynchronous fs.stat call. Use the scanned
counter or a dedicated in-flight reservation to prevent concurrent workers from
exceeding maxScanned; release the reservation for repositories/files and retain
it only for non-repository directories, while preserving the maxRepos limit.
🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 162-167: Apply the addedDirectoryKeys exclusion to cachedMatches
as well as targetedMatches so directories recorded by finishAdd cannot remain
selectable. Reuse the existing directory-key filtering logic or perform one
shared filtering pass for both result sources, while preserving the seen-based
deduplication behavior.

---

Outside diff comments:
In `@QuickShell.Raycast/src/lib/git-repo-discovery.ts`:
- Around line 127-130: Update the worker admission and classification flow in
git-repo-discovery so scan capacity is reserved before the asynchronous fs.stat
call. Use the scanned counter or a dedicated in-flight reservation to prevent
concurrent workers from exceeding maxScanned; release the reservation for
repositories/files and retain it only for non-repository directories, while
preserving the maxRepos limit.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80522f74-eb7f-4d40-bc99-355ee0432f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 35e8833 and 278b325.

📒 Files selected for processing (4)
  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: Analyze Raycast TypeScript with CodeQL
  • GitHub Check: Raycast lint, test, and build
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/components/set-target-branch-form.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🔍 Remote MCP DeepWiki, GitHub Copilot

Relevant review context

  • PR #114 established the launch-row contract: command: null means an explicit terminal-only launch; blank command drafts are discarded, and suggestion pills must never overwrite terminal-only rows.
  • PR #112 introduced the related branch-gate behavior: Raycast stores branch targets locally, validates branch names, uses git switch --, and defaults blockDirtyBranchSwitch to enabled.
  • Earlier review findings on this PR identified and resolved:
    • missing .git/config remote fallback;
    • cancellation of superseded targeted scans;
    • targeted search operating independently of baseline-cache loading/failure;
    • depth-limit nodes consuming visit budget;
    • branch-load error/retry/manual-entry handling;
    • blanket exception handling in path probing and targeted search.
  • A remaining informational concern was recorded: targeted discovery sets maxRepos: Infinity, while maxScanned counts only non-repository directories; repository-dense trees can therefore perform more repository checks than the scan-budget name may imply.
  • Automated review metadata reports the changes are Raycast TypeScript/docs only; C#/Python contracts, SessionWorkflowStage, and managed-host lifecycle invariants are not implicated.
  • DeepWiki could not provide structural context because tonythethompson/QuickShell is not indexed.
🔇 Additional comments (4)
QuickShell.Raycast/src/lib/git-repo-discovery.ts (1)

98-126: LGTM!

Also applies to: 132-159, 179-215

QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts (1)

111-125: LGTM!

QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

30-39: LGTM!

Also applies to: 102-142, 171-193, 201-216

QuickShell.Raycast/src/components/set-target-branch-form.tsx (1)

24-34: LGTM!

Also applies to: 36-105, 107-137

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx
Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e181ae4

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

181-190: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable Quick Add while it’s in flight. handleQuickAdd does the full build/save path with no per-item pending guard, so the same repo can be added twice before the first run finishes. Track a pending key in component state, disable that action until finally, and clear it there.

🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx` around lines
181 - 190, Update handleQuickAdd to track the current repository’s pending key
in component state, using a stable per-item identifier. Set the key before
buildWorkspaceFromRepo and disable the corresponding Quick Add action while it
matches; clear it in a finally block so it is released on success or failure.

Source: MCP tools

🤖 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.

Outside diff comments:
In `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 181-190: Update handleQuickAdd to track the current repository’s
pending key in component state, using a stable per-item identifier. Set the key
before buildWorkspaceFromRepo and disable the corresponding Quick Add action
while it matches; clear it in a finally block so it is released on success or
failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f106635e-cec9-49fd-9d22-dc7854a136c0

📥 Commits

Reviewing files that changed from the base of the PR and between 278b325 and e181ae4.

📒 Files selected for processing (1)
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: .NET build and test
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
🔍 Remote MCP GitHub Copilot

Relevant review context

  • PR #137 is open and titled “Improve Raycast discovery and workspace setup UX.” It changes only Raycast TypeScript/TSX and documentation; automated checks found no C#/Python contracts, workflow-stage, or managed-host lifecycle impact.
  • Related PR #114 establishes the launch-row contract: command: null represents an explicit terminal-only launch; blank command drafts are discarded, and suggestions must not overwrite terminal-only rows.
  • Related PR #112 introduced Raycast-local branch targets, safe git switch -- usage, and the default-enabled dirty-branch gate.
  • Earlier findings were fixed for remote fallback, targeted-search cancellation, cache-independent searching, depth limits, branch-load recovery, and narrowed exception handling. Remaining informational concern: targeted search uses maxRepos: Infinity, while maxScanned counts only non-repository directories.
  • Current review feedback identifies two concrete points to verify:
    • Quick Add performs full seeding asynchronously without a “Preparing workspace…” progress indication.
    • Discovery’s concurrent workers may exceed maxScanned because capacity is checked before asynchronous stat classification but incremented afterward.
  • CI currently reports successful Raycast Windows and macOS lint/test/build jobs and successful TypeScript CodeQL; .NET build/test and C# CodeQL were still in progress at retrieval time.
🔇 Additional comments (1)
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

3-19: LGTM!

Also applies to: 29-39, 78-142, 144-170, 202-217

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

190-193: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Show progress while Quick Add prepares the workspace.

Quick Add now performs the full workspace-seeding flow, but the view exposes no busy state during this await. Users can interpret the action as failed or stalled, unlike the Review flow’s “Preparing workspace…” state. Track the active add and surface it through the list or action UI; clear it in finally.

Suggested direction
+ const [addingDirectory, setAddingDirectory] = useState<string | null>(null);

  async function handleQuickAdd(directory: string, name: string, remoteUrl?: string | null) {
+   setAddingDirectory(directory.toLowerCase());
    try {
      const workspace = await buildWorkspaceFromRepo(directory, name, remoteUrl);
      await storage.upsertWorkspace(workspace);
      ...
    } catch (addError) {
      await showStorageFailure("Add workspace", addError);
+   } finally {
+     setAddingDirectory(null);
    }
  }

Also include addingDirectory !== null in the rendered loading state or disable the active row’s action.

🤖 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 `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx` around lines
190 - 193, Update handleQuickAdd to track the directory currently being added,
set addingDirectory before buildWorkspaceFromRepo, and clear it in finally so
failures and successes reset the state. Use addingDirectory !== null in the
rendered loading state or disable the active row’s Quick Add action, matching
the Review flow’s “Preparing workspace…” behavior.

Source: MCP tools

🤖 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.

Outside diff comments:
In `@QuickShell.Raycast/src/components/discover-git-repos-view.tsx`:
- Around line 190-193: Update handleQuickAdd to track the directory currently
being added, set addingDirectory before buildWorkspaceFromRepo, and clear it in
finally so failures and successes reset the state. Use addingDirectory !== null
in the rendered loading state or disable the active row’s Quick Add action,
matching the Review flow’s “Preparing workspace…” behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3860e63c-028c-4735-a0c0-261d9ca2ac38

📥 Commits

Reviewing files that changed from the base of the PR and between e181ae4 and 7c4950e.

📒 Files selected for processing (1)
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: Analyze Raycast TypeScript with CodeQL
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
  • GitHub Check: Raycast lint, test, and build
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
🔍 Remote MCP GitHub Copilot

Additional review context

  • Quick Add has no progress indicator while full workspace seeding runs. handleQuickAdd awaits buildWorkspaceFromRepo directly; only the Review flow renders “Preparing workspace…”. This may leave Quick Add appearing unresponsive during suggestion/companion detection.

  • maxScanned is not a strict concurrent bound. Workers check scanned < maxScanned before asynchronous stat/classification, but increment scanned later. Multiple workers can therefore pass the check and collectively exceed the configured scan budget.

  • The discovered-workspace seed preserves the related launch-row contract: empty discovery results become a normalized blank launch, while non-empty suggestions are converted into ordered launch rows; repository URL falls back to the local Git remote.

  • Related PR #114 confirms the intended semantics: an explicit terminal-only launch is represented by a null command, while ordinary blank command drafts are discarded.

  • No DeepWiki or Context7 lookup was necessary: this PR is limited to Raycast TypeScript/TSX and documentation, with no C#/Python boundary, Avalonia binding, or dependency/API upgrade involved.

🔇 Additional comments (1)
QuickShell.Raycast/src/components/discover-git-repos-view.tsx (1)

173-183: LGTM!

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 7c4950e

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit ecbe5e6

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 5fcbb01

Comment thread QuickShell.Raycast/src/lib/git-repo-discovery.ts Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 21aad23

Comment thread QuickShell.Raycast/src/components/discover-git-repos-view.tsx Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2ea919c

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 696293c

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 811d50e

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts (1)

112-187: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Solid filesystem-backed regression coverage for the new targeted-search behaviors.

Verified the numbers by hand for the trickiest case: "bounds total visited repositories without restoring the result cap" (Line 169-186) — with a single worker, maxVisited: 11 against 1 root + 20 repo children yields exactly 10 matched repos before the visited budget trips, which lines up with the implementation's visited check at the top of the worker loop.

One gap worth adding: all abort coverage here is "already aborted before the call" (Line 160-167). A mid-scan cancellation test (abort the controller after the first tick, assert the scan stops short of the full tree) would validate the signal?.aborted checks sprinkled through the worker loop, not just the trivial pre-aborted case.

🧪 Suggested addition
it("stops mid-scan once aborted", async () => {
  const root = mkdtempSync(path.join(os.tmpdir(), "quickshell-git-midabort-"));
  try {
    for (let index = 0; index < 20; index += 1) {
      mkdirSync(path.join(root, `repo-${index.toString().padStart(2, "0")}`, ".git"), { recursive: true });
    }
    const controller = new AbortController();
    setTimeout(() => controller.abort(), 0);

    const repos = await discoverGitReposForQueryAsync("repo-", [], {
      rootDirectories: [root],
      concurrency: 1,
      signal: controller.signal,
    });

    expect(repos.length).toBeLessThan(20);
  } finally {
    rmSync(root, { recursive: true, force: true });
  }
});
🤖 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 `@QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts` around lines 112
- 187, Add a mid-scan cancellation test alongside the existing
discoverGitReposForQueryAsync abort coverage. Build a temporary tree with
multiple repositories, schedule the AbortController to abort asynchronously
after scanning begins, invoke discoverGitReposForQueryAsync with concurrency 1
and the signal, and assert fewer than all repositories are returned while
retaining cleanup.
🤖 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.

Outside diff comments:
In `@QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts`:
- Around line 112-187: Add a mid-scan cancellation test alongside the existing
discoverGitReposForQueryAsync abort coverage. Build a temporary tree with
multiple repositories, schedule the AbortController to abort asynchronously
after scanning begins, invoke discoverGitReposForQueryAsync with concurrency 1
and the signal, and assert fewer than all repositories are returned while
retaining cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d1c6a08d-7ca6-40f8-8d7a-ad2f8bae326c

📥 Commits

Reviewing files that changed from the base of the PR and between e181ae4 and 811d50e.

📒 Files selected for processing (3)
  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Analyze Raycast TypeScript with CodeQL
  • GitHub Check: Analyze C# with CodeQL
  • GitHub Check: .NET build and test
  • GitHub Check: Performance harness (artifacts)
🧰 Additional context used
📓 Path-based instructions (1)
QuickShell.Raycast/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Maintain Raycast behavior in parity with Core launch behavior and use the project's Vitest test conventions.

Files:

  • QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts
  • QuickShell.Raycast/src/components/discover-git-repos-view.tsx
  • QuickShell.Raycast/src/lib/git-repo-discovery.ts
🔍 Remote MCP GitHub Copilot

Additional review context

  • Related PR #120 is merged and establishes that Raycast supports both Windows and macOS; repository discovery must therefore retain macOS behavior. Its CI added a macOS test job, and discovery returns results on both win32 and darwin.
  • The current master discovery implementation still has the legacy constraints: MAX_REPOS = 50, MAX_SCANNED = 2000, depth cutoff work.depth > MAX_DEPTH, and concurrent workers sharing mutable scanned/results state. This provides direct baseline context for reviewing the PR’s cap, depth, and concurrency changes.
  • The extension manifest currently declares both "Windows" and "macOS" platforms, with Node >=20, Vitest ^3.0.8, and Raycast Utils ^1.19.1; the PR’s discovery changes should remain compatible with this dual-platform runtime.
🔇 Additional comments (11)
QuickShell.Raycast/src/lib/git-repo-discovery.ts (4)

47-47: EACCES/EPERM/ENAMETOOLONG handling confirmed fixed via shared isExpectedFsLookupError.

Matches the previously requested fix; both the direct-path probe (repoCandidateFromPathQuery) and the async worker now route through the same expected-error set instead of hardcoding ENOENT/ENOTDIR.

Also applies to: 326-360


99-168: Reserve-before-yield scan budget accounting checks out.

Traced this by hand against both new tests: incrementing scanned before the await fs.stat (so sibling workers see the reservation and bail before issuing their own stat) and releasing it only for non-directories, aborted callers, and confirmed .git matches (leaving it permanently consumed for regular scanned directories) matches "Match Core: only non-repo directories consume the scan budget." Simulating the maxVisited: 11 test by hand also lands exactly on the expected 10 results. Good fix for the prior TOCTOU-style admission gap.


43-57: 🎯 Functional Correctness

No change needed for the targeted-search caps.


189-198: 🎯 Functional Correctness

No change needed The fallback scan already passes signal, maxScanned, maxVisited, and concurrency into discoverGitReposAsync, and that path enforces the abort and cap checks throughout.

QuickShell.Raycast/src/__tests__/git-repo-discovery.test.ts (2)

1-16: LGTM!


189-237: Good coverage for expected-vs-unexpected fs.stat failures and the concurrency-reservation invariant.

The "reserves scan capacity before concurrent stat calls" test (Line 209-236) is a strong regression guard — it directly exercises the exact race the reserve-before-yield pattern was built to prevent, with maxScanned: 1/concurrency: 4 and an instrumented fs.stat mock asserting maxActiveStats === 1.

QuickShell.Raycast/src/components/discover-git-repos-view.tsx (5)

30-40: Quick Add now builds the full workspace seed — matches PR objective.

buildWorkspaceFromRepo now resolves setup suggestions, dev server, and companion seed and delegates to createWorkspaceFromDiscoveredGitRepo, so Quick Add and "Review Before Adding" produce equivalent workspaces instead of Quick Add taking a thin shortcut.


78-114: addedDirectoryKeys reconciliation logic is correct.

Worked through the two relevant paths: on a successful revalidate(), freshly-fetched data already excludes the just-added directory (it's now a workspace), so the filter in this effect naturally drops that key since next is a strict subset check via cachedKeys.has(key). On a failed revalidate() (per the try/catch added in finishAdd), data doesn't change reference, so the effect doesn't re-run and the suppression key correctly survives — which is exactly what "reconciles temporary suppression after refresh" needs.


186-201: finishAdd now completes the add flow even when refresh fails — good resilience fix.

Wrapping revalidate() in its own try/catch ensures onWorkspaceAdded and pop() still run after a storage add succeeds, even if the subsequent list refresh errors out. This directly matches the PR objective "Ensures repository adds complete when refresh fails."


203-229: Duplicate quick-add prevention is race-safe.

The synchronous ref check-and-set (pendingQuickAddKeysRef) at the top of handleQuickAdd closes the window between a second click and the disabled prop's re-render, which a state-only guard wouldn't. The finally cleanup correctly clears both the ref and the mirrored state regardless of success/failure path.


268-268: LGTM!

Ensure discoverGitReposForQueryAsync stops early when cancelled after work starts, not only when already aborted.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 6ec2eda

Prevent concurrent workers from oversubscribing maxScanned, and keep the name-search cap-bypass test under Vitest's default timeout on Windows CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tonythethompson
tonythethompson enabled auto-merge (squash) July 29, 2026 12:17
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d8bec5b

@tonythethompson
tonythethompson merged commit d3739e5 into master Jul 29, 2026
10 of 11 checks passed
@tonythethompson
tonythethompson deleted the codex/raycast-discovery-ux branch July 29, 2026 12:22
@linear-code

linear-code Bot commented Jul 29, 2026

Copy link
Copy Markdown

TS-75

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.

1 participant