Discover macOS apps that aren't managed by Homebrew — and switch them over in one command.
Many of us install apps from .dmg files, direct downloads, or vendor websites. Over time this creates a patchwork of apps that don't benefit from Homebrew's unified update management (brew upgrade --cask). switch-to-brew finds those apps, shows you which ones have Homebrew cask equivalents, and lets you selectively adopt them — without reinstalling.
- 🔍 Smart discovery — scans
/Applicationsand~/Applications, filters out Apple system apps, Homebrew-managed apps, and installers/helpers - ⚡ Fast matching — ships with a curated mapping of 90+ popular apps to their Homebrew tokens; falls back to
brew search --caskfor the rest (with caching) - 🎯 Interactive selection — pick exactly which apps to switch using numbers, ranges, or "all"
- 🔒 Non-destructive by default — uses
brew install --cask --adoptto link your existing.appbundle into Homebrew tracking when a cask exists; formula-backed mappings are called out explicitly - 🏃 Dry-run mode — see exactly what would happen before committing
- 📋 Machine-readable output — TSV and JSON formats for scripting
- 🎨 Beautiful terminal UI — colors, spinners, and clean formatting (respects
NO_COLOR)
git clone https://github.com/matthewcorven/switch-to-brew.git
cd switch-to-brew
chmod +x switch-to-brew
./switch-to-brewgit clone https://github.com/matthewcorven/switch-to-brew.git
cd switch-to-brew
sudo make installThis installs to /usr/local/bin. Customise with PREFIX:
make install PREFIX="$HOME/.local"sudo make uninstallJust run it — it discovers apps, shows matches, and lets you pick:
$ switch-to-brew
▸ Scanning for applications...
▸ Found 35 unmanaged apps. Resolving Homebrew packages...
▸ Matched 28 of 35 apps to Homebrew packages.
# Application Package Source
──────────────────────────────────────────────────────────────────────────────────────
1 ChatGPT chatgpt manual
2 Docker docker manual
3 Google Chrome google-chrome manual
4 iTerm iterm2 manual
5 Microsoft Word microsoft-word manual
6 Obsidian obsidian manual
7 Signal signal manual
8 Visual Studio Code visual-studio-code manual
...
Select apps to switch to Homebrew:
Enter numbers separated by spaces (e.g. 1 3 5)
Ranges work too (e.g. 1-5 8 10-12)
Type "all" to select everything, "q" to cancel
❯ 1-4 6
▸ Selected 5 apps to switch:
...
Proceed with switching? [y/N] y
▸ Adopting ChatGPT via cask chatgpt...
✔ ChatGPT is now managed by Homebrew (chatgpt)
▸ Adopting Docker via cask docker...
✔ Docker is now managed by Homebrew (docker)
...
── Summary ─────────────────────────────────────
✔ 5 apps switched to Homebrew
switch-to-brew discover # Pretty table
switch-to-brew discover --json # JSON output
switch-to-brew list # TSV for scriptingswitch-to-brew switch docker obsidian signalswitch-to-brew switch --allIf the installed version of an app differs from what the Homebrew cask currently ships, --adopt alone will fail. By default, switch-to-brew automatically retries with --force and shows a warning:
⚠ Version mismatch: GIMP installed=3.0.6, cask=3.2.0
▸ Retrying with --force (brew upgrade will update it later)...
✔ GIMP force-adopted (3.0.6 → brew upgrade will bring to 3.2.0)
To immediately upgrade to the latest version after adopting:
switch-to-brew switch gimp --upgrade # Adopt + upgrade one app
switch-to-brew switch --all --upgrade # Adopt + upgrade everything
switch-to-brew --upgrade # Interactive mode with upgradeTo disable force-retry and fail on any version mismatch:
switch-to-brew switch gimp --strictPreview what would happen without making any changes:
switch-to-brew --dry-run # Interactive dry run
switch-to-brew --dry-run switch --all # Batch dry runBy default, App Store apps are excluded (they must be removed from the App Store before Homebrew can manage them). Include them in discovery with:
switch-to-brew --app-store discover- Scan — walks
/Applicationsand~/Applicationsfor.appbundles - Filter — removes Apple system apps (by
com.apple.*bundle ID), apps already managed by Homebrew, helper/installer bundles, and Mac App Store apps - Match — resolves each app to a Homebrew package token:
- First checks a built-in mapping of 90+ common apps (
data/known_casks.tsv) - Falls back to
brew search --caskwith normalised name variants for cask-backed apps - Formula-backed mappings are supported for apps that ship a Homebrew formula but no cask (for example, oMLX)
- Results are cached for 5 minutes to speed up repeated runs
- First checks a built-in mapping of 90+ common apps (
- Adopt — runs
brew install --cask <token> --adoptwhich tells Homebrew to claim the existing.appbundle rather than downloading a fresh copy
When you run brew install --cask foo --adopt, Homebrew:
- Downloads the cask metadata (version tracking, uninstall instructions, etc.)
- Sees that the
.appalready exists in/Applications - Links the existing app into its tracking system instead of replacing it
- From this point on,
brew upgradewill manage updates for this app
Your app, its settings, and its data are untouched.
switch-to-brew/
├── switch-to-brew # Main executable
├── lib/
│ ├── constants.sh # Colors, version, exit codes
│ ├── utils.sh # Logging, cache, confirm, helpers
│ ├── discovery.sh # App scanning and filtering
│ ├── cask_match.sh # Homebrew package resolution
│ ├── ui.sh # Table rendering, interactive picker
│ └── brew_ops.sh # Homebrew install / adopt operations
├── data/
│ └── known_casks.tsv # Curated app → Homebrew mapping
├── Makefile # install / uninstall / lint
├── LICENSE # MIT
└── README.md
Contributions are welcome! Common ways to help:
- Add entries to
data/known_casks.tsv— if you notice an app that isn't matched, add its bundle ID → Homebrew mapping - Report mismatches — if an app is matched to the wrong Homebrew token, open an issue
- Test on different macOS versions — the more environments tested, the better
Run validation commands from the repository root:
make test
make lint # requires shellcheck
./switch-to-brew discover --no-color
./switch-to-brew list --no-color
./switch-to-brew -n switch <token>The CLI checks depend on the local macOS and Homebrew state. Prefer --dry-run and --no-color when you want stable, reviewable output.
make lint # requires shellcheck- App Store apps require manual removal from the App Store before Homebrew can manage them. By default they're excluded from discovery.
- Setapp apps are discovered and flagged but may not work correctly with
--adoptsince Setapp manages its own app lifecycle. - Version mismatches — if your installed version differs from the Homebrew cask version,
--adoptalone will fail. By default, switch-to-brew detects this and retries with--forceso Homebrew takes ownership of your existing binary. Runbrew upgrade --casklater (or pass--upgrade) to update to the latest version. Use--strictto fail on mismatch instead. - Formula-backed migrations — some apps are only available as Homebrew formulas, not casks. In those cases switch-to-brew installs the formula and tells you that the original
.appbundle remains in place; there is no--adoptpath for those apps. - Password prompts — some casks include
.pkginstallers or privileged helpers (e.g. Docker, Parallels, DisplayLink, Microsoft Office) that requiresudo. Homebrew will prompt for your macOS password during these adoptions. This is normal Homebrew behaviour and not something switch-to-brew controls. - Requires Homebrew to be installed. Requires macOS (this tool uses macOS-specific APIs like
defaults readandmdls).
MIT © Matthew Corven