A 100% vibecoded, native, low-footprint C++/SDL3 desktop IDE focused on built-in editor, diff, merge, git, search, and terminal workflows. Single-window, keyboard-first, and does not require a GPU (it uses one when available to speed up text, and runs fully on a software renderer otherwise).
Editor, file tree, syntax highlighting, git blame, debugger pane, and terminal — captured straight from the running app. See the full screenshot gallery and hero demo video on the project site.
For the authoritative in-scope / non-goal list see openspec/specs/product-vision/spec.md.
Status: stable. Tagged
v2.11.1(see CHANGELOG) and actively developed. Release binaries are GPG-signed — verify them per Verifying releases. No third-party comparative benchmarks yet. Read Known Limitations and Security & Trust Model before using on a real project.Verified, not just written. 121 test files and 10 fuzz targets back the build, gated by ASAN / UBSAN / TSAN sanitizer runs, committed startup/typing/scroll/diff/search performance baselines, and architecture-invariant lints enforced on every test run.
- What microide is
- Release status
- Current UI preview
- What works today
- Agent control channel
- Build or package locally
- Known limitations
- Plugin trust warning
- Performance methodology summary
- Deeper docs
- Contributing
microide is 100% vibecoded: every source file, test, and document in this repository was written by AI coding agents (a mix of Claude, GPT, and other tools) under human direction. There is no hand-written code path. The repo is published as a real-world experiment in agent-driven development — interesting to read, useful to build on. That describes how the code was authored, not how mature it is: the build is stable and covered by an extensive automated suite (unit, fuzz, sanitizer, and performance gates). Expect the architecture notes to reflect what the agents settled on rather than a hand-curated design, and see Known Limitations for the honest edges.
This project does not claim to be the fastest or smallest editor. It claims to be a native, responsive, single-window IDE with internal regression baselines for startup, typing, scroll, diff, and search. See Performance & Benchmark Methodology for what is actually measured, and what is not.
- Multi-project tabs, file tabs, an n-way split editor grid (drag a tab onto a pane edge to split it, up to eight panes), deferred-commit tab drag with ghost
- Drag and drop from the desktop: a file opens as a tab, a folder opens as the project
- UTF-8 codepoint boundaries, IME preedit, line-ending detection and preservation
- Multi-caret editing with position remap, region-stack highlighting, and copy-with-context
- Column/box selection by mouse (Shift+Alt+drag) and keyboard (Ctrl+Shift+Alt+Arrow): a rectangle of visual columns that stays straight across tabs and non-ASCII text, with a virtual column that survives short lines
- Soft word wrap with hanging indent; long-method fold resolution
- Syntax highlighting with per-file checkpointed state (fast random jumps in large files)
- Undo/redo storing line-range patches rather than full-buffer snapshots; word-level undo coalescing
- Durable writes with a save-time conflict guard and non-blocking external-change banner
- Git blame shadow text — asynchronous, viewport-scoped, caret-local annotations with hover commit details
- Project-local colorscheme, tab size, indent width, and soft-tabs preferences
.editorconfigsupport:indent_style,indent_size,tab_width,end_of_line,trim_trailing_whitespace, andinsert_final_newlineare honored per file, with nearest-file androot = trueprecedence. It overrides both the configured indent and auto-detection, as in VSCode. Toggle witheditor.editorconfig.enabled- Session restore across restarts
- Compare tabs: working-tree vs
HEAD, arbitrary commits, outgoing base-branch files - Three-way merge tabs: incoming / result / current panes with per-hunk picks and whole-side apply
- Shared decorated text-grid pipeline across editor, compare, and merge surfaces
- Change-overview lane alongside the scrollbar in both compare and merge tabs
[/]hunk navigation;oopens the working-tree file at the matched line- State preservation across rename, delete, and reopen — reopening the same target reuses the tab
- Standalone
microide_diff_benchfor repeatable before/after timing on compare hot paths
- Sidebar git view: working-tree changes, staged files, merge conflicts, outgoing branch files
- Per-file stage (
s), discard (x), bulk stage-all, confirmed discard-all - Sidebar branch row: current branch opens a filterable switch picker; Sync shows ahead/behind counts
- Conflict files open directly in the three-way merge tab
- Branch switch/create, fetch, pull, push, publish, sync, and stash push/pop — each run off the shell thread with git's failure classified (auth, no upstream, non-fast-forward, dirty tree, conflict)
- Editable commit message; branch/commit ref picker for compare and review
- Commit picker overlay for compare target selection
- Parallelized project-search sidebar: literal (default) or regex (Alt+R), case control (Alt+C), hidden-file toggle (Alt+H) — the chords work from the results list and from inside the query field
- Scope filters ("..." toggle): comma-separated files-to-include / files-to-exclude globs with
**, character classes, and{a,b}alternation; out-of-scope files are rejected on their path and never read - Count-all totals with match highlighting; replace-in-project in literal and regex modes (regex expands capture groups)
- In-file find & replace with match case (Alt+C), whole word (Alt+W) and regex (Alt+R) — the same first two toggles the terminal find bar carries, and all three apply in literal and regex mode
- File finder overlay with a cached index and quality-ranked fuzzy matching: contiguous runs, word starts and camelCase humps rank above scattered matches, filename matches above path-only ones, and the shorter, shallower path wins an otherwise-equal match
- Standalone
microide_search_benchfor repeatable timing
- PTY-backed terminal tabs with scrollback and selection
- Alternate-screen support, application cursor-key mode, origin mode, autowrap, bracketed paste
- OSC 52 clipboard copy (opt-in, off by default), focus notifications, basic device/cursor query replies
- Terminal text selection, copy, and paste shortcuts
- Tab drag reordering; right-click for "Copy Last Command and Output"
- Built-in DAP debugger (validated against gdb 17.2): per-language launch configs, line / function / conditional breakpoints and logpoints, gutter breakpoint menu
- Continue, Step Over / In / Out, Pause, Restart; capability-gated reverse execution
(
reverseContinue/stepBack) - Dedicated right-side debug pane: Call Stack, Variables / Scopes (lazy tree with inline edit), Watch expressions, and Breakpoints — plus hover-to-inspect and exception breakpoints
- Multi-session debugging with a session selector; Debug Console REPL, with program stdout/stderr surfaced as an Output channel
- Agent-drivable headless debugging through the control channel (set breakpoints, start/step, query variables/threads from stdin/stdout)
- Gated behind
debug.enabled(default off); enable it before thedebug-*commands act
- Manual Lua 5.4 plugins from
~/.config/microide/plugins/, run on a dedicated worker thread off the UI thread (host-renders-data: plugins emit data, the host owns drawing) - Lifecycle hooks, commands, tree sidebars, diagnostics, hover providers, syntax contributions
- Editor rendering: decorations (text styles, gutter marks, end-of-line text, code lenses) and content surfaces (charts/previews via display lists or raster images, in a panel or anchored inline)
- Editor integration: ghost-text inline suggestions, host-owned buffer edits, and reactive editor
events (
on_buffer_change/on_cursor_move/on_selection_change/on_buffer_close) - Language providers: definition, references, signature help, document symbols
- Presentation contributions: color themes, file-icon themes, rich status items (tone + progress)
- Host-owned registries: settings, keybindings, status items, menus, formatters, save participants, completion providers, code actions, tests, SCM, annotations
plugins-reloadcommand; file-watch–triggered asset reload on Linux- Repo-owned dogfood examples:
plugins/eslint(diagnostics),plugins/eol-annotations(decorations),plugins/surface-preview(content surfaces),plugins/presentation-demo(themes/icons/status),plugins/language-tools(language providers),plugins/todo-highlight(decorations)
microide can be driven from outside the window. An external process — typically an LLM agent — gets the same control surface a person has, because the channel routes through the same command chokepoint as the command palette rather than adding a parallel path. It is designed in, not retrofitted through an extension API.
# Drive an instance and stream every response and event as JSONL on stdout.
microide --control --control-spec debug.spec.json
# Or talk to an already-running instance, one request at a time.
microide control-send "breakpoint-set src/main.cpp 42"
microide control-send debug-launch --wait stopped
microide control-send review-branch origin/mainThe window stays open and fully interactive the whole time — an agent and a human can work the same instance.
What it can do
- Debug. Set/remove/enable/disable breakpoints, conditions, hit counts, logpoints, and function breakpoints by symbol name; launch a named config or an ad-hoc program; step, continue, pause; query threads, frames, scopes, and variables.
- Review.
review-conflictsopens one three-way merge tab per conflicted file;review-branch [ref]opens a compare tab per file differing from a ref;review-commit [commit]opens the diff of any commit. Each dedupes against open tabs and closes stale clean review tabs from the previous run. These are non-mutating — they open the tabs, they never rungit merge. - Observe. Query verbs (
debug-state,breakpoints,tabs,projects,status,launch-configs,adapters) and pushed events (stopped,terminated,output). - Start ready.
--control-specopens a project with breakpoints already set, files revealed, and a session started before the window is interactive. - Configure without side effects.
--set <id> <value>applies a setting live for the session but never writes it to the user's config, so a headless run cannot clobber someone's settings.
Two details that matter when writing an agent against it
stopped fires twice per stop, disambiguated by framesPending. The first lands the instant
the adapter halts, so an agent knows it stopped within milliseconds even while a slow adapter is
still indexing DWARF; the second carries the resolved file/line/frames. And terminated
fires for every end — including an adapter that crashes without sending a DAP event — so an
observer is never stranded waiting on a message that will not come.
Full protocol, spec format, security model, and the headless runbook:
dev-docs/control/control-channel.md.
In-scope and non-goals are declared in openspec/specs/product-vision/spec.md.
Short version: built-in editor, diff, merge, search, git, terminal, and debugger/DAP workflows stay host-owned. Out of scope: plugin marketplaces, cloud/collaboration/sync, and out-of-process plugin isolation.
The strongest, most validated workflow today is the native diff / merge / git workstation:
compare tabs (working-tree vs HEAD, arbitrary commits, outgoing base-branch files), three-way
merge with whole-side apply, shared decorated-text-grid rendering across editor / compare / merge,
hunk navigation, and a standalone microide_diff_bench for repeatable timing. Other surfaces work
but are less proven outside the developer's own machine.
Current validation flow is still intentionally narrow and practical:
open repo -> inspect changes -> diff files -> resolve merge conflict -> stage/commit.
- Tagged
v2.11.1. The published Debian package is GPG-signed; verify the signature and checksum before installing — see Verifying releases. You can also build from source or create a local Debian package from this repository. See CHANGELOG for what shipped. - A screenshot gallery and a hero demo video ship on the project site.
They are generated straight from the running app (
tools/capture-media.sh) and regenerated every release, so they never drift from the current UI. - Every push and pull request runs the full validation set in CI
(
.github/workflows/checks.yml): the test suite plus the architecture lint, the allocation-gated pass, ASan / UBSan / TSan, and a build-and-smoke run of the twelve fuzz targets. Each job drivestools/run-checks.sh, so a red run reproduces locally with the command named in its log. Perf baselines are not re-measured in CI — they are absolute timings from one pinned reference machine — but a changed baseline must carry aperf-baseline:justification, which CI does enforce. Release signing stays local: the maintainer key never enters CI.
Each GitHub release attaches the Debian package, a detached GPG signature (.asc), and a SHA256
checksum. Releases are signed with the maintainer key published as microide-signing-key.asc on the
release page (fingerprint 0E32 39B7 1B0F 9598 B71A FB7B 6D33 9CCB FC51 5D70).
# one-time: import the maintainer signing key
gpg --import microide-signing-key.asc
# verify the package signature and checksum
gpg --verify microide_2.11.1_amd64.deb.asc microide_2.11.1_amd64.deb
sha256sum -c microide_2.11.1_amd64.deb.sha256A "Good signature" line plus a matching checksum means the package is authentic and intact.
See the editor, side-by-side diff, three-way merge, debugger, and control channel in the gallery and
hero video on the project site. Those assets are
captured from the running app by tools/capture-media.sh and regenerated on every release (see
dev-docs/project/media-generation.md), so they stay in sync
with the shipped UI. The most honest look is still the running app: build it in two commands (see
Build) and open your own project.
Mature enough to use day-to-day on the maintainer's own work:
- editor: open / save / undo / redo, soft wrap, syntax highlighting with checkpointed state, multi-cursor (Alt+click, add-cursor-at-match, Shift+Alt+drag and Ctrl+Shift+Alt+Arrow column/box selection), folding, indent guides, bracket match, auto-close / surround driven by a language contract, snippets, save normalization
- compare and merge tabs: working-tree vs HEAD, vs arbitrary commit, outgoing-base-branch files,
three-way merge with per-hunk picks and whole-side apply,
[/]navigation - git sidebar: working-tree changes, staging / discard, conflicts open into the merge tab, outgoing-branch file view, commit-picker overlay
- project search: async, literal and regex, replace-in-project (literal & regex), file finder
- terminal: PTY tabs, scrollback, selection / copy / paste, alternate screen, common ANSI paths needed by real interactive programs
- session restore across restarts; per-project config and accent color
- Lua 5.4 plugin runtime with host-owned registries
Shipped but with caveats (see Known Limitations):
- debugger (DAP): breakpoints, stepping, call stack, variables, watch, hover, exception
breakpoints, multi-session, and a console REPL — validated end-to-end against gdb 17.2. It is
opt-in (
debug.enabled, default off), and adapter coverage beyond gdb is still expanding - LSP transport: implemented and tested against fake servers; real-world server validation is ongoing
- Tool downloader / SHA verification: implemented, not exercised against production tool catalogs
- Native file-watch backend: Linux
inotifyis wired intoFileIndex; project search and file finder consume index snapshots instead of rescanning on each refresh. First-load indexing and large watcher bursts can still show refresh lag in large repositories.
Current validated flow:
- Open a local repository (
microide /path/to/repo) and switch to the Git sidebar. - Inspect grouped sections: Conflicts, Staged, Unstaged, Untracked, Outgoing.
- Open unstaged or staged diffs from sidebar rows (or from compare tabs), then stage/unstage by file, hunk, or selected lines where text mapping is available.
- Use discard actions only after confirmation/preview prompts.
- Open conflicted files into the merge tab, resolve hunks, then stage resolved files.
- Open commit workflow from the Git sidebar, verify staged summary, write subject/body, and commit.
- On commit failure, read status/output feedback, fix the issue, and retry without losing draft text.
Known workflow boundaries:
- Binary, submodule, and some complex rename/file-directory conflicts are recognized but not fully interactive in the three-way merge UI.
- Patch staging for hunk/selected-lines can fail when the diff is stale; refresh and retry.
- Branch review markers should not be treated as a durable review database.
Honest list of what this is not, or what is unfinished. Read this before adopting microide for serious work.
- No comparative benchmarks. Internal baselines compare microide against itself; the project has not been measured against VSCode, Zed, Helix, or any other editor. Claims like "fastest" or "lower CPU than X" are not supported here and are not made.
- Piece-tree text model with a 32-bit offset ceiling. The editor handles UTF-8 at codepoint
boundaries for cursor movement and IME. Storage is a piece tree over an immutable original buffer
plus an append-only add buffer: edits are O(log n) splices with no per-line heap allocation
(see
src/editor/TextBuffer.h). Buffer offsets are 32-bit, so there is a practical ~4 GiB per-file ceiling; behavior on extremely large files and pathological single long lines is still under measurement. - Capability-sandboxed plugins, not full isolation. Plugin filesystem/process access is enforced per-plugin (default-deny process, project-scoped fs, Linux kernel confinement of spawned children), but the Lua state still runs in-process. See Security & Trust Model.
- Recovery-mode startup only.
--disable-pluginsand--safe-modeskip user-scope plugins and (for safe mode) workspace/session restore. These are recovery/trust aids, not a sandbox. Opening a repository still does not load plugin code from that repository; only user-installed plugins under~/.config/microide/plugins/run when plugins are enabled. - Single-window only. No detached OS windows. This is deliberate (see
openspec/specs/product-vision/spec.md), not a bug. - No native OS menu bar. The menu bar is rendered by the app.
- Terminal escape coverage is "what real shells need," not exhaustive. Programs that depend on uncommon DEC/xterm sequences may render incorrectly.
- Linux-only. Linux is the only supported host. macOS and Windows are not supported build targets; building and running on them is unsupported.
- Debugger/DAP is opt-in. A built-in DAP debugger ships (breakpoints, stepping, call stack,
variable inspection, watch/REPL, multi-session). It is gated behind
debug.enabled, which defaults to off; enable it before thedebug-*commands do anything. - No plugin marketplace, remote install, or signed-plugin verification. Deliberate non-goal.
If you find a bug or a limitation that is not listed, that itself is a bug — please file it.
microide is a local desktop application that runs with your user privileges. Plugins run in-process (on a dedicated worker thread, off the UI thread) but under an enforced per-plugin capability sandbox; that narrows, but does not eliminate, the trust you place in them. Treat it accordingly.
Plugins are capability-sandboxed local code. When you open a project, microide loads Lua plugins only from:
~/.config/microide/plugins/<plugin-id>/init.lua— user-scope plugins
Project-local directories such as <project-root>/.microide/plugins/ are ignored. That prevents
cloned repositories from executing plugin code just because you opened them.
Plugins declare a capabilities table in their init.lua descriptor, which the host enforces:
- Filesystem (
ctx.files.*) is contained to the active project root (and, for"data"scope, the plugin'sctx.workspace.data_dir()). Absolute paths and..escapes outside those roots are refused. Default: project-scoped read+write. - Process execution (
ctx.process.run/run_async, and contributed formatters / language servers / tasks) is default-deny: a plugin must declareprocess.exec, optionally with anargv[0]allowlist. On Linux, permitted children are additionally confined in the kernel — Landlock restricts writes to the project + plugin data dir, and (when network is not granted) a seccomp filter denies new IPv4/IPv6 sockets. Scope this honestly: the system stays readable/executable (so binaries and shared libraries resolve),/tmp,/dev,/run, and/var/tmpstay writable for scratch space, and the seccomp rule blocks onlyAF_INET/AF_INET6(localAF_UNIX/AF_NETLINKsockets are still allowed). Every kernel layer is best-effort and fail-open: on a kernel without Landlock/seccomp it is skipped, because the in-process capability gate — not the kernel layer — is the primary boundary. microide probes this support at startup and reports it (see below). - The Lua runtime uses a narrow stdlib (
base,table,string,math,utf8,package) — noio/os— withpackage.pathpinned to the plugin directory andpackage.cpath/loadlibdisabled, so plugins cannotrequirearbitrary modules or load native libraries. - Execution runs on a dedicated worker thread under a per-call watchdog (a runaway plugin call is aborted rather than freezing the editor), and rendering contributions (decorations, content surfaces, ghost text) are validated, size-capped data that the host draws — plugins never touch the renderer directly.
This is real enforcement, not just documentation. On Linux the kernel confinement applies to both
ctx.process.run children and contributed language-server processes. Because the kernel layer is
fail-open, microide probes Landlock/seccomp availability once at startup, logs it, and exposes it on
the control channel (status query, sandbox object) so you can confirm whether kernel confinement
is actually active on your machine rather than silently skipped. What it does not do: first-run
capability prompts, signature/marketplace trust, or isolating the Lua state itself out of process. A
plugin you grant process.exec can still run tools that read your whole project. Only install
plugins you trust into ~/.config/microide/plugins/.
Recommendations:
- A plugin with
process.execis roughly as trusted as the tools it invokes; review itscapabilitiesandinit.luabefore installing. - Only copy or symlink plugins into
~/.config/microide/plugins/when you trust their source. - The
plugins-reloadcommand picks up changes;--disable-plugins/--safe-modeturn plugins off entirely. - Project-local plugin loading remains out of scope. See SECURITY.md and dev-docs/project/git-workstation.md for supported scope.
Still out of scope. The capability sandbox above (capability-scoped APIs, narrowed Lua stdlib, per-plugin allowlists, kernel confinement of children) is implemented and enforced today. What is not planned for the immediate roadmap is the next tier of isolation: running the Lua state out-of-process, first-run capability prompts, and plugin signing / marketplace trust. If a plugin marketplace or remote install flow is ever pursued — currently a deliberate non-goal — out-of-process isolation would need to land first. For fully untrusted code, prefer external isolation (VM / container) on top of the capability sandbox.
For the full plugin trust documentation see guidelines/plugin-trust-model.md.
microide ships a perf harness with committed baselines. The harness is reproducible locally; it does not currently produce numbers that can be compared to other editors.
What is measured:
- Startup —
cold_startup_no_project,cold_startup_small_project,cold_startup_large_project - Editing throughput —
typing_small_file,typing_large_file,scroll_large_file,multi_tab_cycle - Search / index —
project_search_literal,project_search_regex,search_first_result,file_finder_cold - Shell surfaces —
compare_tab_open,merge_tab_open,compare_scroll_large_fixture,merge_scroll_large_fixture,git_sidebar_activate - Repo-open memory —
repo_open_rss_idle(asserts a steady-state RSS budget after open) - Terminal —
terminal_scroll_long_output - Idle behavior —
idle_soak_30s(asserts near-zero wake events at rest),long_soak_8h,switch_and_idle - Diff hot paths — standalone
microide_diff_bench - Search — standalone
microide_search_bench
How it is measured:
- isolated app-root:
XDG_CONFIG_HOME/XDG_STATE_HOME/XDG_CACHE_HOME/XDG_DATA_HOMEare redirected into a per-process tempdir so the developer's real config never leaks in - fixed seed (
MICROIDE_PERF_SEED=1337) - software renderer hint (
SDL_HINT_RENDER_DRIVER=software), fixed window size, dummy audio - committed fixtures under
tests/perf/fixtures/ - per-scenario JSON baselines under
tests/perf/baselines/ - explicit "smoke" vs "gate" split: smoke covers fast regression signal, gate provides the
reference baseline used by
--reference-runner=perf-runner-v1 - report metadata records
runner_class,provenance, and resolved SDL drivers; baseline updates must come fromprovenance: referenceruns
What is not measured:
- microide's startup / memory / CPU vs VSCode, Zed, Helix, Sublime, or any other editor. The project has no third-party comparative numbers and does not publish any.
- large-file open-to-first-paint is not yet a dedicated gate scenario. Advisory scenario
large_file_open_first_paintexists for explicit local runs, but the gated suite is still stronger on typing, scrolling, save normalization, and compar

0 comments
log in to comment.