Roadmap
State of this: nothing on this page is built. Every page in these docs describes something that works and proves it with an animation. This one is the opposite: a comparison of deco against VS Code, and a chapter per missing feature — what it would look like here, and the steps to build it. Each chapter is a plan, not a promise; when one ships, its chapter moves out of this page and into a real one with an animation of its own.
Where deco stands against VS Code
What already works is documented, with animations, on the other pages of this site; the top-level README keeps the authoritative compatibility table. In one line: configuration, keybindings and themes are read with VS Code’s meaning; editing, tabs, splits, find and replace, quick open, the command palette, workspace-wide search, syntax highlighting and the listed language-server features work; extensions run in a sandboxed host; remote sessions can open, edit and save.
What VS Code has and deco does not, grouped by how it blocks:
| Missing | Depends on |
|---|---|
| Sidebar and panel — the chrome itself | nothing; everything below sits in it |
| File tree (the explorer) | the sidebar |
| Git — gutter diffs, branch in the status bar, stage and commit | the sidebar, and a status-bar segment |
| Integrated terminal | the panel, and a PTY dependency |
Task runner (tasks.json, ctrl+shift+b) |
the terminal, for somewhere to run |
| Test runner | the task runner, and later the extension host |
| Self-update | nothing — cargo xtask dist already builds what it would install |
AI features — inline completions, chat, agent mode, MCP (chat.disableAIFeatures) |
the extension host, ghost text, and the panel; agents additionally on WorkspaceEdit and the terminal; the off switch depends on nothing and comes first |
| Debugging (DAP) | the panel; by far the largest item here |
WorkspaceEdit — one undoable edit across several files |
nothing; rename, code actions and replace-across-files all wait on it |
| Regular-expression search | nothing — deco-core::search is deliberately literal so far |
| Snippet tab stops | nothing |
The dependencies are why the order below is the order. Three items are
foundations — the sidebar and panel, WorkspaceEdit, and finishing
the extension-host wiring — and most of what users actually ask for (the
tree, git, the terminal) is a tenant of one of them.
Two rules carry over from everything already built, and every chapter below is written under them:
- VS Code’s identifiers, exactly. The commands are
git.stageandworkbench.action.terminal.toggleTerminal, the settings aregit.enabledandterminal.integrated.defaultProfile.linux— never adeco.*synonym for something VS Code already has a name for. That is what makes an existingkeybindings.jsonkeep meaning what it meant. - A key that waits on a feature says so.
ctrl+bandctrl+jare already bound and already name the sidebar and the panel as the features they are waiting on; a test over the default keymap keeps every such key honest. Each chapter shipping turns one of those refusals into behaviour.
The sidebar and the panel
What VS Code has. A left sidebar hosting views (explorer, search, source control, extensions), a bottom panel hosting others (terminal, problems, output), and commands to toggle and focus them.
What deco has. The bindings — ctrl+b is
workbench.action.toggleSidebarVisibility, ctrl+j is
workbench.action.togglePanel — and nothing behind them. The TUI lays out one
editor region plus the status bar and prompt; there is no notion of a second
region with focus of its own.
The plan. A Region layer in deco-editor’s layout: the frontend hands
the session a rectangle, the session splits it between editor, sidebar and
panel, and each region renders through the same pure-function path the editor
does today — so the split is testable in CI with no terminal attached, which is
the property everything else here already has. Focus becomes part of the
session (sideBarFocus, panelFocus join the when-clause context keys), so
the keymap can route keys the way VS Code does. The GUI frontend draws the same
regions; it gets them for free once they exist below the frontends.
Steps.
- Add regions and focus to
deco-editor::layout, with the editor as the only tenant, and tests asserting the arithmetic of the split. - Wire
ctrl+b/ctrl+jto toggle empty regions, replacing their named refusals; add the context keys todeco-keymap’s when engine. - Teach
deco-tui’s renderer to paint a region border and a placeholder body; same fordeco-gui. - Define the view contract — what a tenant must implement to be given a region — against which every later chapter builds.
The file tree
What VS Code has. The explorer: a workspace tree with create, rename,
delete, and reveal; workbench.files.action.focusFilesExplorer,
revealInExplorer.
What deco has. ctrl+p (quick open) over the workspace file list, which is
already enumerated with files.exclude honoured — see deco-tui::files. No
tree.
The plan. The first sidebar tenant. The same file enumeration that feeds
quick open feeds the tree; directories expand lazily so a large workspace costs
what its visible rows cost, which is the bounded-by-the-window rule every hot
path here already follows. Enter opens in a tab; create, rename and delete come
once WorkspaceEdit exists, so that a rename of an open file and the retarget
of its tab are one undoable thing. On a remote workspace the tree lists the far
end through the existing deco-remote file listing, exactly as quick open
already does.
Steps.
- A tree model over the existing walker: visible rows only, expansion state in the session.
- Render as a sidebar view; keyboard first (VS Code’s explorer keys), mouse later with the GUI’s mouse work.
- Open-on-enter, reveal-active-file; the
filesExplorerFocuscontext key. - Mutations (new file, rename, delete) after
WorkspaceEditlands.
Git
What VS Code has. A source-control view (stage, unstage, commit, discard),
gutter decorations for changed lines, the branch and dirty count in the status
bar, and a git.* command family. VS Code implements all of it by running the
git binary, not by linking a library.
What deco has. Nothing. .git appears in the code only as a directory to
skip in search and a marker for finding the workspace root.
The plan. Shell out to git, as VS Code does — it keeps the dependency
count where it is (the binary adds no crate), it inherits the user’s hooks and
config, and a machine without git simply has the feature absent rather than a
copy of libgit2 nobody asked for. Three stages, each useful alone:
- Status bar: branch name and a changed-file count, from
git status --porcelain=v2 --branch, refreshed on save and on focus. No UI to build — the status bar exists. - Gutter: changed/added/deleted line markers, from
git diffof the buffer againstHEAD, computed the way everything else is — for the visible rows, resumed from the earliest edited line. - The source-control view: a sidebar tenant listing changed files;
git.stage,git.unstage,git.commit(message through the existing prompt),git.checkoutthrough the existing picker.git.enabledandgit.decorations.enabledare read with their VS Code meanings.
Diffing an open buffer against HEAD means diffing unsaved text; the diff
runs against the buffer’s content handed to git diff --no-index (or computed
in-process against the blob), never against the file on disk pretending to be
the buffer.
Steps.
- A
deco-scmcrate: spawngit, parse porcelain v2, one supervisor per workspace modelled ondeco-lsp’s server supervisor. - Status-bar segment; refresh triggers; a workspace without git, or a machine without the binary, shows nothing and logs why once.
- Line diff for the visible window; gutter marks in both frontends.
- The sidebar view and the
git.*commands, last, because the first two are most of the daily value.
The integrated terminal
What VS Code has. Terminals in the panel, ctrl+` to toggle, profiles,
and the terminal.integrated.* settings family.
What deco has. The bindings — ctrl+` is already
workbench.action.terminal.toggleTerminal, refusing by name — and nothing
behind them. And one honest complication the plan has to answer: deco’s TUI
is already inside a terminal.
The plan. A PTY per terminal, a VT parser feeding a screen model, and the
panel region painting that model — through the same pure render path, so a
terminal’s screen is assertable in CI like any other layout. This is the one
chapter that adds a real dependency (a PTY crate, and possibly a VT parser);
the README counts its 44 crates in public, so the addition is made once, named
in the dependency table, and justified by this feature alone. In the GUI the
same model paints into the window; in the TUI the inner terminal’s cells map
onto the panel’s cells, colours through the existing theme layer. In a remote
session the shell belongs on the machine holding the files: the server side
grows a PTY endpoint over the existing deco-remote framing, the way language
servers already run over there.
Steps.
- Choose and justify the PTY dependency; spawn the user’s shell
(
terminal.integrated.defaultProfile.*), pump its output. - A screen model: grid, cursor, scrollback, SGR colours mapped through
deco-theme. - Panel view, focus routing — keys go to the shell when the terminal has
focus, except the escape hatch VS Code also keeps (
ctrl+j,ctrl+`). - Remote PTYs over the session connection.
The task runner
What VS Code has. tasks.json (build tasks, ctrl+shift+b =
workbench.action.tasks.build, workbench.action.tasks.runTask from the
palette), problem matchers that turn output into diagnostics.
What deco has. Nothing, including the bindings.
The plan. Read .vscode/tasks.json — the file users already have — through
deco-config’s existing JSONC reader. A task runs in the integrated terminal
(which is why this chapter follows that one) and its exit status lands in the
status bar. Problem matchers come second: the named matchers ($rustc, $tsc)
map output lines to the same diagnostics pipeline LSP already fills, so a
compile error from a task underlines code exactly as a language server’s would.
Steps.
- Parse
tasks.json(shellandprocesstypes,group.kind == "build"); surface tasks in the palette under their VS Code command names. - Run in a terminal; report exit status.
- The common problem matchers, into
deco-lsp’s diagnostics store. ${workspaceFolder}and friends — the variable set, resolved against the session.
The test runner
What VS Code has. A testing view fed by extensions through the
vscode.tests API; run/debug at the test, file and suite level; results inline
in the gutter.
What deco has. Nothing. (deco’s own test suite is documented in Testing; this chapter is about running your tests.)
The plan. Two honest stages. First, tests are tasks: a group.kind ==
"test" task bound to VS Code’s test-task command, run in the terminal — no new
UI, immediately useful. Second, a real testing view once the extension host can
carry it, because in VS Code the things that discover tests are extensions,
and deco’s decision is to run those rather than to hardcode one runner per
language. That makes the full feature a tenant of the extension-host chapter
below, and the mediated API it needs (tests.*) an entry in the capability
table like readFile before it.
Steps.
- Test-group tasks through the task runner.
- The
vscode.testssurface in the host shim, brokered like the rest. - A sidebar testing view rendering what extensions report; run through tasks, results to gutter marks.
Self-update
What VS Code has. Background download and install, update.mode to turn it
off.
What deco has. cargo xtask dist builds the release archive and writes its
.sha256 beside it; the release workflow publishes both for seven targets. No
updater.
The plan. deco --update: ask GitHub Releases for the latest tag, compare
versions, download the archive for this target, verify the checksum, replace
the running binary atomically (write beside, rename over — with the Windows
rename dance, which is exactly what cargo xtask cross’s Wine run exists to
exercise). Explicitly invoked, never in the background: an editor that phones
home unasked contradicts the way everything else here treats ambient authority,
so update.mode is read but only none and manual are honoured, and the
status bar may say a release exists only if a check was asked for. A
package-manager install (where the binary is not the user’s to replace) is
detected and refused with the right command named instead.
Steps.
- Version check against the Releases API;
--update --check-onlyprints and exits. - Download, verify against the published
.sha256, stage beside the binary. - Atomic replace, per platform, with tests where tests can run (Wine covers the Windows path daily).
- The read-only-install detection and its refusal message.
AI features
What VS Code has. Copilot woven into the core rather than shipped beside
it: inline completions as ghost text, a chat view, an agent mode that edits
files — and, for the people who want none of that, chat.disableAIFeatures,
the one setting that hides all of it.
What deco has. Nothing — no AI code, no account, and nothing that phones home. Which is not only a gap: for one of the two camps this chapter has to serve, it is the feature, and the plan below is written so that building for the other camp never takes it away.
The plan. Opinion genuinely splits here — some people want AI assistance in the editor, and some want it provably absent, not merely hidden. deco can serve both honestly because of two decisions already made: AI arrives as extensions, never as a built-in, and an extension has no ambient authority.
- The off switch is read first, and it means more here.
chat.disableAIFeatures: true— VS Code’s own key — is honoured as a hard gate: no AI-declaring extension activates, no AI surface renders. And where VS Code’s switch hides features that are still installed, deco’s sits on top of the capability broker: an extension can only reach a model through a declarednetworkcapability, so with the gate closed there is no path to a model at all — provably absent rather than out of sight. With nothing AI shipped by default, a fresh deco already behaves as if the switch were on. - Using AI is an explicit grant, not a default. An AI extension declares
the host it talks to —
{"capability": "network", "host": "api.anthropic.com"}— which is visible before anything runs and decided underextensions.permissions.defaultlike every other capability. A local model is the same declaration with a loopback host (an Ollama onlocalhost), and the difference between “my code goes to a vendor” and “my code stays on this machine” is readable off the manifest instead of taken on faith. - The surfaces are the ordinary ones. Inline completions are ghost text —
a rendering concern worth building once, since parameter hints and inlay
hints want it too. Chat is a sidebar or panel tenant like the views before
it. Both are fed through the host’s mediated API
(
InlineCompletionItemProvider, the chat participant surface), so an AI extension is not a special kind of extension — it is an extension with an unusually interestingnetworkdeclaration.
Steps.
- Read
chat.disableAIFeaturesand enforce it in the broker — a gate an extension cannot argue with, not a UI preference; refuse activation of extensions whose manifest declares AI surfaces while it is set. - Ghost-text rendering in both frontends, as its own feature.
InlineCompletionItemProviderthrough the host shim, brokered like the rest.- A chat view as a sidebar/panel tenant, last — it is the largest surface and the least of the daily value.
Agent integration
Completions and chat are the small half of what “AI features” now means. The
larger half is agents: a model that plans, edits several files, runs
commands and iterates — VS Code’s agent mode (chat.agent.enabled), its MCP
support (Model Context Protocol servers offering tools to the model), and the
external CLI agents people run beside their editor. This is where deco’s
architecture stops being a constraint on AI and starts being the point, so it
is planned as its own stage rather than left implied by “chat”.
- An agent is the capability model’s hardest customer, and its best
argument. In VS Code an agent’s tool calls run with the user’s full
privileges and safety is a per-call confirmation dialog. Under deco every
tool an agent reaches for is already a brokered capability: file access is
scoped and checked on resolved paths, running a program is a declared
capability decided by policy, and the network is a named host. Nothing new
has to be invented to make an agent safe — the broker does not care whether
a
writeFilewas asked for by a keystroke or by a model. What agents add is volume, which the permission UX must absorb: session-scoped grants (“this agent may editsrc/until this chat ends”) rather than a dialog per call, and an audit trail of what was touched. - An agent’s edits arrive as a
WorkspaceEdit. The multi-file, undoable edit that rename needs is exactly the unit an agent’s changes should land as: applied atomically, reviewable as a diff before or after, and undone as one step —ctrl+zas the recovery from a bad agent turn. This is the strongest reasonWorkspaceEditis a foundation and not a feature. - MCP fits the broker better than it fits VS Code. An MCP server is a
local process speaking JSON-RPC over stdio — structurally what
deco-lspalready supervises.mcp.jsonnames the servers; starting one is aprocesscapability, and each tool the server offers becomes a named, individually grantable capability rather than a blanket “the model may use tools”. Deny-by-default then means a tool the user never approved is never offered to the model at all. - External CLI agents come through the terminal, not an API. People already run Claude Code and its peers beside their editor. The integrated terminal is the honest first integration: the agent runs there, and the editor’s job is to notice what it changed — files reloading cleanly (watch for external modification, which unsaved-conflict handling needs anyway) and the git gutter showing the agent’s diff. Deeper integration — the agent driving the editor — is the same mediated surface extensions get, and nothing more.
Steps (agents).
- Session-scoped grants and an activity log in the broker — the permission UX for high-volume callers, built before any agent uses it.
- Agent edits as
WorkspaceEdits: atomic apply, one-step undo, a diff view of what a turn changed. - MCP: supervise
mcp.jsonservers with thedeco-lspsupervisor pattern, surface each tool as a grantable capability. - Terminal-first support for external CLI agents: external-change reload and gutter diffs are the integration.
- Agent mode in the chat view, last, gated by
chat.agent.enabledand insidechat.disableAIFeatureslike everything else in this chapter.
Debugging
What VS Code has. The Debug Adapter Protocol: breakpoints, stepping,
variables, the debug console, launch.json, F5.
What deco has. Nothing, and no near-term plan — this chapter exists so the
answer is written down rather than implied. DAP is the right shape for deco
(a JSON protocol over stdio to a separate adapter process — structurally a
sibling of deco-lsp, whose framing it literally shares), and launch.json
already parses with the JSONC reader. But the surface is the cost:
breakpoints in the gutter, a stopped-state overlay, variables and watch views,
a debug console — each a tenant of chrome that must exist first. It is
deliberately last: everything above it is smaller, more asked-for, and most of
it is chrome that debugging will then stand on.
Steps, when it is begun.
deco-dap: framing (shared withdeco-lsp), lifecycle, capabilities — the supervisor pattern a third time.- Breakpoints as a session concept, gutter-rendered, sent on attach.
- Launch/attach from
launch.json; stop/continue/step with a stopped-line marker. - Variables and console as panel tenants.
The gaps behind the features
Three smaller items block or shrink the chapters above and are worth doing first; they are listed in the README’s “what is not built yet” and repeated here because the plans above lean on them.
WorkspaceEdit. One undoable action across several documents, open or not. Unblocks LSP rename and code actions (the keys already refuse by name), replace-across-files, and the file tree’s mutations. The shape: a plan of per-document invertible edit groups, validated against document versions before any write, applied all-or-nothing, undone as one step.- Regular-expression search.
deco-core::searchis literal on purpose; regex needs its own escaping rules and its own error reporting for a bad pattern (alt+rsays so today). Decision to make: a regex crate dependency versus the subset a hand-written engine can honestly support. The find bar, multi-cursor find and search-in-files all inherit whichever lands. - Snippet tab stops. deco advertises
snippetSupport: falseand flattensfoo(${1:arg})tofoo(arg), saying so in the status bar. Tab stops are a session concept (ordered regions,tabcycles,escapeleaves) and unlock both server completions and, later, user snippets.