How I Built a VS Code Extension That Shows What My AI Actually Learned
A step-by-step guide to building a status bar extension — from yo code to seeing your widget light up in the editor. Full source code included.
Why I built this
I run Cachly, an AI memory layer that lets coding assistants (Copilot, Claude, Cursor) remember what they learned across sessions. The MCP server stores lessons like “don’t use ${var,,} on macOS — use trinstead” and recalls them automatically before the AI makes the same mistake twice.
The problem: I had no visibility into what the brain actually knew. Was it learning? How many lessons? How many tokens was it saving me?
So I built a VS Code extension. Here’s exactly how — including the parts that bit me.
session_start / session_end needed.Step 1: Scaffold the extension
You need Node.js 20+ and the VS Code Extension Generator:
npm install -g yo generator-code
yo codeChoose New Extension (TypeScript), name it cachly-brain, enable strict TypeScript. Delete the hello-world command in src/extension.ts— we won’t need it.
Step 2: Define the manifest
The package.json is the heart of every VS Code extension:
{
"name": "cachly-brain",
"displayName": "Cachly Brain",
"version": "0.5.2",
"engines": { "vscode": "^1.85.0" },
"activationEvents": ["onStartupFinished"],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{ "command": "cachly.showBrainHealth", "title": "Cachly: Show Brain Health" },
{ "command": "cachly.showLessons", "title": "Cachly: Show Lessons" }
],
"configuration": {
"title": "Cachly Brain",
"properties": {
"cachly.apiKey": { "type": "string", "default": "", "description": "Your Cachly API key" },
"cachly.instanceId": { "type": "string", "default": "", "description": "Your Brain instance UUID" },
"cachly.refreshInterval": { "type": "number", "default": 300, "description": "Refresh interval (seconds)" }
}
}
}
}Key decisions
Use onStartupFinished — it activates after VS Code is ready, not blocking startup. Never use "*" unless you absolutely must. Configuration properties appear in the Settings UI automatically — users get a nice form instead of editing JSON.
Step 3: Create the status bar widget
import * as vscode from 'vscode';
let statusBarItem: vscode.StatusBarItem;
let refreshTimer: NodeJS.Timeout | undefined;
export function activate(context: vscode.ExtensionContext) {
statusBarItem = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right, 100
);
statusBarItem.command = 'cachly.showBrainHealth';
statusBarItem.tooltip = 'Cachly Brain Health — click for details';
context.subscriptions.push(statusBarItem);
context.subscriptions.push(
vscode.commands.registerCommand('cachly.showBrainHealth', showHealthPanel),
vscode.commands.registerCommand('cachly.refreshBrain', updateStatusBar),
);
startRefreshLoop();
}What I learned the hard way
Always push to context.subscriptions. Otherwise your disposables leak and VS Code complains on reload. Use StatusBarAlignment.Right — the left side is crowded with Git/problems indicators. The command property is just a string — it must match what you registered in package.json AND in registerCommand(). Typos mean silent failures.
Step 4: Fetch data with zero dependencies
import * as https from 'https';
import * as http from 'http';
function apiGet(url: string, apiKey: string): Promise<unknown> {
return new Promise((resolve, reject) => {
const parsedUrl = new URL(url);
const mod = parsedUrl.protocol === 'https:' ? https : http;
const req = mod.get(url, {
headers: { 'Authorization': `Bearer ${apiKey}`, 'Accept': 'application/json' },
timeout: 5000,
}, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
try { resolve(JSON.parse(data)); }
catch { resolve(null); }
});
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
});
}VS Code extensions run in Node.js, not a browser. Using the built-in http/https modules gives you control over timeouts and avoids polyfill issues across VS Code versions — with zero added dependencies.
Step 5: The refresh loop
function startRefreshLoop() {
if (refreshTimer) clearInterval(refreshTimer);
const config = vscode.workspace.getConfiguration('cachly');
const apiKey = config.get<string>('apiKey', '');
const instanceId = config.get<string>('instanceId', '');
if (!apiKey || !instanceId) {
statusBarItem.text = '$(brain) Cachly: not configured';
statusBarItem.show();
return;
}
updateStatusBar();
const interval = config.get<number>('refreshInterval', 300) * 1000;
refreshTimer = setInterval(() => updateStatusBar(), interval);
}
// Restart loop when the user saves new settings
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('cachly')) startRefreshLoop();
});Without the onDidChangeConfiguration listener, users have to reload VS Code after updating their API key. Bad UX — two lines fix it.
Step 6: Rich detail panel (Markdown, no webview)
async function showHealthPanel() {
const health = await fetchBrainHealth();
const lines = [
'# 🧠 Cachly Brain Health\n',
'| Metric | Value |',
'|--------|-------|',
`| Status | ${health.status === 'healthy' ? '✅' : '❌'} |`,
`| Lessons | **${health.lessons}** |`,
`| Recalls | **${health.totalRecalls}** |`,
`| Tokens Saved | ~${health.estimatedTokensSaved} |`,
];
const doc = await vscode.workspace.openTextDocument({
content: lines.join('\n'),
language: 'markdown',
});
await vscode.window.showTextDocument(doc, { preview: true });
}No webview needed — Markdown renders natively with tables, headers, and emoji. Preview mode avoids cluttering the editor with permanent tabs. Users can also copy-paste the health report.
Step 7: Package and install
npm install -g @vscode/vsce
vsce package
# → cachly-brain-0.5.2.vsix
code --install-extension cachly-brain-0.5.2.vsixPackaging gotchas
Node version matters — vsce 3.x requires Node 20+. Create a .vscodeignore to exclude src/, node_modules/, and tsconfig.json from the package — otherwise your VSIX bloats from ~15 KB to several MB.
Step 8: Publish to the Marketplace
vsce login cachly
vsce publishYou need a Visual Studio Marketplace publisher and a Personal Access Token with Marketplace scope. The first publish takes a few minutes to appear — Marketplace listing for the Cachly Brain extension is coming soon.
The satisfying result
After all this, you open VS Code and see 🧠 Brain: 42 lessons in the status bar. Click it and you get a full report: which bugs the AI fixed, how many times each lesson was recalled, estimated token savings.
Seeing your own widget in the editor you use every day — not a web app in a tab, but inside your tool — is deeply satisfying.
Try it yourself.Install the Cachly Brain extension, set your instance ID and API key, and watch your AI’s lessons accumulate in real time.