What is an extension?
A Silo extension is a small object with an id and an activate function. The host calls activate once at startup and hands it an ExtensionContext — usually called ctx. Everything your extension adds to the app, it adds through ctx.
import type { Extension } from "@silo-code/sdk";
export const extension: Extension = {
id: "acme.hello",
activate(ctx) {
// register viewers, commands, panels… against ctx here
},
};That's the whole contract (Extension). There's an optional deactivate() for cleanup, reserved for when dynamic load/unload lands.
The one rule
An extension touches the running app only through
ctxand the@silo-code/sdktypes. It never imports app internals.
This isn't a style preference — it's enforced (a lint rule fails the build if an extension reaches into the app's state, services, or UI directly). The payoff is that extensions stay decoupled from the app's internals, so the app can evolve without breaking them. The status-bar toggle below — a complete extension — imports nothing but SDK types.
import type { Extension } from "@silo-code/sdk";
import { useServiceState } from "@silo-code/sdk";
export const extension: Extension = {
id: "acme.panel-toggle",
activate(ctx) {
const { layout } = ctx;
function PanelToggles() {
const state = useServiceState(layout); // read state via a typed service
return (
<button
className={state.left.collapsed ? "" : "active"}
onClick={() => ctx.executeCommand("view.toggleLeftPanel")} // act via a command
>
▢
</button>
);
}
ctx.registerStatusItem({ id: "panel-toggles", alignment: "right", component: PanelToggles });
},
};It reads state through a typed service (ctx.layout) and acts by invoking a command (ctx.executeCommand) — never by importing the app's store.
How ctx is organized
Everything ctx offers falls into three groups:
| Group | What it's for |
|---|---|
| Registration | register* methods that add things to the app — viewers, side panels, status items, commands, keybindings, menu items, file types, dock panels, settings pages. Each returns a Disposable. |
| State | typed services that read & drive app state — ctx.workspaces (workspaces and editor tabs) and ctx.layout (side-panel collapse) — so you never reach into the store. |
| Extension APIs | ctx.getExtension(id) to consume another extension's published API — how features that live outside core (git, terminal, themes) expose capabilities to everyone else. See Extension-to-extension APIs. |
| Other | ctx.executeCommand(id) to invoke any registered command, plus ctx.extensionId and ctx.subscriptions. |
The API Reference walks through every member of each group, with signatures, examples, and links to the type definitions.
Next
Build one — Your first extension walks through a complete, working status-bar clock step by step. Prefer to describe what you want and let an AI do the scaffolding? See Build with Claude Code.
Depend on another extension, or let others depend on yours — extensions aren't only UI. A provider extension can register nothing at all and just publish a typed API — see Extension-to-extension APIs.
Polish the UI — Styling your extension, Workspace status, sections & badges, Keyboard navigation, and Building a theme cover the visual surface.
Ship it — Permissions & access, Publishing an extension, and Sharing extensions cover the packaging and distribution side.
Explore ctx — the API Reference documents every member with signatures, examples, and generated type definitions.