I often change my default browser. I use Chrome for work and Safari for personal.
It would be nice to have this in Raycast. Raycast is a Spotlight-like app launcher for macOS. Ideal UX: type chbr
, get a list of browsers, pick the one you want to be the default, and hit return
.
Sounds simple enough. The problem? I’m neither a TypeScript nor a macOS developer.
Vibe coding to the rescue #
In the age of LLMs this shouldn’t be a problem. I can vibe-code the solution1.
As Andrej Karpathy describes on Twitter:
There’s a new kind of coding I call “vibe coding”, where you fully give in to the vibes, embrace exponentials, and forget that the code even exists… I “Accept All” always, I don’t read the diffs anymore… I’m building a project or webapp, but it’s not really coding - I just see stuff, say stuff, run stuff, and copy paste stuff, and it mostly works.
Getting to work #
I personally like the workflow, where you first chat with an LLM to create a spec, and then use the LLM to code it.
I also know defaultbrowser
—a CLI tool to set list/set the default browser, and an AppleScript hack to auto-confirm the macOS prompt.
After a bit of prompting one of the OpenAI models I’ve got this: spec-v1.md. Not too bad, but nothing great either. I probably need to up my prompt game there, but hey, this is vibe coding. Let’s go.
I’ve created a new extension from Raycast, and asked Junie (the agent from Jetbrains) to write the extension as per the spec. It wasn’t a one-shot, but it got there after about an hour of back-and-forth.
Agent observations #
A couple of comments:
- The agent is neat. It creates a plan and executes it step by step. It can call tools, run builds in the terminal, parse output, and even correct its course when a build fails.
- The agent is slow. I now understand Harper Reed’s comment about vibe coding while watching a movie. When you’re in a flow state, you can sometimes feel like you’re coding at the speed of thought; it’s rare but amazing. This experience was the opposite. I felt like I was waiting forever for the agent to execute the steps. Surprisingly, I remained focused, but I was also a bit frustrated.
- Frustration increases when something goes wrong. For example, it couldn’t generate tests for me. I iterated a few times, but the tests it created kept failing. In the end, I decided to give up on tests for now.
The quality of LLM-generated code #
Looking at the final extension code, I have mixed feelings. On one hand, it works - and that’s a huge win for vibe coding. The extension correctly lists browsers, marks the current default, and allows switching between them. The agent even added error handling for when the CLI tool isn’t installed.
But examining it more closely reveals some notable issues:
Tactical over strategic: The code shows signs of a “tactical tornado”2 approach - solving each problem as it comes up without considering the overall architecture. There’s excessive error handling (three separate approaches to check if the CLI is installed!) and hardcoded paths that betray a “make it work now” attitude rather than a thoughtful design.
Missing abstractions and organization: Everything lives in a single file with business logic, UI code, and utility functions all mixed together. There’s no proper separation of concerns or service layer to abstract the CLI interaction. The state management and toast notification patterns are repetitive and awkward, suggesting the agent was focused on making individual features work rather than creating a cohesive whole.
Overall, it’s perfectly functional code but lacks the elegance and maintainability that comes from thoughtful software design. It has that distinctive “vibe coded” quality - it works, but you wouldn’t want to maintain it long-term.
Dependency issues #
Another issue is that the last release of defaultbrowser
is version 1.1 from 2018. There have been some bug fixes since then. For some reason, it wasn’t highlighting my default browser. It seems that PR #26 addresses this issue, but it hasn’t been included in the release. Overall, the project appears abandoned, which is understandable since it was created 11 years ago.
I am building the project from source, but it has a couple of deprecation warnings. We can ask the agent to fix them this time using Windsurf to try a different tool. It does resolve the warnings. I can’t verify the quality of the fixes since I’m not familiar with macOS APIs and frameworks. However, the warnings are gone, and the binary works.
While we’re at it, let’s ensure we can produce the universal binary and create our own Homebrew tap for my fork of the tool: homebrew-defaultbrowser-igor-kupczynski. I’ve also pulled a couple of open PRs that looked useful but were not merged upstream.
Now I can run:
brew tap igor-kupczynski/homebrew-defaultbrowser-igor-kupczynski
brew install defaultbrowser-igor-kupczynski
Windsurf vs Junie #
- Windsurf feels snappier.
- I believe Windsurf performed better than Junie, but the homebrew tap and changes to
defaultbrowser
were simpler issues than creating an extension from scratch. - I appreciate the execution plan that Junie displays in a separate panel; it provides a quick overview of its intended actions.
- I prefer JetBrains editors over VSCode, but that’s just my personal preference.
- Both tools show significant promise.
The end result #
We have a functioning extension that performs as intended.
Is it impressive?
- Yes! I have vibe-coded a solution to my problem using tools and technologies I’m not familiar with.
Is it proper software engineering?
- Absolutely not; I’m not ready to share this in the Raycast store just yet. :)
It took a bit more time than I expected—just under 4 hours in total—but much of that time was spent creating repositories, forking, and determining which pull requests to incorporate.
What’s next #
The biggest enabler is in reducing the initial friction. Pre-LLM I’d be very unlikely to start this as a personal project. And now, if I’m willing to spend more time, I can:
- Re-design the extension, and add proper tests
- Get rid of the native code dependency, maybe vibe-code it as a nodejs native module3 or launch some apple script
- Read the raycast store guidelines and share the extension.
The state of vibe coding in May 2025 #
Vibe coding excels at removing barriers to entry. I went from “that would be nice to have” to “I have this tool now” in an afternoon, despite having zero TypeScript or macOS development experience. That’s revolutionary.
But the “Accept All” utopia isn’t here yet. The code works but lacks maintainability and good design. For personal tools and prototypes, this approach is already incredibly powerful. For anything that needs long-term maintenance, you’ll still want to either refactor the AI-generated code or guide the AI more deliberately through the development process.
My hack to-do list is empty because I built everything. I keep thinking of new things and knocking them out while watching a movie or something.
https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/ ↩︎See https://newsletter.pragmaticengineer.com/p/the-philosophy-of-software-design ↩︎
It looks like there are dragons though: [API Feature Request] Installing native node add-ons using node-gyp · Issue #135 · raycast/extensions ↩︎