Skip to content
Back to skills

Vscode Extension Patterns

ASecurity

Reusable patterns for VS Code extension development.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentsjavascripttypescriptgojavashellnodetestinggitapisecurity

Works with

  • vscode
  • cli
  • api

Security analysis

A100/100

Scanned September 3, 2026

npx -y skills add fabioc-aloha/Alex_Skill_Mall --skill vscode-extension-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Vscode Extension Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Vscode Extension Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fabioc-aloha-vscode-extension-patterns/badge)](https://www.skillsdirectory.com/skills/fabioc-aloha-vscode-extension-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: vscode-extension-patterns
description: "Reusable patterns for VS Code extension development."
lastReviewed: 2026-04-30
---

# VS Code Extension Patterns


> Reusable patterns for VS Code extension development.
> **Staleness Watch**: See `EXTERNAL-API-REGISTRY.md` for source URLs and recheck cadence

**Last validated:** March 2026 (VS Code 1.111+)

---

## Webview Dashboard

```typescript
const [health, knowledge] = await Promise.all([checkHealth(), getKnowledgeSummary()]);
panel.webview.html = await getWebviewContent(health, knowledge);
```

---

## TreeDataProvider for Sidebar

```typescript
class WelcomeViewProvider implements vscode.WebviewViewProvider {
  resolveWebviewView(view: vscode.WebviewView) {
    view.webview.options = { enableScripts: true };
    view.webview.html = this.getHtmlContent();
    view.webview.onDidReceiveMessage(async (msg) => {
      if (msg.command === "refresh") await this.refresh();
    });
  }
}
vscode.window.registerWebviewViewProvider("my.welcomeView", new WelcomeViewProvider());
```

---

## CSP-Compliant Webview Events

**Problem**: Inline `onclick` handlers violate CSP.

**Solution**: Use `data-cmd` with delegated listener:

```html
<button data-cmd="play">Play</button>
```

```javascript
document.addEventListener("click", (e) => {
  const cmd = e.target.closest("[data-cmd]")?.getAttribute("data-cmd");
  if (cmd === "play") audio.play();
});
```

**CSP header** (required when `enableScripts: true`):

```typescript
const nonce = crypto.randomUUID();
const csp = `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'unsafe-inline';`;
return `<html><head><meta http-equiv="Content-Security-Policy" content="${csp}"></head>...`;
```

---

## Agent Hooks (1.111+)

Shell scripts at lifecycle points. Config: `.github/hooks.json`.

| Event | When | Use Cases |
|-------|------|-----------|
| `SessionStart` | Chat begins | Load context, set persona |
| `PreToolUse` | Before tool | Safety blocks, sanitization |
| `PostToolUse` | After tool | Logging, compile reminders |
| `Stop` | Session ends | Metrics, commit reminders |

```json
{
  "hooks": {
    "SessionStart": [{ "steps": [{ "hooks": [
      { "type": "command", "command": ".github/muscles/hooks/start.cjs", "timeout": 10 }
    ]}]}]
  }
}
```

**PreToolUse decisions**: `allow`, `deny` (exit 2), or `updatedInput` (modify params).

---

## Configuration Patterns

```typescript
// Safe config access with defaults
function getSetting<T>(key: string, fallback: T): T {
  return vscode.workspace.getConfiguration("myext").get<T>(key, fallback);
}

// Listen for changes
vscode.workspace.onDidChangeConfiguration(e => {
  if (e.affectsConfiguration("myext.feature")) refreshUI();
});
```

---

## SecretStorage for Tokens

**Never store secrets in settings** — use `context.secrets`:

```typescript
let cachedToken: string | null = null;

export async function initSecrets(ctx: vscode.ExtensionContext) {
  cachedToken = await ctx.secrets.get("myext.token") || null;
  // Migration from settings
  const old = vscode.workspace.getConfiguration("myext").get<string>("token");
  if (old && !cachedToken) {
    await ctx.secrets.store("myext.token", old);
    cachedToken = old;
  }
}

export function getToken(): string | null { return cachedToken; }
```

---

## Webview Sandbox

Webviews cannot access VS Code APIs directly. Communication via `postMessage`:

```typescript
// Extension
webview.postMessage({ type: "update", data });

// Webview
window.addEventListener("message", (e) => {
  if (e.data.type === "update") renderData(e.data.data);
});

// Webview → Extension
vscode.postMessage({ command: "save", payload });
```

---

## Telemetry Opt-Out

```typescript
if (vscode.env.isTelemetryEnabled) sendTelemetry(event);
```

Always check before sending. Respect user settings.

---

## Portability Rules

1. Use `vscode.Uri.joinPath()` — not string concatenation
2. Use `vscode.workspace.fs` — not Node `fs`
3. Use `path.posix.join()` for URI paths
4. Normalize paths: `uri.fsPath` for filesystem, `uri.toString()` for display

---

## Publishing Workflow

1. `npm run compile` — build
2. `npx vsce package --no-dependencies` — create VSIX
3. `npx vsce publish --packagePath <vsix>` — publish

**Pre-publish**: Verify VSCE_PAT not expired, all tests pass.

---

## Testing: Debug vs Rebuild

| Change | Method |
|--------|--------|
| TS logic | F5 (debug) |
| package.json (commands, settings) | Rebuild + reload |
| Webview HTML/CSS | Reload webview |
| activationEvents | Rebuild + restart |

---

## Asset Optimization

**Problem**: Large images inflate extension size.

```javascript
// Resize to max dimension
const sharp = require('sharp');
await sharp(input).resize(768, 768, { fit: 'inside' }).toFile(output);
```

the AI assistant extension: 553MB → 33MB (94% reduction).

---

## TDZ in Production Builds

**Problem**: esbuild minifies + hoists, causing `Cannot access 'X' before initialization`.

**Bad**:

```typescript
const handlers = { click: () => config.value };  // config hoisted below
const config = { value: 42 };
```

**Good**:

```typescript
const config = { value: 42 };  // Define before use
const handlers = { click: () => config.value };
```

---

## Agent Platform (1.109+)

| Setting | Purpose |
|---------|---------|
| `chat.agent.enabled` | Enable custom agents |
| `chat.agentSkillsLocations` | Auto-load skills |
| `chat.useAgentsMdFile` | Load AGENTS.md |
| `chat.hooks.enabled` | Lifecycle hooks |

**Agent file** (`.github/agents/my.agent.md`):

```yaml
---
name: "MyAgent"
description: "Specialized agent"
---
Instructions here.
```

**Chat participant**:

```typescript
vscode.chat.createChatParticipant("myext.agent", async (req, ctx, stream) => {
  stream.markdown("Hello!");
});
```

**Tool registration**:

```typescript
vscode.lm.registerTool("myext-search", {
  async invoke(opts) {
    return new vscode.LanguageModelToolResult([
      new vscode.LanguageModelTextPart(JSON.stringify(results))
    ]);
  }
});
```

---

## Integration Audit Checklist

| # | Category | Check |
|---|----------|-------|
| 1 | Activation | `activationEvents` match actual needs |
| 2 | Context | `subscriptions`, `secrets`, `globalState` |
| 3 | Disposables | All pushed to subscriptions |
| 4 | Commands | package.json matches registerCommand |
| 5 | Configuration | getConfiguration, onDidChangeConfiguration |
| 6 | Webview Security | CSP, nonce, enableScripts |
| 7 | LM/Chat | vscode.lm patterns, tool registration |
| 8 | Telemetry | isTelemetryEnabled respected |
| 9 | Error Handling | try/catch patterns |
| 10 | File System | vscode.workspace.fs vs Node fs |

**Scoring**: 45-50 Excellent, 40-44 Good, 35-39 Fair, <35 Needs Work

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.