@maccesar/aiskills 1.11.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +43 -8
  2. package/lib/cleanup.js +42 -0
  3. package/lib/commands/skills.js +110 -8
  4. package/lib/config.js +17 -12
  5. package/lib/installer.js +5 -3
  6. package/lib/platform.js +1 -1
  7. package/lib/symlink.js +45 -3
  8. package/lib/utils.js +41 -0
  9. package/package.json +1 -1
  10. package/skills/audit-codebase/SKILL.md +70 -0
  11. package/skills/audit-codebase/agents/openai.yaml +4 -0
  12. package/skills/audit-codebase/references/comprehensive-audit.md +220 -0
  13. package/skills/audit-codebase/references/report-format.md +119 -0
  14. package/skills/humaniza/SKILL.md +55 -4
  15. package/skills/humaniza/references/ai-patterns-es.md +40 -0
  16. package/skills/humaniza/references/checklist.md +9 -0
  17. package/skills/humaniza/references/examples.md +16 -0
  18. package/skills/humaniza/references/lexicon-es-mx.md +18 -0
  19. package/skills/humaniza/references/structures-es.md +132 -0
  20. package/skills/humaniza/scripts/check_ai_patterns.py +216 -0
  21. package/skills/refactoring-ui/SKILL.md +65 -29
  22. package/skills/refactoring-ui/references/05-motion.md +124 -0
  23. package/skills/refactoring-ui/references/06-dark-mode.md +117 -0
  24. package/skills/refactoring-ui/references/07-component-patterns.md +181 -0
  25. package/skills/stitch-showcase/SKILL.md +24 -232
  26. package/skills/stitch-showcase/references/07-theme-system.md +12 -0
  27. package/skills/stitch-showcase/references/08-type-detection.md +9 -1
  28. package/skills/stitch-showcase/references/10-component-standardization.md +25 -0
  29. package/skills/stitch-showcase/references/12-video-embedding.md +113 -0
  30. package/skills/stitch-showcase/references/13-language-detection.md +82 -0
  31. package/skills/stitch-showcase/references/14-troubleshooting-known-issues.md +122 -0
  32. package/skills/stitch-showcase/references/15-build-flags.md +71 -0
  33. package/skills/stitch-showcase/references/16-design-md-format.md +107 -0
  34. package/skills/stitch-showcase/references/index.html +25 -19
  35. package/skills/stitch-showcase/references/viewer.html +24 -12
  36. package/skills/stitch-showcase/scripts/__pycache__/build_showcase.cpython-314.pyc +0 -0
  37. package/skills/stitch-showcase/scripts/__pycache__/component_utils.cpython-314.pyc +0 -0
  38. package/skills/stitch-showcase/scripts/__pycache__/detect_components.cpython-314.pyc +0 -0
  39. package/skills/stitch-showcase/scripts/__pycache__/extract_catalog.cpython-314.pyc +0 -0
  40. package/skills/stitch-showcase/scripts/__pycache__/extract_text.cpython-314.pyc +0 -0
  41. package/skills/stitch-showcase/scripts/__pycache__/extract_zips.cpython-314.pyc +0 -0
  42. package/skills/stitch-showcase/scripts/__pycache__/parse_design_md.cpython-314.pyc +0 -0
  43. package/skills/stitch-showcase/scripts/__pycache__/slug_demangle.cpython-314.pyc +0 -0
  44. package/skills/stitch-showcase/scripts/build_showcase.py +150 -10
  45. package/skills/stitch-showcase/scripts/parse_design_md.py +145 -12
  46. package/skills/stitch-showcase/scripts/slug_demangle.py +209 -0
  47. package/skills/vscode-extension-dev/SKILL.md +90 -41
  48. package/skills/vscode-extension-dev/references/api-additional.md +168 -0
  49. package/skills/vscode-extension-dev/references/api-progress.md +55 -0
  50. package/skills/vscode-extension-dev/references/api-quickpick.md +75 -0
  51. package/skills/vscode-extension-dev/references/api-secretstorage.md +57 -0
  52. package/skills/vscode-extension-dev/references/api-statusbar.md +38 -0
  53. package/skills/vscode-extension-dev/references/api-treeview.md +78 -0
  54. package/skills/vscode-extension-dev/references/api-webview.md +149 -0
  55. package/skills/vscode-extension-dev/references/architecture.md +67 -0
  56. package/skills/vscode-extension-dev/references/debugger.md +179 -0
  57. package/skills/vscode-extension-dev/references/lsp.md +175 -0
  58. package/skills/vscode-extension-dev/references/notebooks.md +208 -0
  59. package/skills/vscode-extension-dev/references/testing.md +208 -0
  60. package/skills/vscode-extension-dev/references/api-patterns.md +0 -625
@@ -0,0 +1,75 @@
1
+ # QuickPick
2
+
3
+ Modal list with filtering, multi-select, and async items.
4
+
5
+ ## Simple QuickPick
6
+
7
+ ```typescript
8
+ const items: vscode.QuickPickItem[] = [
9
+ { label: 'Item 1', description: 'First item', detail: 'Additional details' },
10
+ { label: 'Item 2', description: 'Second item', picked: true },
11
+ { label: 'Item 3', description: 'Third item' },
12
+ ];
13
+
14
+ const selected = await vscode.window.showQuickPick(items, {
15
+ placeHolder: 'Select an item',
16
+ canPickMany: false,
17
+ matchOnDescription: true,
18
+ matchOnDetail: true,
19
+ });
20
+
21
+ if (selected) {
22
+ vscode.window.showInformationMessage(`Selected: ${selected.label}`);
23
+ }
24
+ ```
25
+
26
+ ## QuickPick with Async Loading
27
+
28
+ ```typescript
29
+ async function showAsyncQuickPick(): Promise<void> {
30
+ const qp = vscode.window.createQuickPick<vscode.QuickPickItem>();
31
+ qp.placeholder = 'Search items...';
32
+ qp.matchOnDescription = true;
33
+
34
+ // Debounced search
35
+ let searchTimer: ReturnType<typeof setTimeout> | undefined;
36
+ const controller = new AbortController();
37
+
38
+ qp.onDidChangeValue((value) => {
39
+ if (searchTimer) {
40
+ clearTimeout(searchTimer);
41
+ }
42
+ searchTimer = setTimeout(async () => {
43
+ qp.busy = true;
44
+ try {
45
+ const results = await fetchItems(value, controller.signal);
46
+ qp.items = results.map((r) => ({
47
+ label: r.name,
48
+ description: r.description,
49
+ }));
50
+ } catch (err) {
51
+ if (err instanceof Error && err.name !== 'AbortError') {
52
+ vscode.window.showErrorMessage(`Search failed: ${err.message}`);
53
+ }
54
+ } finally {
55
+ qp.busy = false;
56
+ }
57
+ }, 300);
58
+ });
59
+
60
+ qp.onDidAccept(() => {
61
+ const selected = qp.selectedItems[0];
62
+ if (selected) {
63
+ vscode.window.showInformationMessage(`Selected: ${selected.label}`);
64
+ }
65
+ qp.dispose();
66
+ });
67
+
68
+ qp.onDidHide(() => {
69
+ controller.abort();
70
+ qp.dispose();
71
+ });
72
+
73
+ qp.show();
74
+ }
75
+ ```
@@ -0,0 +1,57 @@
1
+ # SecretStorage
2
+
3
+ Secure credential storage using the OS keychain.
4
+
5
+ ```typescript
6
+ class CredentialManager {
7
+ private static readonly TOKEN_KEY = 'myExt.apiToken';
8
+
9
+ constructor(private readonly secrets: vscode.SecretStorage) {}
10
+
11
+ async getToken(): Promise<string | undefined> {
12
+ return this.secrets.get(CredentialManager.TOKEN_KEY);
13
+ }
14
+
15
+ async setToken(token: string): Promise<void> {
16
+ await this.secrets.store(CredentialManager.TOKEN_KEY, token);
17
+ }
18
+
19
+ async deleteToken(): Promise<void> {
20
+ await this.secrets.delete(CredentialManager.TOKEN_KEY);
21
+ }
22
+
23
+ onDidChange(callback: (e: vscode.SecretStorageChangeEvent) => void): vscode.Disposable {
24
+ return this.secrets.onDidChange(callback);
25
+ }
26
+ }
27
+
28
+ // In activate():
29
+ export function activate(context: vscode.ExtensionContext) {
30
+ const credentials = new CredentialManager(context.secrets);
31
+
32
+ context.subscriptions.push(
33
+ vscode.commands.registerCommand('myExt.login', async () => {
34
+ const token = await vscode.window.showInputBox({
35
+ prompt: 'Enter your API token',
36
+ password: true,
37
+ ignoreFocusOut: true,
38
+ });
39
+ if (token) {
40
+ await credentials.setToken(token);
41
+ vscode.window.showInformationMessage('Token saved securely.');
42
+ }
43
+ }),
44
+
45
+ vscode.commands.registerCommand('myExt.logout', async () => {
46
+ await credentials.deleteToken();
47
+ vscode.window.showInformationMessage('Token removed.');
48
+ }),
49
+
50
+ credentials.onDidChange((e) => {
51
+ if (e.key === 'myExt.apiToken') {
52
+ // React to credential change
53
+ }
54
+ }),
55
+ );
56
+ }
57
+ ```
@@ -0,0 +1,38 @@
1
+ # StatusBarItem
2
+
3
+ Persistent info in the status bar with click action.
4
+
5
+ ```typescript
6
+ export function activate(context: vscode.ExtensionContext) {
7
+ const statusBar = vscode.window.createStatusBarItem(
8
+ vscode.StatusBarAlignment.Right,
9
+ 100, // priority — higher = more to the left
10
+ );
11
+
12
+ statusBar.text = '$(sync) My Extension';
13
+ statusBar.tooltip = 'Click to refresh';
14
+ statusBar.command = 'myExt.refresh';
15
+ statusBar.backgroundColor = undefined; // use new vscode.ThemeColor('statusBarItem.warningBackground') for warnings
16
+ statusBar.show();
17
+
18
+ context.subscriptions.push(statusBar);
19
+
20
+ // Update dynamically
21
+ function updateStatus(count: number): void {
22
+ statusBar.text = `$(check) ${count} items`;
23
+ statusBar.tooltip = `${count} items synced`;
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## Codicon Icons in StatusBar
29
+
30
+ Use `$(icon-name)` syntax. Common icons:
31
+
32
+ - `$(sync~spin)` — spinning sync (loading)
33
+ - `$(check)` — checkmark
34
+ - `$(warning)` — warning triangle
35
+ - `$(error)` — error circle
36
+ - `$(info)` — info circle
37
+ - `$(cloud-upload)` — upload
38
+ - Full list: https://code.visualstudio.com/api/references/icons-in-labels
@@ -0,0 +1,78 @@
1
+ # TreeDataProvider
2
+
3
+ Provides data for a TreeView in the sidebar or panel.
4
+
5
+ ```typescript
6
+ import * as vscode from 'vscode';
7
+
8
+ interface TreeItem {
9
+ id: string;
10
+ label: string;
11
+ children?: TreeItem[];
12
+ }
13
+
14
+ class MyTreeProvider implements vscode.TreeDataProvider<TreeItem> {
15
+ private _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined>();
16
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
17
+
18
+ private items: TreeItem[] = [];
19
+
20
+ refresh(): void {
21
+ this._onDidChangeTreeData.fire(undefined);
22
+ }
23
+
24
+ getTreeItem(element: TreeItem): vscode.TreeItem {
25
+ const treeItem = new vscode.TreeItem(
26
+ element.label,
27
+ element.children?.length
28
+ ? vscode.TreeItemCollapsibleState.Collapsed
29
+ : vscode.TreeItemCollapsibleState.None
30
+ );
31
+ treeItem.id = element.id;
32
+ treeItem.contextValue = element.children ? 'parent' : 'leaf';
33
+ treeItem.iconPath = new vscode.ThemeIcon('symbol-file');
34
+ // Make leaf items clickable
35
+ if (!element.children) {
36
+ treeItem.command = {
37
+ command: 'myExt.openItem',
38
+ title: 'Open Item',
39
+ arguments: [element],
40
+ };
41
+ }
42
+ return treeItem;
43
+ }
44
+
45
+ getChildren(element?: TreeItem): TreeItem[] {
46
+ if (!element) {
47
+ return this.items;
48
+ }
49
+ return element.children ?? [];
50
+ }
51
+
52
+ setItems(items: TreeItem[]): void {
53
+ this.items = items;
54
+ this.refresh();
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Registering the TreeView
60
+
61
+ ```typescript
62
+ export function activate(context: vscode.ExtensionContext) {
63
+ const treeProvider = new MyTreeProvider();
64
+
65
+ const treeView = vscode.window.createTreeView('myTreeView', {
66
+ treeDataProvider: treeProvider,
67
+ showCollapseAll: true,
68
+ });
69
+
70
+ context.subscriptions.push(
71
+ treeView,
72
+ vscode.commands.registerCommand('myExt.refresh', () => treeProvider.refresh()),
73
+ vscode.commands.registerCommand('myExt.openItem', (item: TreeItem) => {
74
+ vscode.window.showInformationMessage(`Opened: ${item.label}`);
75
+ }),
76
+ );
77
+ }
78
+ ```
@@ -0,0 +1,149 @@
1
+ # Webview Panel
2
+
3
+ Full HTML rendering with CSP and bidirectional messaging.
4
+
5
+ ```typescript
6
+ import * as vscode from 'vscode';
7
+
8
+ class MyWebviewPanel {
9
+ public static readonly viewType = 'myExt.webview';
10
+ private readonly _panel: vscode.WebviewPanel;
11
+ private readonly _extensionUri: vscode.Uri;
12
+ private _disposables: vscode.Disposable[] = [];
13
+
14
+ public static create(extensionUri: vscode.Uri): MyWebviewPanel {
15
+ const panel = vscode.window.createWebviewPanel(
16
+ MyWebviewPanel.viewType,
17
+ 'My Panel',
18
+ vscode.ViewColumn.One,
19
+ {
20
+ enableScripts: true,
21
+ retainContextWhenHidden: false, // saves memory; set true if state is expensive
22
+ localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'media')],
23
+ },
24
+ );
25
+
26
+ return new MyWebviewPanel(panel, extensionUri);
27
+ }
28
+
29
+ private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
30
+ this._panel = panel;
31
+ this._extensionUri = extensionUri;
32
+
33
+ this._panel.webview.html = this._getHtml(this._panel.webview);
34
+
35
+ // Handle messages FROM the webview
36
+ this._panel.webview.onDidReceiveMessage(
37
+ (message: { command: string; data?: unknown }) => {
38
+ switch (message.command) {
39
+ case 'save':
40
+ this._handleSave(message.data);
41
+ return;
42
+ case 'requestData':
43
+ this._sendData();
44
+ return;
45
+ }
46
+ },
47
+ null,
48
+ this._disposables,
49
+ );
50
+
51
+ this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
52
+ }
53
+
54
+ /** Send data TO the webview */
55
+ public sendMessage(command: string, data: unknown): void {
56
+ this._panel.webview.postMessage({ command, data });
57
+ }
58
+
59
+ private _handleSave(data: unknown): void {
60
+ vscode.window.showInformationMessage('Data saved!');
61
+ }
62
+
63
+ private _sendData(): void {
64
+ this.sendMessage('loadData', { items: ['a', 'b', 'c'] });
65
+ }
66
+
67
+ private _getHtml(webview: vscode.Webview): string {
68
+ const styleUri = webview.asWebviewUri(
69
+ vscode.Uri.joinPath(this._extensionUri, 'media', 'style.css'),
70
+ );
71
+ const scriptUri = webview.asWebviewUri(
72
+ vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js'),
73
+ );
74
+ const nonce = getNonce();
75
+
76
+ return /*html*/ `<!DOCTYPE html>
77
+ <html lang="en">
78
+ <head>
79
+ <meta charset="UTF-8">
80
+ <meta http-equiv="Content-Security-Policy"
81
+ content="default-src 'none';
82
+ style-src ${webview.cspSource};
83
+ script-src 'nonce-${nonce}';
84
+ img-src ${webview.cspSource} https:;
85
+ font-src ${webview.cspSource};">
86
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
87
+ <link href="${styleUri}" rel="stylesheet">
88
+ <title>My Panel</title>
89
+ </head>
90
+ <body>
91
+ <div id="app"></div>
92
+ <script nonce="${nonce}" src="${scriptUri}"></script>
93
+ </body>
94
+ </html>`;
95
+ }
96
+
97
+ public dispose(): void {
98
+ this._panel.dispose();
99
+ while (this._disposables.length) {
100
+ const d = this._disposables.pop();
101
+ d?.dispose();
102
+ }
103
+ }
104
+ }
105
+
106
+ function getNonce(): string {
107
+ let text = '';
108
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
109
+ for (let i = 0; i < 32; i++) {
110
+ text += chars.charAt(Math.floor(Math.random() * chars.length));
111
+ }
112
+ return text;
113
+ }
114
+ ```
115
+
116
+ ## Webview Script (media/main.js)
117
+
118
+ ```javascript
119
+ // Inside the webview — communicates with the extension via postMessage
120
+ (function () {
121
+ // @ts-ignore
122
+ const vscode = acquireVsCodeApi();
123
+
124
+ // Send message TO the extension
125
+ function save(data) {
126
+ vscode.postMessage({ command: 'save', data });
127
+ }
128
+
129
+ // Receive messages FROM the extension
130
+ window.addEventListener('message', (event) => {
131
+ const message = event.data;
132
+ switch (message.command) {
133
+ case 'loadData':
134
+ renderData(message.data);
135
+ break;
136
+ }
137
+ });
138
+
139
+ function renderData(data) {
140
+ const app = document.getElementById('app');
141
+ if (app) {
142
+ app.textContent = JSON.stringify(data, null, 2);
143
+ }
144
+ }
145
+
146
+ // Request initial data
147
+ vscode.postMessage({ command: 'requestData' });
148
+ })();
149
+ ```
@@ -285,3 +285,70 @@ export function deactivate(): void {
285
285
  // Disposables in context.subscriptions are auto-disposed
286
286
  }
287
287
  ```
288
+
289
+ ## Extension Host Runtime
290
+
291
+ The extension host is a **single Node.js process shared by every extension** in the window. Two consequences shape the code you write:
292
+
293
+ - **Synchronous file I/O blocks the UI.** Calls like `fs.readFileSync` or `fs.writeFileSync` stall the host event loop, which freezes responses from every extension AND the editor's command handling. Always prefer async APIs:
294
+ - Node: `import { readFile } from 'node:fs/promises'` — `await readFile(path, 'utf8')`
295
+ - VS Code abstraction: `await vscode.workspace.fs.readFile(uri)` — works across remote, virtual, and local file systems
296
+ - **CPU-heavy work in the host blocks too.** For long parsing/analysis, move work to a worker thread (`node:worker_threads`) or a language server (LSP), not a tight loop on the host.
297
+ - The Webview runs in a separate process; messaging is async by construction. Don't try to "call" the host synchronously from the webview.
298
+
299
+ ```typescript
300
+ // BAD — blocks the extension host
301
+ import * as fs from 'node:fs';
302
+ const text = fs.readFileSync('/tmp/big-file.txt', 'utf8');
303
+
304
+ // GOOD — async, yields back to the event loop
305
+ import { readFile } from 'node:fs/promises';
306
+ const text = await readFile('/tmp/big-file.txt', 'utf8');
307
+
308
+ // GOOD — workspace-aware (preferred when reading user files)
309
+ const uri = vscode.Uri.file('/tmp/big-file.txt');
310
+ const bytes = await vscode.workspace.fs.readFile(uri);
311
+ const text2 = new TextDecoder('utf-8').decode(bytes);
312
+ ```
313
+
314
+ ## Workspace Folders (`rootPath` is deprecated)
315
+
316
+ `vscode.workspace.rootPath` is **deprecated** and returns only the *first* folder in multi-root workspaces, silently dropping the rest. Use `vscode.workspace.workspaceFolders` instead — it returns `readonly WorkspaceFolder[] | undefined`.
317
+
318
+ ```typescript
319
+ // BAD — deprecated, fails silently in multi-root workspaces
320
+ const root = vscode.workspace.rootPath;
321
+
322
+ // GOOD — iterate all workspace folders
323
+ const folders = vscode.workspace.workspaceFolders;
324
+ if (!folders || folders.length === 0) {
325
+ vscode.window.showWarningMessage('Open a folder first.');
326
+ return;
327
+ }
328
+ for (const folder of folders) {
329
+ // folder.uri.fsPath is the absolute path
330
+ // folder.name is the display name
331
+ // folder.index is its position in the list
332
+ }
333
+
334
+ // GOOD — find the folder that owns a given file
335
+ const fileUri = vscode.window.activeTextEditor?.document.uri;
336
+ if (fileUri) {
337
+ const owner = vscode.workspace.getWorkspaceFolder(fileUri);
338
+ // owner is the WorkspaceFolder containing the file, or undefined
339
+ }
340
+
341
+ // GOOD — react to folders being added or removed
342
+ context.subscriptions.push(
343
+ vscode.workspace.onDidChangeWorkspaceFolders((event) => {
344
+ for (const added of event.added) {
345
+ // ...
346
+ }
347
+ for (const removed of event.removed) {
348
+ // ...
349
+ }
350
+ }),
351
+ );
352
+ ```
353
+
354
+ Relative paths in extension code should resolve against a specific `WorkspaceFolder`, never against `process.cwd()` (which is unrelated to the user's workspace).
@@ -0,0 +1,179 @@
1
+ # Debugger Extensions (Debug Adapter Protocol)
2
+
3
+ Add a new debugger by implementing the **Debug Adapter Protocol** (DAP). The debug adapter is a separate process that translates between VS Code's generic debug UI and the language/runtime's actual debugging facilities.
4
+
5
+ Official guide: https://code.visualstudio.com/api/extension-guides/debugger-extension
6
+ Protocol spec: https://microsoft.github.io/debug-adapter-protocol/
7
+
8
+ ## Three Components
9
+
10
+ | Component | Job | Where |
11
+ |---|---|---|
12
+ | Debug adapter | Speaks DAP over stdio/socket, drives the underlying debugger | Separate process (any language) |
13
+ | `DebugAdapterDescriptorFactory` | Tells VS Code how to launch the adapter | In the extension |
14
+ | `DebugConfigurationProvider` | Resolves/validates `launch.json` entries | In the extension |
15
+
16
+ Plus declarations in `package.json` under `contributes.debuggers`.
17
+
18
+ ## package.json Manifest
19
+
20
+ ```json
21
+ "contributes": {
22
+ "debuggers": [
23
+ {
24
+ "type": "mydebug",
25
+ "label": "My Debugger",
26
+ "languages": ["mylang"],
27
+ "configurationAttributes": {
28
+ "launch": {
29
+ "required": ["program"],
30
+ "properties": {
31
+ "program": {
32
+ "type": "string",
33
+ "description": "Path to program to debug"
34
+ },
35
+ "stopOnEntry": {
36
+ "type": "boolean",
37
+ "default": true
38
+ }
39
+ }
40
+ }
41
+ },
42
+ "initialConfigurations": [
43
+ {
44
+ "type": "mydebug",
45
+ "request": "launch",
46
+ "name": "Launch program",
47
+ "program": "${workspaceFolder}/main.mylang",
48
+ "stopOnEntry": true
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ - `type` is the debugger identifier — referenced in user `launch.json`
57
+ - `configurationAttributes` powers IntelliSense inside `launch.json`
58
+ - `initialConfigurations` is what "Add Configuration" inserts
59
+
60
+ ## Descriptor Factory
61
+
62
+ Tells VS Code how to start the adapter process for a debug session.
63
+
64
+ ```typescript
65
+ import * as vscode from 'vscode';
66
+
67
+ class MyDebugAdapterDescriptorFactory
68
+ implements vscode.DebugAdapterDescriptorFactory {
69
+
70
+ createDebugAdapterDescriptor(
71
+ _session: vscode.DebugSession,
72
+ _executable: vscode.DebugAdapterExecutable | undefined,
73
+ ): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
74
+ // Option 1: launch a separate Node executable
75
+ return new vscode.DebugAdapterExecutable('node', [
76
+ this._extensionPath + '/dist/debug-adapter.js',
77
+ ]);
78
+
79
+ // Option 2: run the adapter inline in the extension host
80
+ // return new vscode.DebugAdapterInlineImplementation(new MyDebugSession());
81
+
82
+ // Option 3: connect to a server on a TCP port (for development)
83
+ // return new vscode.DebugAdapterServer(4711);
84
+ }
85
+
86
+ constructor(private readonly _extensionPath: string) {}
87
+ }
88
+
89
+ export function activate(context: vscode.ExtensionContext) {
90
+ context.subscriptions.push(
91
+ vscode.debug.registerDebugAdapterDescriptorFactory(
92
+ 'mydebug',
93
+ new MyDebugAdapterDescriptorFactory(context.extensionPath),
94
+ ),
95
+ );
96
+ }
97
+ ```
98
+
99
+ **Choose the right form**:
100
+
101
+ - `DebugAdapterExecutable` — production. Adapter is a separate Node script.
102
+ - `DebugAdapterInlineImplementation` — testing, or very simple debuggers. Runs in extension host (so it shares the event loop — beware blocking).
103
+ - `DebugAdapterServer` — development convenience. Run the adapter in a debugger yourself, point VS Code at the port.
104
+
105
+ ## Configuration Provider
106
+
107
+ Resolves user `launch.json` BEFORE the session starts. Useful for filling in defaults, validating, or generating configs dynamically.
108
+
109
+ ```typescript
110
+ class MyDebugConfigurationProvider
111
+ implements vscode.DebugConfigurationProvider {
112
+
113
+ resolveDebugConfiguration(
114
+ folder: vscode.WorkspaceFolder | undefined,
115
+ config: vscode.DebugConfiguration,
116
+ _token?: vscode.CancellationToken,
117
+ ): vscode.ProviderResult<vscode.DebugConfiguration> {
118
+ // Called when "Debug" is hit with no launch.json — supply a default
119
+ if (!config.type && !config.request && !config.name) {
120
+ const editor = vscode.window.activeTextEditor;
121
+ if (editor && editor.document.languageId === 'mylang') {
122
+ config.type = 'mydebug';
123
+ config.request = 'launch';
124
+ config.name = 'Launch';
125
+ config.program = '${file}';
126
+ }
127
+ }
128
+
129
+ if (!config.program) {
130
+ return vscode.window
131
+ .showInformationMessage('Cannot find a program to debug')
132
+ .then(() => undefined);
133
+ }
134
+ return config;
135
+ }
136
+ }
137
+
138
+ context.subscriptions.push(
139
+ vscode.debug.registerDebugConfigurationProvider(
140
+ 'mydebug',
141
+ new MyDebugConfigurationProvider(),
142
+ ),
143
+ );
144
+ ```
145
+
146
+ ## DAP — What the Adapter Has to Implement
147
+
148
+ Minimum lifecycle:
149
+
150
+ | Request | Response | When |
151
+ |---|---|---|
152
+ | `initialize` | capabilities (e.g., `supportsConfigurationDoneRequest`) | Session start |
153
+ | `launch` or `attach` | empty success | After initialize |
154
+ | `setBreakpoints` | array of verified breakpoints | When breakpoints change |
155
+ | `configurationDone` | empty success | After initial setup |
156
+ | `threads` | list of thread ids/names | UI refresh |
157
+ | `stackTrace` | call frames | Thread is stopped |
158
+ | `scopes` | scopes per frame | Frame selected |
159
+ | `variables` | variables in a scope | Scope expanded |
160
+ | `continue` / `next` / `stepIn` / `stepOut` | empty success | User clicks step button |
161
+ | `disconnect` | empty success | Session ends |
162
+
163
+ Events the adapter must SEND to the UI:
164
+
165
+ - `initialized` (after responding to `initialize`)
166
+ - `stopped` (with reason: `breakpoint`, `step`, `exception`, etc.)
167
+ - `terminated` (debugging finished)
168
+ - `output` (console output for the Debug Console)
169
+ - `thread` (thread started/exited)
170
+
171
+ Use the `vscode-debugadapter` npm package to handle the protocol plumbing — implement just the verbs you support.
172
+
173
+ ## Anti-Patterns
174
+
175
+ - ❌ Sending `stopped` without a previous `initialized` event — UI shows nothing
176
+ - ❌ Returning ad-hoc thread/frame ids — must be stable integers within the session
177
+ - ❌ Sending `output` events without a `category` (`stdout` | `stderr` | `console` | `important`)
178
+ - ❌ Long-running synchronous work in `DebugAdapterInlineImplementation` — blocks the extension host
179
+ - ❌ Forgetting to send `terminated` — session never closes and "Stop" hangs