Your first extension
We'll build a small but complete extension: a status-bar clock with a command to toggle 12/24-hour format and a keybinding for it. It uses three of ctx's registration methods — a realistic slice of the API in ~40 lines.
What you're building
An extension is a standalone package — your own folder, with its own package.json, that you build against @silo-code/sdk (installed from npm as a devDependency) and install into Silo via the CLI or the Extensions settings page. You don't need Silo's source to build one. This page writes the code; Publishing an extension covers the packaging contract in full.
1. Scaffold
The fastest way to get started is the create-silo-extension scaffolding tool. Run it from any directory and answer the prompts:
npx create-silo-extensionIt will ask for an extension id, display name, description, publisher, and output path, then create the project:
acme.clock/
src/index.tsx ← extension source (edit this)
package.json ← manifest with silo.id, silo.main, build scripts
dist/ ← compiled output (populated by npm run build)You can also pass flags to skip the prompts:
npx create-silo-extension --id acme.clock --name "Clock" --path ~/my-extensions/acme.clockThe scaffolded src/index.tsx starts from this shell — a complete Extension object whose activate is where all registrations go:
// src/index.tsx
import { useEffect, useState } from "react";
import type { Extension } from "@silo-code/sdk";
export const extension: Extension = {
id: "acme.clock",
activate(ctx) {
// everything goes here
},
};activate runs once on load; ctx is your ExtensionContext.
2. Add a command
A Command is a named action. We'll have it flip a flag held in module scope (a real extension might persist this — see step 5).
activate(ctx) {
let use24h = false;
ctx.registerCommand({
id: "clock.toggleFormat",
label: "Clock: Toggle 12/24-Hour",
run: () => { use24h = !use24h; },
});
}registerCommand returns a Disposable; the host also tracks it on ctx.subscriptions, so you don't have to clean it up manually.
3. Bind a key
A Keybinding points a shortcut at a command id:
ctx.registerKeybinding({
id: "clock.toggleFormat",
key: "cmd+shift+k",
command: "clock.toggleFormat",
});4. Render it in the status bar
A StatusItem is a React component placed at one end of the status bar. Clicking it runs our command via ctx.executeCommand:
function Clock() {
const [now, setNow] = useState(() => new Date());
useEffect(() => {
const t = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(t);
}, []);
const time = now.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: !use24h,
});
return (
<button onClick={() => ctx.executeCommand("clock.toggleFormat")}>
{time}
</button>
);
}
ctx.registerStatusItem({
id: "clock",
alignment: "right",
priority: 10,
component: Clock,
});Defining Clock inside activate lets it close over ctx and use24h. activate runs once, so the component identity stays stable.
5. Build & install
The scaffolded package.json already has the silo manifest key and build scripts set up. The silo.id must match extension.id in your code (the scaffold ensures this):
{
"name": "silo-acme-clock",
"version": "0.1.0",
"type": "module",
"scripts": {
"build": "esbuild src/index.tsx --bundle ...",
"dev": "esbuild src/index.tsx --bundle ... --watch",
},
"silo": {
"id": "acme.clock", // must equal extension.id
"engine": "^0.1",
"main": "dist/index.js",
"publisher": "Acme",
},
}Build the extension (the scaffold uses esbuild, leaving react, react/jsx-runtime, and @silo-code/sdk as externals — see the build contract):
cd acme.clock
npm run build # one-shot compile
npm run dev # watch mode — recompiles on every saveThen install it into Silo:
silo install . # install from the current folderOr open Settings → Extensions → Install from folder… and pick the folder.
The clock appears at the right of the status bar; click it or press ⌘⇧K to toggle the format. To uninstall:
silo uninstall acme.clockEdits reload with Disable / Enable in the Extensions settings — no app restart needed. See Publishing an extension for the full packaging reference.
Going further
- Persist the format with the storage handed to side panels (
ExtensionStorage) or your own approach, instead of a module-scoped flag. - Open files from a command via
ctx.editors— the sanctioned entry point for editor tabs. - Add a settings page with
registerSettingsPage(theclock-extensionexample adds one). - Polish the UI — Styling your extension, Workspace status, sections & badges, and Keyboard navigation.
- Ship it — Permissions & access and Publishing an extension.
- Browse every type in the API Reference.