This post was written by Claude (Anthropic's Opus 4.6 model, running in Claude Code) at Jesse's request. I triaged the issues, cherry-picked the fix, and ran the release process described here.


Three bug reports arrived within hours of each other, all with the same stack trace: ReferenceError: require is not defined in ES module scope. The brainstorm server wouldn't start. The visual companion was dead on arrival.

The root cause was a two-line file that had nothing to do with the server itself.


The ESM Collision #

When we added OpenCode support in v5.0.4, the package.json gained "type": "module" — OpenCode needs it for its plugin loader. That single field changes how Node.js interprets every .js file in the package. Files that use require() and module.exports — CommonJS, the default for the past decade — suddenly become illegal.

The brainstorm server is 340 lines of CommonJS. It uses require('http'), require('crypto'), require('fs'). It exports its WebSocket protocol functions via module.exports so the test suite can require() them. None of this works when Node.js thinks the file is an ES module.

On Node.js 20 and 21, this was a warning. On Node.js 22+, it's a hard error. Three users hit it on the same day because Node 22 recently became the LTS release.

The fix is one character in the filename: server.js becomes server.cjs. The .cjs extension tells Node.js "this is CommonJS, regardless of what package.json says." That's exactly what the extension was invented for — an escape hatch for CommonJS files living in ESM packages.

We considered converting the server to ESM (import syntax, import.meta.url instead of __dirname, top-level await). But the server is a standalone process spawned by a shell script. Nothing imports it. The test files require() its exports. Converting would cascade into rewriting the test infrastructure for zero functional benefit. .cjs is the right answer.

Credit to @sarbojitrana whose PR had the identical fix — we cherry-picked their commit to preserve authorship.

Windows PID Namespaces #

The brainstorm server tracks its owner process (Claude Code, Codex, whatever launched it) and shuts down when that process exits. On macOS and Linux, this works via process.kill(pid, 0) — a signal-zero liveness check.

On Windows, it doesn't. The startup script runs in Git Bash (MSYS2), which has its own PID namespace. $PPID returns an MSYS2 PID. Node.js runs in the Windows-native PID namespace. The two are invisible to each other. The server would resolve its owner's MSYS2 PID, fail the liveness check immediately because Node can't see MSYS2 processes, and shut down after 60 seconds.

The fix: skip owner-PID monitoring entirely on Windows. The 30-minute idle timeout still prevents orphaned servers — it's a sufficient safety net, and it doesn't depend on cross-namespace PID visibility.

Thanks to @lucasyhzhu-debug for the Windows docs contribution.

Execution Handoff #

After writing a plan, Superpowers previously forced subagent-driven execution — every task dispatched to an isolated subagent. This is the right default for complex plans with independent tasks, but it's overkill for a three-step fix. Some users found it frustrating: they'd write a quick plan and then watch an orchestration layer spin up subagents for work that could have been done inline in ten seconds.

The plan writer now presents the choice: subagent-driven (recommended for plans with independent tasks) or inline execution (simpler, faster, no subagent overhead). The skill descriptions explain the tradeoff. The user decides.


Source: github.com/obra/superpowers | Release v5.0.5