Four Studios, One Platform: Inside the VS Codium Extension Suite
In my last post, I went deep on Prompt Workbench's CQRS architecture and the multi-provider AI router underneath it. This post covers the next thread from the overview article: the VS Codium extension suite — four studios and the shared platform layer that keeps them from each becoming their own tangled monolith.
The White-Label Pivot
Every studio in this suite started life as a standalone idea — a SaaS app with its own hosting, its own auth, its own deploy pipeline. Somewhere around early June this year, I stepped back and asked myself a question I'd recommend any solo builder ask before committing to a fourth or fifth standalone product: who is actually going to install and pay for four separate hosted apps from a one-person shop, each with its own login?
Nobody, realistically. So I pivoted the whole suite to a white-label VS Codium extension model
instead — same product ideas, same underlying code, delivered as installable extensions inside an
editor developers already have open all day, rather than as yet another tab in a browser. That
pivot is why the directory structure you'll see if you go looking is extensions/studios/* and
extensions/platform/*, not apps/*. It also forces a discipline I actually like: an extension
that can't work offline on a laptop with no network connection is a broken extension, not a
degraded one. More on that below.
The Tour
Prompt Studio
Prompt Studio is the VS Codium face of Prompt Workbench, the platform I covered in the last
post. And I mean that literally, not as a marketing simplification: the extension's build scripts
run nx build prompt-workbench and nx build prompt-workbench-api, then copy the resulting Angular
bundle and NestJS bundle straight into the extension's webview/ and sidecar/ folders. There is
no second implementation to keep in sync — when I fix a bug or ship a feature in Prompt Workbench,
Prompt Studio picks it up on the next build. On activation, the extension spawns the sidecar as a
child process and talks to it over plain HTTP, the same way the SaaS app's Angular front end talks
to its NestJS back end. The webview is just that same front end, rendered inside a VS Codium panel
instead of a browser tab.
Infographic Studio
Infographic Studio generates publication-quality SVG diagrams from structured input, and I want
to be honest about where it actually stands rather than describe it as further along than it is:
the generator-worker implementation — separate workers for raw SVG, Mermaid, draw.io XML, and
Syncfusion output — is real, working code, but it's still on its own feature branch, not yet merged
to main. I'm currently on the generator-implementations phase, chasing down a draw.io WebView
rendering defect before I call that phase done. I'd rather tell you it's in progress than round up.
Compliance Studio
Compliance Studio wraps a CLI I built for architecture and standards review — seven review
agents (architecture, standards, ATDD, security, UX, observability, process) that run against a
live Nx project graph, in --mode pr or --mode full. The extension doesn't reimplement that
review logic; it discovers the local LM Host's port through VS Code's extensionDependencies
mechanism, spawns the CLI as a child process with COMPLIANCE_PROVIDER=lm-host, and streams results
back line by line as the CLI emits them — each finding shows up in a tree view the moment the
relevant agent finishes, not all at once when the whole run completes. Results get written to a
local PouchDB audit log, and the tree view subscribes to that database's live changes feed to stay
current. It also registers itself as a chat participant, so you can ask it questions about a finding
directly in the shared chat sidebar instead of only reading a report.
Chat Panel
Chat Panel is the shared AI sidebar that any studio can plug into, and the contract for doing that is intentionally small: one method.
registerParticipant(
id: string,
displayName: string,
handler: (request: ChatParticipantRequest, stream: ChatResponseStream) => Promise<void>,
): vscode.Disposable
A studio calls extensions.getExtension('singularity.chat-panel').exports.registerParticipant(...)
during its own activation, hands over a handler function, and from that point on its participant
shows up in the same sidebar as every other studio's — Compliance Studio's participant sits next to
whatever Prompt Studio or a future studio registers, all inside one panel the user never has to
switch between. I like this pattern specifically because it's boring: no shared state, no event
bus, just a registration call and a callback. The boring parts of a platform are usually the parts
that don't break at 11pm.
The Shared Platform Layer
Underneath all four studios sit three platform extensions that do the unglamorous work of making a suite feel like one product instead of four:
- Event Bus Host — a PouchDB-backed store for domain events with a live changes feed, so studios can react to things happening in other parts of the suite without polling.
- LM Host — an HTTP bridge that lets CLI subprocesses (like the compliance runner above) reach
vscode.lmand the Anthropic/Cerebras providers without needing their own API credentials wired in separately. - Runtime Routing — license-aware routing between a local sidecar process and a cloud API base URL, for products that need an online/offline licensing split.
A Correction
I said in the overview article that the offline-first mandate includes "an API router with two
paths — offline calls go to the local sidecar; online, licensed use routes to the cloud API gateway
and microservices instead," describing that as suite-wide. Writing this piece required actually
reading runtime-routing's code, and that framing overstated where things stand today: Runtime
Routing is real, working infrastructure, but right now only the Lean-Agile Studio product line
declares it as an extensionDependencies entry and routes through it. None of the four studios in
this article currently wire into it. The rest of the offline-first mandate — local inference via
Ollama and LM Studio, the bundled sidecar per extension, UI/API code reuse with the SaaS twin — is
accurate and shipping today, as Prompt Studio's build pipeline above demonstrates. The cloud-routing
half of the mandate is built, proven on one product line, and not yet extended to the rest of the
suite. I'd rather correct that in public than let it stand uncorrected.
The Engineering Note: Why EventBus.getInstance() Doesn't Work Across Extensions
Here's a trap I hit early and want to save you from hitting yourself, if you're building anything
similar. VS Code loads each extension into its own Node.js module cache. That means a singleton
pattern — EventBus.getInstance() — doesn't behave like a singleton at all once you cross an
extension boundary: calling it from Compliance Studio and calling it from Chat Panel returns two
different objects, in two different module caches, that have never heard of each other. A
subscription registered against one is invisible to events published against the other. I burned an
afternoon convinced I had a bug in my event-publishing code before I realized the actual problem was
architectural, not logical — I documented the fix as ADR-045 once I understood it.
The fix isn't a smarter singleton — you can't out-clever a module boundary VS Code itself enforces.
The fix is to stop treating in-process pub/sub as the transport and use something that already
crosses process and module boundaries by design: PouchDB's live changes feed. Event Bus Host writes
every published event into a shared PouchDB database, and any extension — regardless of which
module cache it's loaded into — can open a changes({ live: true }) subscription against that same
database file and see everything published by every other extension. It's a slightly odd-looking
solution the first time you write it, because you're using a database as your event transport
instead of an in-memory bus. But it's the one that actually works given how VS Code isolates
extensions from each other, and once you know the constraint, the workaround stops looking odd and
starts looking obvious.
Who Else Is Building This Way?
I'm curious how common this pattern actually is outside my own repo: building a product suite as a set of cooperating VS Code / VS Codium extensions instead of shipping each idea as its own hosted web app. It's not the default choice — most tooling advice still assumes "SaaS app" is the shape a new product idea takes by default — but for a solo builder, an editor extension gets you distribution, an existing UI shell, and a captive, technical audience for free. If you've built something similar, or if you've hit the module-cache trap above from the other direction, I'd genuinely like to compare notes.
Next in this series: LeanAgileScript, the Domain-Specific Language that started the developer tooling thread — and why acceptance criteria that are readable by a product owner and directly executable as a test turned out to need a real grammar and language server, not a lighter-weight parsing trick.