Cortex: AI Coding Tool
April 17, 2026
I built a Windows desktop app (Electron + React) that runs Claude, Gemini CLI, and OpenAI Codex side-by-side in one window. No API keys needed, uses the official CLIs directly. Live token streaming, real-time git diff, persistent chat history per project.
What is it?
I built Cortex because I wanted one desktop app where I could run Claude, Gemini CLI, and OpenAI Codex side by side while working in a real project folder. I did not want to keep bouncing between terminals and separate apps. I wanted one place where I could chat with the tools, watch their output stream in, and see file changes as they happened.
So Cortex became a Windows desktop app that sits on top of the official CLIs already installed on my machine. No extra API key layer. No fake wrappers pretending to be the real tools. Just the actual CLIs orchestrated through one interface.
How it works
The Electron main process spawns each CLI as a child process and reads stdout as it arrives. I pipe the process output through a line reader, normalize it, and send it over IPC to the React renderer so the UI can update live.
At the same time, I watch the current project directory for file changes. Whenever the AI edits code, the app triggers a git diff and sends the parsed result to the renderer. That means the UI is not just a chat log. It is also a live view into what the model is doing to the actual codebase.
Under the hood: Electron IPC
Electron gives me two very different environments: the main process with system access, and the renderer process with the app UI. They cannot just share state directly, so IPC is the bridge. Request-response actions go through the normal `handle` and `invoke` style APIs, while streaming output uses event-based channels.
That split is what makes the app architecture work. The main process owns process management, filesystem access, and git operations. The renderer owns the chat UI, diff panes, and interaction flow. IPC is the contract between them, and once I structured that boundary cleanly, the app became much easier to reason about.
Spawning multiple AI CLIs
One annoying problem was that each CLI speaks a different output format. One streams plain text. Another emits structured events. Another has its own conventions for progress and partial output. If I let the renderer deal with all of that directly, the UI code would turn ugly fast.
So I normalize everything into one internal stream shape before it reaches React. The renderer only cares about a clean sequence of tokens, events, and status updates. That abstraction layer is what makes the app feel unified even though the tools under it are all different.
Real-time git diff
The live diff view is one of my favorite parts because it makes the AI less of a black box. I watch the filesystem with `chokidar`, run `git diff` when files change, parse the unified diff output, and send structured hunks to the UI.
That means I can literally watch added lines, removed lines, and changed blocks appear while the model is still working. It turns the whole experience from blind trust into supervised collaboration, which is exactly how I want an AI coding tool to feel.
Key takeaways
- Electron IPC: main vs renderer process boundary, handle/invoke for request-response, on/emit for streams
- child_process.spawn with piped stdio: readline streaming from CLI stdout
- Unified diff format: parsing hunk headers, additions, deletions, context lines
- Normalizing multiple CLI streaming formats into a single internal event stream
- Electron packaging with electron-builder: NSIS installer for Windows, auto-update config