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.
cachly is a persistent AI Brain for developers — memory shared across Claude Code, Cursor, GitHub Copilot & Windsurf simultaneously. Auto-detects every editor. Bootstraps from your git history. 115 MCP tools. Free tier, EU servers, no credit card.
Your AI is forgetting everything right now.
Every session starts blank. Every bug re-discovered. Every deploy procedure re-explained. cachly fixes that in 30 seconds — your AI remembers every lesson, every fix, every teammate's hard-won knowledge. Forever.