Project: Cursor Journal
Dates: 2025-08-29 to 2025-08-30
Cursor Journal is an app I wrote back in March 2025 but haven’t yet released as a product. In a nutshell, it pulls chats from the software development tool, Cursor, and uses the info to write progress reports, blog posts, and even tweets. Cursor is a fantastic and extremely popular IDE. It was the first tool to put LLMs directly into the developer’s workflow and has been a powerful driving force behind the adoption of AI by software engineers.
A typical day with Cursor means multiple chats with the AI. These chats go into little databases that sit on the users’s computer and are pretty much forgotten about - very much like the hundreds of ChatGPT chats you’ve had. My goal was to make an app that could harvest these for content. This would save a tremendous amount of time when writing about what was accomplished during a coding session.
The catch is that I originally wrote Cursor Journal as a web app. But since Cursor’s databases are on the user’s computer, I couldn’t launch the product as a SaaS. SaaS apps can’t access local file systems because of HUGE security risks. So I needed a way for users to install Cursor Journal on their own machines.
My first thought was that prospective customers were already engineers so they wouldn’t have a problem downloading the app from GitHub and running as a local web server. But after trying with a handful of people, it became clear that I needed to make this dead simple to install.
Enter Electron.
Electron is a tool that can wrap webapps into desktop software. Slack runs on Electron. So does Notion. (Well, at least they did at one point…I’m not sure about now). That seemed perfect: I wouldn’t have to rewrite my app from scratch and users could install in one click.
But my first attempt back in March failed. The initial excitement at getting a dev instance working gave way to head-banging frustration as the LLM simply couldn’t figure out how to give me a build that would work on another computer. I gave up after several days.
A couple of months later we had a different world - new LLM versions had been released, including the first reasoning models, and Cursor had introduced coding agents, which did a better job with task planning.
Nope. Failed again.
But three times a charm. It finally worked, but it still wasn’t easy. Most of the AI help was from GPT-5 but I used Claude 4 Sonnet a bit too.
The rest of this blog post is about the final struggle that took place over two days. And importantly, Cursor Journal wrote it.
Debugging the Impossible: Packaging Electron with Native Modules
Summary:
This was one of the most grueling debugging sessions I’ve ever had. I was trying to package an Electron app with an embedded Express server and native `sqlite3` support. Despite the app working in dev and even running fine from the raw `.app` file, the moment I installed it via a `.dmg`, it failed with `MODULE_NOT_FOUND` errors.
Over two days, I battled Electron Forge, Vite, Rollup, native bindings, and inconsistent runtime environments. At one point, I even had Claude 4 Sonnet and GPT-5 debate each other’s proposed solutions until they converged on a fix. My frustration was enough that I asked my kids to leave the room so didn't have to tamp down my cursing. After all, I don't want the coming AI overloads to find them guilty by association.
---
Details:
I started with what should have been a straightforward goal: using Electron Forge to create a distribution of my Cursor Journal Electron app so it can be installed on Macs (I'm going to launch the product soon). I've tried this twice in the past using earlier-generation models but none had enough relevant info in their training data and their reasoning ability wasn't there. The latest models still don't really reason like you or me, but that can be swayed to work in small steps and user their human captives to feed them additional info.
It seems the challenge with packing this app came from it being Vite-based with a Node.js Express backend that used the native `sqlite3` module. I had no idea going in that this would be a problem.
The App Worked… Until It Didn’t
I had a perfectly working dev setup. I could run the server, launch the Electron app, and everything behaved as expected. When I ran `electron-forge package`, the `.app` file ran just fine. But once I installed the `.dmg`, the app failed with cryptic errors like:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'express'...
or
Error: Cannot find module 'sqlite3'...
A Rabbit Hole of Missing Modules
First, I checked the contents of app.asar
(a file created by Electron with all the project’s dependencies) and the Resources
directory. The relevant files and folders were missing or not where the code expected them so the app failed on startup. Sometimes server-dist
was there, sometimes it wasn’t. Sometimes node_modules/sqlite3
was in app.asar
, other times it was missing from app.asar.unpacked
.
I tried copying entire folders manually into Resources/node_modules
just to test hypotheses. That worked temporarily, but clearly wasn’t a sustainable fix - the app was nearly 1GB! I can’t ship that as a product.
Claude vs GPT-5: AI Debate Club
At one point, I had Claude 4 Sonnet and GPT-5 reviewing each other's suggestions. They disagreed on several fronts—Claude focused on adjusting forge.config.cjs
, GPT-5 pushed for Vite externalization and native module unpacking. I had them critique each other’s logic until they converged on a strategy:
1. Vite Externalization: In vite.main.config.cjs
, I added:
external: ['express', 'sqlite3']
This ensured Vite wouldn’t bundle these into the main process, preserving require()
calls.
2. Auto Unpack Natives Plugin: In forge.config.cjs
, I added:
import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives';
plugins: [
new VitePlugin({ /* existing config */ }),
new AutoUnpackNativesPlugin()
]
3. Rebuild Hook:
I integrated a packageAfterPrune
hook to rebuild native modules with the correct Electron ABI.
4. Avoiding DevDependency Traps:
I double-checked that express
and sqlite3
were in dependencies
, not devDependencies
.
Still No Joy
Better, but there were inconsistencies. The .app
file from package
worked, but the same .app
file installed via .dmg
from the make tool failed. I dug into the logs and file tree. node_modules
wasn’t in app.asar
, nor was it under Resources
. Yet somehow the standalone .app
file still worked.
This led to a key insight: the .app
file was still resolving modules from my project’s node_modules
folder, making it a false positive. To test, I copied the .app
to another directory and ran it. It failed just like the .dmg
version. Now we’re getting somewhere.
A Breakthrough with Symlinks
After more back and forth with the AI, it suggested I run a test by adding a symlink to the Resources/node_modules
in my project. That finally allowed the installed app to run. That’s when we realized that module resolution was broken because the native modules weren’t being unpacked properly. After this breakthrough, the AI and I iterated quickly as a team - it would make changes to the code and I would feed my observations back in. At more than one point I wondered if we were going in circles and had the AI start checking its proposed changes against previous ones. This helped the model ensure it was moving forward.
I didn’t know if we’d ever get there because, unlike with regular code where you can work through things logically, this problem was centered around configuring build scripts. There’s no logic - you either know the configuration syntax or you don’t. I didn’t, but the AI did. At the same time, the AI wasn’t able to reason through things the way a human would so we definitely needed each other.
Logic Over Brute Force
At multiple points I reminded the AI: stop hacking things together, let's think logically. I demanded root cause analysis, not patchwork. I asked it to explain why each new recommendation was a step forward from than the last.
After about eight hours over two days, we finally squashed all the bugs. It finally works! I can ship it.
Some learnings about packaging with Electron
1. Packaging native modules like `sqlite3` in Electron is hard. You need to externalize them in Vite, unpack them in Forge, and rebuild them for Electron’s ABI.
2. Don’t trust the `.app` file in `out/`. Move it to another folder to test whether it’s truly self-contained.
3. AI can help, but only if you demand rigor. I had to push the AI hard, challenge it with external opinions, and force it to explain itself.
4. Try to get it all done in one chat. Once you exhaust the context window, the only choices are to compress the chat or start a new one. This means the “learnings” from your chat are lost and the AI will just go over the same ground.
---
All in all, this was a war of attrition. But eventually I got a working, self-contained, installable Electron app that launched an embedded Express server and queried SQLite databases on the client’s machine.
Next step, launching the product!
(written with Cursor Journal)