用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/fabioc-aloha/Alex_Plug_In --skill vscode-extension-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Create and maintain ASCII visual dashboards for project tracking with parallel lane progress bars
Store and manage voice samples for TTS cloning — portable, version-controlled audio references
Clear documentation through visual excellence
正在显示 SKILL.md
基于 SOC 职业分类
| name | vscode-extension-patterns |
| description | Reusable patterns for VS Code extension development. |
| tier | core |
| applyTo | **/extension.ts,**/src/**/*.ts,**/*.vsix |
Reusable patterns for VS Code extension development.
Last validated: March 2026 (VS Code 1.111+)
const [health, knowledge] = await Promise.all([checkHealth(), getKnowledgeSummary()]);
panel.webview.html = await getWebviewContent(health, knowledge);
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());
Problem: Inline onclick handlers violate CSP.
Solution: Use data-cmd with delegated listener:
<button data-cmd="play">Play</button>
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):
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>...`;
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 |
{
"hooks": {
"SessionStart": [{ "steps": [{ "hooks": [
{ "type": "command", "command": ".github/muscles/hooks/start.cjs", "timeout": 10 }
]}]}]
}
}
PreToolUse decisions: allow, deny (exit 2), or updatedInput (modify params).
// 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();
});
Never store secrets in settings — use context.secrets:
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; }
Webviews cannot access VS Code APIs directly. Communication via postMessage:
// 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 });
if (vscode.env.isTelemetryEnabled) sendTelemetry(event);
Always check before sending. Respect user settings.
vscode.Uri.joinPath() — not string concatenationvscode.workspace.fs — not Node fspath.posix.join() for URI pathsuri.fsPath for filesystem, uri.toString() for displaynpm run compile — buildnpx vsce package --no-dependencies — create VSIXnpx vsce publish --packagePath <vsix> — publishPre-publish: Verify VSCE_PAT not expired, all tests pass.
| Change | Method |
|---|---|
| TS logic | F5 (debug) |
| package.json (commands, settings) | Rebuild + reload |
| Webview HTML/CSS | Reload webview |
| activationEvents | Rebuild + restart |
Problem: Large images inflate extension size.
// Resize to max dimension
const sharp = require('sharp');
await sharp(input).resize(768, 768, { fit: 'inside' }).toFile(output);
Alex extension: 553MB → 33MB (94% reduction).
Problem: esbuild minifies + hoists, causing Cannot access 'X' before initialization.
Bad:
const handlers = { click: () => config.value }; // config hoisted below
const config = { value: 42 };
Good:
const config = { value: 42 }; // Define before use
const handlers = { click: () => config.value };
| Setting | Purpose |
|---|---|
chat.agent.enabled | Enable custom agents |
chat.agentSkillsLocations | Auto-load skills |
chat.useAgentsMdFile | Load AGENTS.md |
chat.hooks.enabled | Lifecycle hooks |
chat.autopilot.enabled | Autopilot mode |
Agent file (.github/agents/my.agent.md):
---
name: "MyAgent"
description: "Specialized agent"
---
Instructions here.
Chat participant:
vscode.chat.createChatParticipant("myext.agent", async (req, ctx, stream) => {
stream.markdown("Hello!");
});
Tool registration:
vscode.lm.registerTool("myext-search", {
async invoke(opts) {
return new vscode.LanguageModelToolResult([
new vscode.LanguageModelTextPart(JSON.stringify(results))
]);
}
});
| # | 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