@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.
- package/README.md +531 -0
- package/bin/aiskills.js +76 -0
- package/lib/cache.js +49 -0
- package/lib/cleanup.js +77 -0
- package/lib/commands/auto-update.js +131 -0
- package/lib/commands/doctor.js +139 -0
- package/lib/commands/list.js +77 -0
- package/lib/commands/skills.js +263 -0
- package/lib/commands/status.js +94 -0
- package/lib/commands/uninstall.js +182 -0
- package/lib/commands/update.js +149 -0
- package/lib/config.js +90 -0
- package/lib/downloader.js +110 -0
- package/lib/hooks.js +74 -0
- package/lib/installer.js +114 -0
- package/lib/platform.js +112 -0
- package/lib/prompts/checkboxCancel.js +264 -0
- package/lib/prompts/selectCancel.js +204 -0
- package/lib/symlink.js +154 -0
- package/lib/utils.js +49 -0
- package/package.json +61 -0
- package/skills/humaniza/SKILL.md +51 -0
- package/skills/humaniza/agents/openai.yaml +4 -0
- package/skills/humaniza/references/ai-patterns-es.md +51 -0
- package/skills/humaniza/references/checklist.md +9 -0
- package/skills/humaniza/references/examples.md +17 -0
- package/skills/humaniza/references/lexicon-es-mx.md +36 -0
- package/skills/humaniza/references/modes-es-mx.md +41 -0
- package/skills/humaniza/references/voice-es-mx.md +24 -0
- package/skills/refactoring-ui/SKILL.md +59 -0
- package/skills/refactoring-ui/references/01-design-process.md +72 -0
- package/skills/refactoring-ui/references/02-visual-hierarchy.md +84 -0
- package/skills/refactoring-ui/references/03-layout-spacing.md +69 -0
- package/skills/refactoring-ui/references/04-typography.md +70 -0
- package/skills/refactoring-ui/references/05-color.md +96 -0
- package/skills/refactoring-ui/references/06-depth-shadows.md +74 -0
- package/skills/refactoring-ui/references/07-images.md +75 -0
- package/skills/refactoring-ui/references/08-finishing-touches.md +91 -0
- package/skills/stitch-showcase/SKILL.md +411 -0
- package/skills/stitch-showcase/references/01-navbar.md +52 -0
- package/skills/stitch-showcase/references/02-hero.md +56 -0
- package/skills/stitch-showcase/references/03-design-system.md +102 -0
- package/skills/stitch-showcase/references/04-screen-gallery.md +102 -0
- package/skills/stitch-showcase/references/05-viewer-web.md +105 -0
- package/skills/stitch-showcase/references/06-viewer-mobile.md +104 -0
- package/skills/stitch-showcase/references/07-theme-system.md +77 -0
- package/skills/stitch-showcase/references/08-type-detection.md +81 -0
- package/skills/stitch-showcase/references/09-quality-standards.md +126 -0
- package/skills/stitch-showcase/references/10-component-standardization.md +40 -0
- package/skills/stitch-showcase/references/11-component-catalog.md +70 -0
- package/skills/stitch-showcase/references/catalog-template.html +841 -0
- package/skills/stitch-showcase/references/index.html +299 -0
- package/skills/stitch-showcase/references/viewer.html +412 -0
- package/skills/stitch-showcase/scripts/__pycache__/build_showcase.cpython-314.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/component_utils.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/detect_components.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/extract_catalog.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/extract_text.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/extract_zips.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/__pycache__/parse_design_md.cpython-313.pyc +0 -0
- package/skills/stitch-showcase/scripts/apply_canonical.py +238 -0
- package/skills/stitch-showcase/scripts/build_showcase.py +2103 -0
- package/skills/stitch-showcase/scripts/component_utils.py +398 -0
- package/skills/stitch-showcase/scripts/detect_components.py +284 -0
- package/skills/stitch-showcase/scripts/extract_catalog.py +913 -0
- package/skills/stitch-showcase/scripts/extract_text.py +268 -0
- package/skills/stitch-showcase/scripts/extract_zips.py +178 -0
- package/skills/stitch-showcase/scripts/parse_design_md.py +397 -0
- package/skills/vscode-extension-dev/SKILL.md +114 -0
- package/skills/vscode-extension-dev/references/api-patterns.md +625 -0
- package/skills/vscode-extension-dev/references/architecture.md +287 -0
- package/skills/vscode-extension-dev/references/package-json-schema.md +345 -0
- 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
|