Use this to quickly open and switch VS Code/Cursor projects, and use AI assistant to analyze your code and chat with it instantly.
Spotlight-like quick open: press ⌃+⌘+R or click the menu bar icon to launch the Quick Switcher. Search and select a project to open or switch to it in VS Code or Cursor — even if the IDE is not running yet.
- Recent projects (white items): your latest VS Code/Cursor folders, workspaces, and recently opened files — read directly from IDE data, no extension required
- Working folder items (green items): first-level subfolders found by scanning a folder you choose (Settings → Working Directory)
- Git branch display: shows the current branch for each recently opened project
- Multi-word search across project names, paths, and branch names
- Supports VS Code and Cursor — switch between them in Settings → IDE Preference
- Remove items from the recent list by hovering and clicking "x"
Download: Latest release (notarized DMG) · Mac App Store (v1.0.33, does not include Claude Code sessions or git branch features)
CodeV can list, search, and resume Claude Code sessions. Press ⌃+⌘+R to open the Quick Switcher, then Tab to toggle to Sessions mode.
Simple rule: when running multiple sessions in the same project directory at the same time, give each running session a name. Closed sessions don't need names — they won't cause issues.
- Best: start with a name —
claude -n "my task"(orclaude --name "my task") - Or:
/renamein-session, then exit and resume (bareclaudeorclaude -rpicker sessions need this to be identifiable) - Temporary sessions: no need to rename — just close them when done. Only sessions that are actively running alongside other same-directory sessions need names.
- When resuming from terminal:
claude --resume <uuid>orclaude -r <uuid>are most reliable. Note:claude -r(interactive picker) does not update process args after selection — it behaves like bareclaudefor detection. CodeV itself always uses--resume <uuid>.
For the full same-cwd accuracy matrix (detection + switch by launch method and terminal), see the design doc.
Terminal support:
| Terminal | Switch method | Launch method | Notes |
|---|---|---|---|
| iTerm2 | Title match → TTY fallback | AppleScript new tab/window | Most reliable; cross-reference fixes detection for bare claude + /rename'd sessions |
| Ghostty | Title match → cwd fallback | AppleScript new tab/window | Needs /rename for same-cwd |
| cmux | Title match → cwd fallback | CLI new-workspace | Needs /rename for same-cwd; requires socket access in cmux Settings (automation or allowAll) |
- Select the code or text you want to get analyzed insight in any editor or even on web page.
- Press
⌘+Cto copy the selected code to your clipboard. - Press
⌃+⌘+Eto open the Code AI Assistant window, which will:- Create a floating window with the code from your clipboard
- Generate an insight using Anthropic Claude
- The window will display your code and start generating an insight.
- You can use the input text field to continue the discussion.
- You can custom your prompt on the menu bar.
Note: For a smooth demonstration workflow, make sure to copy your code to the clipboard before triggering the Code AI Assistant with ⌃+⌘+E.
You can click the toggle on the top bar and switch to Insight Split view mode.
When you customize the prompt as empty, you can still copy your code first, and trigger ⌃+⌘+E, it would still navigate to the AI Assistant view without triggering insight generation, then you can input your follow-up message to discuss with AI.
Trigger ⌃+⌘+C shortcut to launch pure AI chat mode, just like the Claude or ChatGPT desktop
- Syntax highlighting for various programming languages
- Streaming explanation that updates in real-time
- Automatic language detection
- Error handling
- The Code AI Assistant uses Claude API to generate explanations/insight.
- The API request is made from the main Electron process (not the renderer) for security.
- Explanations/Insights are streamed in real-time for a better user experience.
- The UI is a semi-transparent floating window that can be closed when not needed.
-
Make sure you have an Anthropic API key. You can get one from Anthropic's website.
-
Set up your API key on the menu bar (-> Setting -> API key setting), or add your API key to the
.envfile in the root directory:ANTHROPIC_API_KEY=your_api_key_here
- Do the following in the
root folder:yarn install- DB setup
- For the first time or every time db scheme changes, execute
yarn db:migrateto generate SQLite DB file (./prisma/dev.db) and generate TypeScript interface.yarn db:viewcan be used to view DB data.db:migratewill also automatically do this part,yarn installwill also include generated types in node_modules/.prisma/index.d.ts)
- For the first time or every time db scheme changes, execute
yarn start
CodeV uses its own SQLite database (via Prisma) for storing user settings, AI assistant settings, and conversation history. Recent projects data is read directly from VS Code/Cursor's state.vscdb.
- Development:
./prisma/dev.db - Production (non-MAS):
~/Library/Application\ Support/CodeV/dev.db - Production (MAS sandbox):
~/Library/Containers/com.lifeoverflow.switchv/Data/Library/Application\ Support/CodeV/dev.db
Note: paths with spaces require escaping with \ in the terminal (e.g. Application\ Support).
Database models and their current usage:
| Model | Purpose | Status |
|---|---|---|
| User | Stores workingFolder path |
Active |
| AIAssistantSettings | API key, custom prompt, IDE preference, left click behavior | Active |
| Conversation | AI chat conversation history | Active |
| Message | AI chat messages | Active |
| VSWindow | Legacy — was used by the old VS Code extension to send window records via HTTP. No longer written to since migrating to reading VS Code/Cursor's state.vscdb directly. |
Deprecated |
In VS Code Run and Debug, choose Electron: Main Process to launch and debug.
To debug render process, please directly set up breakpoints in the opened dev tool instead, which is what Electron official site recommends
Ref: https://www.electronjs.org/docs/latest/tutorial/application-debugging#renderer-process.
p.s. We had tried to use VS Code debugger setting for this, but it became invalid after migrating to the new version of Electron.
- package a mac app:
yarn make. Then you can move/copy out/CodeV-darwin-arm64/CodeV.app to your application folder and use it daily.
- Create certificates in Xcode → Settings → Accounts → Manage Certificates, or via Apple Developer website. You need:
Apple Distribution: Your Name (TEAM_ID)— for signing the app3rd Party Mac Developer Installer: Your Name (TEAM_ID)— for signing the pkg
- Download and import the certificates: double-click the
.cerfiles or use Keychain Access → File → Import Items. - Import the intermediate certificate: If code signing fails with "valid signing identity not found", you may be missing the intermediate certificate. Download Apple Worldwide Developer Relations Certification Authority and import it into Keychain Access (System keychain).
- Create a provisioning profile on the Apple Developer website → Profiles → Mac App Store distribution. Select the App ID and distribution certificate. No device registration is needed for MAS profiles (that's only for iOS/Ad Hoc). Download and save as
embedded.provisionprofilein the project root.
Ref: Electron MAS submission guide, Electron code signing
- Execute
yarn make_masto generate the app. - Execute
sh ./sign.shto convert app to pkg. - Use Transporter to upload the pkg to App Store Connect, then submit for review.
- Note: The MAS build runs in a sandbox. Users need to grant access to IDE data via IDE Settings → Grant Access so CodeV can read the recent projects list.
macOS 10.15+ requires notarization for apps distributed outside the App Store, otherwise Gatekeeper will block them.
- Create a "Developer ID Application" certificate on the Apple Developer website → Certificates → + → Developer ID Application. Choose G2 Sub-CA. You'll need a CSR file (Keychain Access → Certificate Assistant → Request a Certificate From a Certificate Authority → Save to disk).
- Import the certificate and its intermediate cert. If codesign fails with "unable to build chain to self-signed root", download the Developer ID G2 intermediate certificate (
DeveloperIDG2CA.cer) and import it:sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain DeveloperIDG2CA.cer - Create an App-Specific Password at appleid.apple.com → Sign-In and Security → App-Specific Passwords. This is used by
notarytool. - Set environment variables in
.env:APPLE_ID=your@email.com APPLE_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx APPLE_TEAM_ID=GL35G6YCWG
No provisioning profile is needed for non-MAS distribution.
- Execute
yarn maketo generate the app. - Execute
sh ./sign-notarize.sh— this will sign with Developer ID, create a DMG, submit for notarization (takes ~2-5 min), and staple the ticket. - The output
./out/CodeV.dmgis ready to distribute.
Key differences from MAS build: uses Developer ID (not Apple Distribution) certificate, no sandbox, hardened runtime required, entitlements are in notarize-parent.plist (main app: hardened runtime + network + file access) / notarize-child.plist (helpers: hardened runtime only). Since there's no sandbox, using a single plist for both would also work, but we split them for consistency with the MAS build's parent.plist / child.plist pattern and to follow the principle of least privilege.
Ref: Electron code signing & notarization
ref:
- prisma/prisma#8449
vercel/pkg#1508(we had use vercel/pkg to package server but we have decided to embed server to electron)
- shared in the FOSSASIA 2025 summit talk https://slides.com/grimmer/fossasia-2025-switchv-streamlining-developer-workflow-with-an-open-source-vs-code-launcher
- On MacOS, the created Electron BrowserWindow object may be destroyed by the system automatically somehow due to the resource management (e.g. running in the background for a while and memory is tight), so we need to check if a windows
isDestroyed()result when reusing them. - React UI takeaway
- Pay an attention to React closure trap. E.g. if we register some callback function in the
useEffect(()=>{ /*...*/}, []});, it may always use some initial state value, even its implementation is outside this useEffect, the solution is to use the useRef version of that state. - Updating state may not take an effect immediately. If you have some logic which is checking the value as some condition, you may update
useRefversion of that state when you update the state.
- Pay an attention to React closure trap. E.g. if we register some callback function in the