| name | silo-extension-builder |
| description | Help the user design, write, compile, and install a Silo extension from a natural-language description. The extension follows the same format as any publishable Silo extension and can be shared via npm or a tarball URL when ready. |
| tools | Bash, Read, Write |
Extension Builder
Turn a natural-language description into a working Silo extension. The extension
is real, publishable TypeScript โ not a prototype or a hack. silo install drops
it into Silo immediately; silo uninstall removes it cleanly.
Arguments
The skill accepts two optional arguments: id and path.
id โ the extension id (e.g. dave.clock). If not provided, ask the user.
path โ the directory to create the extension in. If not provided, default to /tmp/silo-ext/<id> and confirm with the user.
Parse arguments from the invocation line. Examples:
/silo-extension-builder id=dave.clock โ id given, use default path
/silo-extension-builder path=~/projects/my-ext โ path given, ask for id
/silo-extension-builder id=dave.clock path=~/projects/my-ext โ both given, skip both questions
/silo-extension-builder โ neither given, ask for both before proceeding
Workflow (always in this order)
- Resolve args โ extract
id and path from the invocation; ask the user for any that are missing
- Scaffold โ run
npx create-silo-extension to create the directory structure and manifest
- Plan โ identify which
ctx.* APIs the extension needs (see API Reference below)
- Write the implementation to
<path>/src/index.tsx (overwrites the scaffold placeholder)
- Compile with esbuild (output to
<path>/dist/index.js)
- Ask โ show the user the compiled output path and ask if they want to install it now
- Install (only if the user says yes) via
silo install <path>
- Tell the user what to look for in Silo
Choosing an extension id
If the user didn't supply one, ask before writing any files. Pick a namespaced id
that reflects what the extension does: dave.clock, dave.tasks,
dave.git-branch, etc. The user's name or handle makes a natural namespace.
Step 2 โ Scaffold
Run create-silo-extension with all known args to avoid interactive prompts.
Prefer the local build when inside the Silo repo; fall back to npx otherwise:
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
LOCAL_SCAFFOLD="$REPO_ROOT/packages/create-silo-extension/dist/index.js"
if [ -f "$LOCAL_SCAFFOLD" ]; then
SCAFFOLD_CMD="node $LOCAL_SCAFFOLD"
else
SCAFFOLD_CMD="npx --yes create-silo-extension"
fi
$SCAFFOLD_CMD \
--id dave.<slug> \
--path <path> \
--name "<Display Name>" \
--description "<short description>" \
--publisher "<publisher>"
This creates <path>/src/index.tsx (placeholder) and <path>/package.json.
Skip this step if the directory already exists from a previous run.
Step 3 โ Source file shape (write over the scaffold placeholder)
import React, { useState, useEffect } from "react";
import type { Extension, SidePanelProps } from "@silo-code/sdk";
import { useServiceState } from "@silo-code/sdk";
export const extension: Extension = {
id: "dave.<slug>",
manifest: {
name: "My Extension",
description: "What it does.",
version: "0.1.0",
},
activate(ctx) {
},
};
Step 4 โ Compile
cd <path> && npm run build
The scaffold's package.json includes build (one-shot) and dev (watch) scripts
wired to esbuild with the correct flags. Use npm run dev for interactive iteration.
Step 5 โ Tell the user what to look for
There is no programmatic verification โ Silo is a production desktop app.
Describe what appeared and where:
- Status bar item: "A clock now appears on the left side of the status bar."
- Side panel: "Open the left sidebar and click the Tasks icon to see the panel."
- Command: "Run it via the command palette (โK โ 'Tasks: Add item')."
- Extensions panel: "It should appear in Settings โ Extensions with an Uninstall button."
Iteration
To rebuild: cd <path> && npm run build, ask the user if they want to
reinstall, then silo install <path> if they confirm. Silo replaces the installed version.
To remove: silo uninstall <id>
Publishing
When the extension is ready to share:
- Move source to a proper project directory
npm publish
- Others install it from Silo's Extensions settings page by typing the package name
API Reference
Registration โ what activate(ctx) can register
Commands โ named, invokable actions:
ctx.registerCommand({ id: "dave.foo.bar", label: "Foo: Do Bar", run: () => { ... } });
Status bar items โ widgets in the strip at the bottom:
ctx.registerStatusItem({
id: "dave.foo.status",
alignment: "left" | "right",
priority: 0,
tooltip: "optional hover text",
component: MyStatusWidget,
});
Side panels โ left or right column panels:
ctx.registerSidePanel({
id: "dave.foo.panel",
location: "left" | "right",
title: "My Panel",
component: MyPanel,
lazyMount: true,
});
SidePanelProps: { active: boolean, storage: ExtensionStorage, hydrated: boolean }
Settings pages โ a page in the Settings dialog:
ctx.registerSettingsPage({
id: "dave.foo.settings",
title: "My Settings",
component: MySettings,
});
Menu items โ entry in File/Edit/View/Window menu:
ctx.registerMenuItem({
id: "dave.foo.item",
menu: "file",
command: "dave.foo.bar",
label: "...",
group: "9_custom",
});
Keybindings โ keyboard shortcut:
ctx.registerKeybinding({
id: "dave.foo.key",
key: "cmd+shift+l",
command: "dave.foo.bar",
});
Avoid registerDockPanelKind โ dock kinds can't be cleanly removed without a reload.
Services โ what ctx.* provides
ctx.process.exec(cmd, args, opts?) โ one-shot subprocess:
const { stdout, code } = await ctx.process.exec("git", [
"branch",
"--show-current",
]);
ctx.workspaces โ reactive workspace state:
const ws = useServiceState(ctx.workspaces);
const activeFolder = ws.open.find((w) => w.id === ws.activeId)?.folder;
ctx.editors โ editor/document model:
const es = useServiceState(ctx.editors);
ctx.files โ filesystem:
const text = await ctx.files.readText(path);
await ctx.files.writeText(path, content);
const entries = await ctx.files.list(dir);
ctx.ui โ toast notifications, file pickers, modals:
ctx.ui.notify("info" | "warn" | "error", "message");
const path = await ctx.ui.pickFile();
const ok = await ctx.ui.confirm({
title: "...",
body: "...",
confirmLabel: "...",
});
SidePanelProps.storage โ persisted key/value per panel:
storage.get<T>("key");
storage.set("key", value);
storage.subscribe(listener);
React patterns
Reactive workspace subscription (get active workspace folder):
function MyStatusWidget() {
const ws = useServiceState(ctx.workspaces);
const folder = ws.open.find((w) => w.id === ws.activeId)?.folder ?? null;
}
CSS theming โ always use design tokens, never hard-code colors/fonts:
color: var(--silo-color-fg);
background: var(--silo-color-surface);
border: 1px solid var(--silo-color-border);
font-family: var(--silo-font-mono);
border-radius: var(--silo-radius-sm);
Example Prompts
Git branch status bar:
/silo-extension-builder Create a status bar item on the left that shows the current git branch for the active workspace, with a dot when there are uncommitted changes. Update when the active workspace switches.
Workspace task list:
/silo-extension-builder Create a right-side panel called "Tasks" with a persistent to-do list. Items have a checkbox and a delete button. Persist tasks so they survive app restarts.
GitHub issues panel:
/silo-extension-builder Create a right-side panel called "Issues" that fetches open GitHub issues for the active workspace's repo using gh issue list --json number,title,url. Show a refresh button and make each issue a clickable link. Load lazily on first open.
Open PRs panel:
/silo-extension-builder Same as the Issues panel but for pull requests โ call it "Pull Requests", use gh pr list --json number,title,isDraft,url, and dim draft PRs.
Scratch pad:
/silo-extension-builder A right-side panel called "Scratch Pad" with a full-height textarea, persisted via storage so notes survive restarts. Use Silo's theme tokens for colors and the monospace font.
Word count status bar:
/silo-extension-builder A right-aligned status bar item showing the word count of the currently active editor. Update when the active editor changes.