Deterministic Editing — repeatable operations, a plugin host that can't hang the editor, and a crash-recovery format that survives its own filenames.
Macros: transactional record and replay
Macros record the command stream off the message bus, not raw keystrokes, so replay is immune to mode and timing skew.
q+ a register starts recording,qstops;[N] @+ a register replays with a count.- Replay is all-or-nothing. The whole macro applies as one undo group, and a replay that errors mid-way rolls the buffer back to its pre-replay state instead of leaving a half-applied edit.
- A recording indicator sits in the status bar; a failed replay surfaces the rolled-back step as a toast.
Macros live in per-session a–z registers. Durable, cross-restart storage arrives with the named-register work in #3.
The plugin host is now a library, and every call is metered
The pure-Zig wasm interpreter has been extracted into its own project, wick — stem's second general-purpose spin-off after vigil. stem now depends on it as a pinned package.
- Fuel-metered calls. Every plugin call runs under an instruction budget. A runaway plugin fails that one call with
OutOfFuel— logged, counted, contained — instead of hanging the editor. Host calls cost one instruction, so a plugin waiting on a slowgitsubprocess is never penalized. - Containment you can see. Per-plugin call, trap, last-error, and fuel-usage stats appear in the plugin-manager dashboard and the control center, and plugin traps feed the check-engine light alongside dead letters and open circuits.
Session files that survive their own filenames
The session and crash-recovery parser scanned for field markers instead of parsing JSON, so a path containing ", \, or } truncated or dropped the buffer it belonged to. On a real session written for a workspace holding quote"file.zig and brace}file.zig, the old parser recovered one usable buffer out of three.
Parsing and serialization now both go through std.json:
- Paths round-trip exactly — quotes, braces, newlines, control bytes, emoji. Control characters were previously written raw, producing a file that wasn't valid JSON at all.
- Out-of-range numbers saturate instead of wrapping; a long enough digit run used to panic a safe build.
- A path that isn't valid UTF-8 costs that one buffer rather than making the whole file unparseable, and a malformed split layout is dropped the same way — losing your layout beats losing your buffers.
Install
Prebuilt archives for macOS (arm64/x86_64), Linux (x86_64/arm64), and Windows (x86_64) are attached below. The Unix tarballs now ship their own installer, which places the binaries and bundled plugins exactly where a from-source install does and tells you what to add to your PATH if it isn't already there:
tar xzf stem-v0.14.0-<target>.tar.gz
cd stem-v0.14.0-<target>
./install.sh # or: ./install.sh --prefix ~/.localThe binaries aren't notarized yet, so a browser download is quarantined by Gatekeeper. install.sh clears that for you; if you run the binary straight from the archive instead, fetch it with curl -LO or clear the attribute yourself with xattr -d com.apple.quarantine ./stem.
Verify a download against the attached SHA256SUMS.txt:
shasum -a 256 -c SHA256SUMS.txtAlso in this release
- Test coverage went from 656 to 723 passing tests — not by writing 67 new ones, but by discovering that nine modules' test suites were never wired into the build and had quietly rotted,
split_manager(window splits) among them. Fixing them turned up tests asserting the opposite of real behavior, and a leak-checking helper that could only pass for code that allocated nothing. - Dead code removed: an orphaned
help_view.zig, a leftover loader helper, an unused import. - Docs refreshed across the README, architecture, plugin, and roadmap pages.
Full Changelog: v0.13.1...v0.14.0