@maccesar/aiskills 1.7.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 (73) hide show
  1. package/README.md +531 -0
  2. package/bin/aiskills.js +76 -0
  3. package/lib/cache.js +49 -0
  4. package/lib/cleanup.js +77 -0
  5. package/lib/commands/auto-update.js +131 -0
  6. package/lib/commands/doctor.js +139 -0
  7. package/lib/commands/list.js +77 -0
  8. package/lib/commands/skills.js +263 -0
  9. package/lib/commands/status.js +94 -0
  10. package/lib/commands/uninstall.js +182 -0
  11. package/lib/commands/update.js +149 -0
  12. package/lib/config.js +90 -0
  13. package/lib/downloader.js +110 -0
  14. package/lib/hooks.js +74 -0
  15. package/lib/installer.js +114 -0
  16. package/lib/platform.js +112 -0
  17. package/lib/prompts/checkboxCancel.js +264 -0
  18. package/lib/prompts/selectCancel.js +204 -0
  19. package/lib/symlink.js +154 -0
  20. package/lib/utils.js +49 -0
  21. package/package.json +61 -0
  22. package/skills/humaniza/SKILL.md +51 -0
  23. package/skills/humaniza/agents/openai.yaml +4 -0
  24. package/skills/humaniza/references/ai-patterns-es.md +51 -0
  25. package/skills/humaniza/references/checklist.md +9 -0
  26. package/skills/humaniza/references/examples.md +17 -0
  27. package/skills/humaniza/references/lexicon-es-mx.md +36 -0
  28. package/skills/humaniza/references/modes-es-mx.md +41 -0
  29. package/skills/humaniza/references/voice-es-mx.md +24 -0
  30. package/skills/refactoring-ui/SKILL.md +59 -0
  31. package/skills/refactoring-ui/references/01-design-process.md +72 -0
  32. package/skills/refactoring-ui/references/02-visual-hierarchy.md +84 -0
  33. package/skills/refactoring-ui/references/03-layout-spacing.md +69 -0
  34. package/skills/refactoring-ui/references/04-typography.md +70 -0
  35. package/skills/refactoring-ui/references/05-color.md +96 -0
  36. package/skills/refactoring-ui/references/06-depth-shadows.md +74 -0
  37. package/skills/refactoring-ui/references/07-images.md +75 -0
  38. package/skills/refactoring-ui/references/08-finishing-touches.md +91 -0
  39. package/skills/stitch-showcase/SKILL.md +411 -0
  40. package/skills/stitch-showcase/references/01-navbar.md +52 -0
  41. package/skills/stitch-showcase/references/02-hero.md +56 -0
  42. package/skills/stitch-showcase/references/03-design-system.md +102 -0
  43. package/skills/stitch-showcase/references/04-screen-gallery.md +102 -0
  44. package/skills/stitch-showcase/references/05-viewer-web.md +105 -0
  45. package/skills/stitch-showcase/references/06-viewer-mobile.md +104 -0
  46. package/skills/stitch-showcase/references/07-theme-system.md +77 -0
  47. package/skills/stitch-showcase/references/08-type-detection.md +81 -0
  48. package/skills/stitch-showcase/references/09-quality-standards.md +126 -0
  49. package/skills/stitch-showcase/references/10-component-standardization.md +40 -0
  50. package/skills/stitch-showcase/references/11-component-catalog.md +70 -0
  51. package/skills/stitch-showcase/references/catalog-template.html +841 -0
  52. package/skills/stitch-showcase/references/index.html +299 -0
  53. package/skills/stitch-showcase/references/viewer.html +412 -0
  54. package/skills/stitch-showcase/scripts/__pycache__/build_showcase.cpython-314.pyc +0 -0
  55. package/skills/stitch-showcase/scripts/__pycache__/component_utils.cpython-313.pyc +0 -0
  56. package/skills/stitch-showcase/scripts/__pycache__/detect_components.cpython-313.pyc +0 -0
  57. package/skills/stitch-showcase/scripts/__pycache__/extract_catalog.cpython-313.pyc +0 -0
  58. package/skills/stitch-showcase/scripts/__pycache__/extract_text.cpython-313.pyc +0 -0
  59. package/skills/stitch-showcase/scripts/__pycache__/extract_zips.cpython-313.pyc +0 -0
  60. package/skills/stitch-showcase/scripts/__pycache__/parse_design_md.cpython-313.pyc +0 -0
  61. package/skills/stitch-showcase/scripts/apply_canonical.py +238 -0
  62. package/skills/stitch-showcase/scripts/build_showcase.py +2103 -0
  63. package/skills/stitch-showcase/scripts/component_utils.py +398 -0
  64. package/skills/stitch-showcase/scripts/detect_components.py +284 -0
  65. package/skills/stitch-showcase/scripts/extract_catalog.py +913 -0
  66. package/skills/stitch-showcase/scripts/extract_text.py +268 -0
  67. package/skills/stitch-showcase/scripts/extract_zips.py +178 -0
  68. package/skills/stitch-showcase/scripts/parse_design_md.py +397 -0
  69. package/skills/vscode-extension-dev/SKILL.md +114 -0
  70. package/skills/vscode-extension-dev/references/api-patterns.md +625 -0
  71. package/skills/vscode-extension-dev/references/architecture.md +287 -0
  72. package/skills/vscode-extension-dev/references/package-json-schema.md +345 -0
  73. package/skills/vscode-extension-dev/references/publishing.md +251 -0
@@ -0,0 +1,287 @@
1
+ # Extension Architecture
2
+
3
+ Layered architecture, project structure, and testing strategy for VS Code extensions.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ my-extension/
9
+ ├── .vscode/
10
+ │ ├── extensions.json # Recommended extensions for contributors
11
+ │ ├── launch.json # Debug configurations
12
+ │ ├── settings.json # Workspace settings
13
+ │ └── tasks.json # Build tasks
14
+ ├── src/
15
+ │ ├── extension.ts # Entry point: activate() and deactivate()
16
+ │ ├── commands/ # Command handlers
17
+ │ │ ├── index.ts # Re-exports all commands
18
+ │ │ └── runAnalysis.ts # Individual command
19
+ │ ├── providers/ # VS Code API providers
20
+ │ │ ├── treeProvider.ts # TreeDataProvider
21
+ │ │ ├── webviewProvider.ts # Webview panel manager
22
+ │ │ └── contentProvider.ts # TextDocumentContentProvider
23
+ │ ├── services/ # Business logic (no vscode imports)
24
+ │ │ ├── apiClient.ts # External API communication
25
+ │ │ └── dataProcessor.ts # Data transformation
26
+ │ ├── models/ # TypeScript interfaces and types
27
+ │ │ └── types.ts
28
+ │ └── utils/ # Shared utilities
29
+ │ └── logger.ts
30
+ ├── media/ # Webview assets
31
+ │ ├── style.css
32
+ │ └── main.js
33
+ ├── resources/ # Icons and images
34
+ │ ├── dark/
35
+ │ │ └── icon.svg
36
+ │ └── light/
37
+ │ └── icon.svg
38
+ ├── test/
39
+ │ ├── unit/ # Unit tests (no VS Code dependency)
40
+ │ │ └── dataProcessor.test.ts
41
+ │ └── integration/ # Integration tests (require VS Code)
42
+ │ └── extension.test.ts
43
+ ├── .vscodeignore # Files to exclude from VSIX package
44
+ ├── esbuild.js # Bundler configuration
45
+ ├── package.json
46
+ ├── tsconfig.json
47
+ ├── CHANGELOG.md
48
+ └── README.md
49
+ ```
50
+
51
+ ## Layered Architecture
52
+
53
+ ```
54
+ ┌─────────────────────────────────────────────┐
55
+ │ VS Code Layer │
56
+ │ extension.ts, commands/, providers/ │
57
+ │ Imports from 'vscode'. Registers commands, │
58
+ │ creates UI, manages disposables. │
59
+ ├─────────────────────────────────────────────┤
60
+ │ Business Logic Layer │
61
+ │ services/ │
62
+ │ Pure TypeScript. NO 'vscode' imports. │
63
+ │ Testable without VS Code runtime. │
64
+ ├─────────────────────────────────────────────┤
65
+ │ Data / API Layer │
66
+ │ services/apiClient.ts, models/ │
67
+ │ HTTP clients, data models, storage. │
68
+ │ Also pure TypeScript. │
69
+ └─────────────────────────────────────────────┘
70
+ ```
71
+
72
+ ### Why This Layering Matters
73
+
74
+ - **Business logic** in `services/` has zero `vscode` imports — it can be unit tested with plain Mocha/Jest, no Extension Host needed
75
+ - **VS Code layer** is thin — it wires providers/commands to services
76
+ - **Data layer** is injectable — swap real API for mock in tests
77
+
78
+ ### Example: Keeping Layers Separate
79
+
80
+ ```typescript
81
+ // services/dataProcessor.ts — NO vscode imports
82
+ export interface ProcessResult {
83
+ total: number;
84
+ errors: string[];
85
+ }
86
+
87
+ export function processData(items: string[]): ProcessResult {
88
+ const errors: string[] = [];
89
+ for (const item of items) {
90
+ if (!item.trim()) {
91
+ errors.push('Empty item found');
92
+ }
93
+ }
94
+ return { total: items.length, errors };
95
+ }
96
+ ```
97
+
98
+ ```typescript
99
+ // commands/runAnalysis.ts — thin VS Code wrapper
100
+ import * as vscode from 'vscode';
101
+ import { processData } from '../services/dataProcessor';
102
+
103
+ export async function runAnalysis(): Promise<void> {
104
+ const editor = vscode.window.activeTextEditor;
105
+ if (!editor) {
106
+ vscode.window.showWarningMessage('No active editor');
107
+ return;
108
+ }
109
+
110
+ const text = editor.document.getText();
111
+ const lines = text.split('\n');
112
+ const result = processData(lines);
113
+
114
+ if (result.errors.length > 0) {
115
+ vscode.window.showWarningMessage(`Found ${result.errors.length} issues`);
116
+ } else {
117
+ vscode.window.showInformationMessage(`Processed ${result.total} lines`);
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Debug Configuration
123
+
124
+ `.vscode/launch.json`:
125
+
126
+ ```json
127
+ {
128
+ "version": "0.2.0",
129
+ "configurations": [
130
+ {
131
+ "name": "Run Extension",
132
+ "type": "extensionHost",
133
+ "request": "launch",
134
+ "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
135
+ "outFiles": ["${workspaceFolder}/dist/**/*.js"],
136
+ "preLaunchTask": "npm: compile"
137
+ },
138
+ {
139
+ "name": "Extension Tests",
140
+ "type": "extensionHost",
141
+ "request": "launch",
142
+ "args": [
143
+ "--extensionDevelopmentPath=${workspaceFolder}",
144
+ "--extensionTestsPath=${workspaceFolder}/out/test/integration"
145
+ ],
146
+ "outFiles": ["${workspaceFolder}/out/**/*.js"],
147
+ "preLaunchTask": "npm: compile"
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ ## Testing Strategy
154
+
155
+ ### Unit Tests (Business Logic)
156
+
157
+ Test `services/` with plain Mocha — no VS Code runtime needed.
158
+
159
+ ```typescript
160
+ // test/unit/dataProcessor.test.ts
161
+ import * as assert from 'assert';
162
+ import { processData } from '../../src/services/dataProcessor';
163
+
164
+ suite('dataProcessor', () => {
165
+ test('counts items correctly', () => {
166
+ const result = processData(['a', 'b', 'c']);
167
+ assert.strictEqual(result.total, 3);
168
+ assert.strictEqual(result.errors.length, 0);
169
+ });
170
+
171
+ test('reports empty items', () => {
172
+ const result = processData(['a', '', 'c']);
173
+ assert.strictEqual(result.errors.length, 1);
174
+ });
175
+
176
+ test('handles empty input', () => {
177
+ const result = processData([]);
178
+ assert.strictEqual(result.total, 0);
179
+ });
180
+ });
181
+ ```
182
+
183
+ ### Integration Tests (VS Code API)
184
+
185
+ Test commands, providers, and UI interactions with `@vscode/test-electron`.
186
+
187
+ ```typescript
188
+ // test/integration/extension.test.ts
189
+ import * as assert from 'assert';
190
+ import * as vscode from 'vscode';
191
+
192
+ suite('Extension Integration', () => {
193
+ suiteSetup(async () => {
194
+ // Wait for extension to activate
195
+ const ext = vscode.extensions.getExtension('publisher.my-extension');
196
+ await ext?.activate();
197
+ });
198
+
199
+ test('extension activates', () => {
200
+ const ext = vscode.extensions.getExtension('publisher.my-extension');
201
+ assert.ok(ext?.isActive);
202
+ });
203
+
204
+ test('command registers successfully', async () => {
205
+ const commands = await vscode.commands.getCommands(true);
206
+ assert.ok(commands.includes('myExt.run'));
207
+ });
208
+
209
+ test('command executes without error', async () => {
210
+ // Open a test document first
211
+ const doc = await vscode.workspace.openTextDocument({
212
+ content: 'line1\nline2\nline3',
213
+ language: 'plaintext',
214
+ });
215
+ await vscode.window.showTextDocument(doc);
216
+
217
+ // Execute command — should not throw
218
+ await vscode.commands.executeCommand('myExt.run');
219
+ });
220
+ });
221
+ ```
222
+
223
+ ### Test Runner Configuration
224
+
225
+ `.vscode-test.mjs`:
226
+
227
+ ```javascript
228
+ import { defineConfig } from '@vscode/test-cli';
229
+
230
+ export default defineConfig({
231
+ files: 'out/test/integration/**/*.test.js',
232
+ mocha: {
233
+ timeout: 20000,
234
+ ui: 'tdd',
235
+ },
236
+ });
237
+ ```
238
+
239
+ `tsconfig.json` for tests (if separate):
240
+
241
+ ```json
242
+ {
243
+ "compilerOptions": {
244
+ "module": "commonjs",
245
+ "target": "ES2022",
246
+ "lib": ["ES2022"],
247
+ "outDir": "out",
248
+ "rootDir": ".",
249
+ "strict": true,
250
+ "esModuleInterop": true,
251
+ "skipLibCheck": true
252
+ },
253
+ "include": ["src/**/*.ts", "test/**/*.ts"]
254
+ }
255
+ ```
256
+
257
+ ## Extension Entry Point Pattern
258
+
259
+ ```typescript
260
+ // src/extension.ts
261
+ import * as vscode from 'vscode';
262
+ import { runAnalysis } from './commands/runAnalysis';
263
+ import { MyTreeProvider } from './providers/treeProvider';
264
+
265
+ export function activate(context: vscode.ExtensionContext) {
266
+ const output = vscode.window.createOutputChannel('My Extension', { log: true });
267
+ output.info('Activating...');
268
+
269
+ // Initialize providers
270
+ const treeProvider = new MyTreeProvider();
271
+
272
+ // Register everything and push to subscriptions
273
+ context.subscriptions.push(
274
+ output,
275
+ vscode.window.createTreeView('myTreeView', { treeDataProvider: treeProvider }),
276
+ vscode.commands.registerCommand('myExt.run', runAnalysis),
277
+ vscode.commands.registerCommand('myExt.refresh', () => treeProvider.refresh()),
278
+ );
279
+
280
+ output.info('Activated');
281
+ }
282
+
283
+ export function deactivate(): void {
284
+ // Only for async cleanup (network connections, child processes)
285
+ // Disposables in context.subscriptions are auto-disposed
286
+ }
287
+ ```
@@ -0,0 +1,345 @@
1
+ # package.json Schema Reference
2
+
3
+ Complete reference for VS Code extension `package.json` configuration.
4
+
5
+ ## Minimal Valid Extension
6
+
7
+ ```json
8
+ {
9
+ "name": "my-extension",
10
+ "displayName": "My Extension",
11
+ "description": "What it does",
12
+ "version": "0.0.1",
13
+ "publisher": "your-publisher-id",
14
+ "engines": {
15
+ "vscode": "^1.85.0"
16
+ },
17
+ "categories": ["Other"],
18
+ "main": "./dist/extension.js",
19
+ "activationEvents": [],
20
+ "contributes": {
21
+ "commands": [
22
+ {
23
+ "command": "myExtension.helloWorld",
24
+ "title": "Hello World",
25
+ "category": "My Extension"
26
+ }
27
+ ]
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## engines.vscode
33
+
34
+ Specifies the minimum VS Code version your extension supports.
35
+
36
+ ```json
37
+ "engines": {
38
+ "vscode": "^1.85.0"
39
+ }
40
+ ```
41
+
42
+ - Use `^` prefix for "this version or newer"
43
+ - Check API availability at https://code.visualstudio.com/api/references/vscode-api
44
+ - Common milestones: 1.74 (implicit activation), 1.82 (SecretStorage improvements)
45
+
46
+ ## activationEvents
47
+
48
+ Controls **when** your extension loads. Less is better — lazy activation improves startup.
49
+
50
+ ```json
51
+ "activationEvents": [
52
+ "onLanguage:python",
53
+ "onCommand:myExtension.doSomething",
54
+ "onView:myTreeView",
55
+ "onUri",
56
+ "onFileSystem:myScheme",
57
+ "onWebviewPanel:myPanel",
58
+ "workspaceContains:**/tsconfig.json",
59
+ "onStartupFinished",
60
+ "*"
61
+ ]
62
+ ```
63
+
64
+ ### Implicit Activation (VS Code 1.74+)
65
+ Commands declared in `contributes.commands` **automatically** generate `onCommand:` activation events. You do NOT need to list them in `activationEvents`.
66
+
67
+ ```json
68
+ // This is enough — no activationEvents entry needed for this command
69
+ "contributes": {
70
+ "commands": [
71
+ { "command": "myExt.run", "title": "Run" }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ ### Event Reference
77
+
78
+ | Event | Fires when |
79
+ | ---------------------------- | ------------------------------------------- |
80
+ | `onLanguage:langId` | File of that language is opened |
81
+ | `onCommand:commandId` | Command is invoked (implicit since 1.74) |
82
+ | `onView:viewId` | TreeView with that ID is expanded |
83
+ | `onUri` | Extension URI is opened (deep linking) |
84
+ | `onFileSystem:scheme` | File from that scheme is read |
85
+ | `onWebviewPanel:type` | Webview panel of that type is restored |
86
+ | `workspaceContains:pattern` | Workspace contains matching file |
87
+ | `onStartupFinished` | VS Code has fully started |
88
+ | `*` | On VS Code start — **avoid in production** |
89
+
90
+ ## contributes
91
+
92
+ ### commands
93
+
94
+ ```json
95
+ "contributes": {
96
+ "commands": [
97
+ {
98
+ "command": "myExt.refresh",
99
+ "title": "Refresh",
100
+ "category": "My Extension",
101
+ "icon": {
102
+ "light": "resources/light/refresh.svg",
103
+ "dark": "resources/dark/refresh.svg"
104
+ },
105
+ "enablement": "myExt.isConnected"
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ - `category` groups commands in the Command Palette: "My Extension: Refresh"
112
+ - `icon` is used when the command appears in menus/toolbars
113
+ - `enablement` uses when-clause context for conditional availability
114
+
115
+ ### menus
116
+
117
+ ```json
118
+ "contributes": {
119
+ "menus": {
120
+ "view/title": [
121
+ {
122
+ "command": "myExt.refresh",
123
+ "when": "view == myTreeView",
124
+ "group": "navigation"
125
+ }
126
+ ],
127
+ "view/item/context": [
128
+ {
129
+ "command": "myExt.deleteItem",
130
+ "when": "view == myTreeView && viewItem == deletable",
131
+ "group": "inline"
132
+ }
133
+ ],
134
+ "editor/context": [
135
+ {
136
+ "command": "myExt.formatSelection",
137
+ "when": "editorHasSelection",
138
+ "group": "1_modification"
139
+ }
140
+ ],
141
+ "commandPalette": [
142
+ {
143
+ "command": "myExt.deleteItem",
144
+ "when": "false"
145
+ }
146
+ ]
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Menu locations
152
+
153
+ | Location | Where it appears |
154
+ | --------------------- | --------------------------------------- |
155
+ | `commandPalette` | Command Palette (Ctrl+Shift+P) |
156
+ | `editor/context` | Editor right-click menu |
157
+ | `editor/title` | Editor title bar |
158
+ | `view/title` | View title bar (tree view header) |
159
+ | `view/item/context` | Tree view item right-click |
160
+ | `explorer/context` | File explorer right-click |
161
+ | `scm/title` | Source control title bar |
162
+
163
+ ### keybindings
164
+
165
+ ```json
166
+ "contributes": {
167
+ "keybindings": [
168
+ {
169
+ "command": "myExt.run",
170
+ "key": "ctrl+shift+r",
171
+ "mac": "cmd+shift+r",
172
+ "when": "editorTextFocus && editorLangId == typescript"
173
+ }
174
+ ]
175
+ }
176
+ ```
177
+
178
+ ### configuration
179
+
180
+ ```json
181
+ "contributes": {
182
+ "configuration": {
183
+ "title": "My Extension",
184
+ "properties": {
185
+ "myExt.apiUrl": {
186
+ "type": "string",
187
+ "default": "https://api.example.com",
188
+ "description": "API endpoint URL",
189
+ "format": "uri"
190
+ },
191
+ "myExt.maxResults": {
192
+ "type": "number",
193
+ "default": 50,
194
+ "minimum": 1,
195
+ "maximum": 500,
196
+ "description": "Maximum number of results to display"
197
+ },
198
+ "myExt.logLevel": {
199
+ "type": "string",
200
+ "default": "info",
201
+ "enum": ["debug", "info", "warn", "error"],
202
+ "enumDescriptions": [
203
+ "Verbose debug logging",
204
+ "Standard information",
205
+ "Warnings only",
206
+ "Errors only"
207
+ ],
208
+ "description": "Logging verbosity level"
209
+ },
210
+ "myExt.features.autoRefresh": {
211
+ "type": "boolean",
212
+ "default": true,
213
+ "description": "Automatically refresh data on file save"
214
+ }
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ Reading configuration in TypeScript:
221
+
222
+ ```typescript
223
+ const config = vscode.workspace.getConfiguration('myExt');
224
+ const apiUrl = config.get<string>('apiUrl', 'https://api.example.com');
225
+ const maxResults = config.get<number>('maxResults', 50);
226
+
227
+ // Listen for changes
228
+ vscode.workspace.onDidChangeConfiguration(e => {
229
+ if (e.affectsConfiguration('myExt.apiUrl')) {
230
+ // Re-initialize API client
231
+ }
232
+ });
233
+ ```
234
+
235
+ ### views and viewsContainers
236
+
237
+ ```json
238
+ "contributes": {
239
+ "viewsContainers": {
240
+ "activitybar": [
241
+ {
242
+ "id": "myExtExplorer",
243
+ "title": "My Extension",
244
+ "icon": "resources/icon.svg"
245
+ }
246
+ ]
247
+ },
248
+ "views": {
249
+ "myExtExplorer": [
250
+ {
251
+ "id": "myTreeView",
252
+ "name": "Items",
253
+ "icon": "resources/items.svg",
254
+ "contextualTitle": "My Extension Items"
255
+ },
256
+ {
257
+ "id": "myFavoritesView",
258
+ "name": "Favorites",
259
+ "visibility": "collapsed"
260
+ }
261
+ ]
262
+ },
263
+ "viewsWelcome": [
264
+ {
265
+ "view": "myTreeView",
266
+ "contents": "No items found.\n[Add Item](command:myExt.addItem)"
267
+ }
268
+ ]
269
+ }
270
+ ```
271
+
272
+ ## scripts (esbuild)
273
+
274
+ ```json
275
+ "scripts": {
276
+ "vscode:prepublish": "npm run package",
277
+ "compile": "npm run check-types && node esbuild.js",
278
+ "check-types": "tsc --noEmit",
279
+ "watch": "node esbuild.js --watch",
280
+ "package": "npm run check-types && node esbuild.js --production",
281
+ "lint": "eslint src",
282
+ "test": "vscode-test",
283
+ "deploy": "vsce publish"
284
+ }
285
+ ```
286
+
287
+ ### esbuild.js
288
+
289
+ ```javascript
290
+ const esbuild = require('esbuild');
291
+
292
+ const production = process.argv.includes('--production');
293
+ const watch = process.argv.includes('--watch');
294
+
295
+ async function main() {
296
+ const ctx = await esbuild.context({
297
+ entryPoints: ['src/extension.ts'],
298
+ bundle: true,
299
+ format: 'cjs',
300
+ minify: production,
301
+ sourcemap: !production,
302
+ sourcesContent: false,
303
+ platform: 'node',
304
+ outfile: 'dist/extension.js',
305
+ external: ['vscode'],
306
+ logLevel: 'silent',
307
+ plugins: [
308
+ /* add esbuild problem matcher plugin if needed */
309
+ ],
310
+ });
311
+
312
+ if (watch) {
313
+ await ctx.watch();
314
+ } else {
315
+ await ctx.rebuild();
316
+ await ctx.dispose();
317
+ }
318
+ }
319
+
320
+ main().catch(e => {
321
+ console.error(e);
322
+ process.exit(1);
323
+ });
324
+ ```
325
+
326
+ ## devDependencies
327
+
328
+ ```json
329
+ "devDependencies": {
330
+ "@types/vscode": "^1.85.0",
331
+ "@types/mocha": "^10.0.6",
332
+ "@types/node": "20.x",
333
+ "@vscode/test-cli": "^0.0.6",
334
+ "@vscode/test-electron": "^2.3.8",
335
+ "esbuild": "^0.20.0",
336
+ "eslint": "^8.56.0",
337
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
338
+ "@typescript-eslint/parser": "^7.0.0",
339
+ "typescript": "^5.3.0"
340
+ }
341
+ ```
342
+
343
+ - `@types/vscode` version must match your `engines.vscode` range
344
+ - `@vscode/test-cli` provides the `vscode-test` command
345
+ - `@vscode/test-electron` downloads and launches VS Code for integration tests