@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,397 @@
1
+ """
2
+ parse_design_md.py — Extracts metadata from DESIGN.md for stitch-showcase.
3
+
4
+ Usage:
5
+ python parse_design_md.py /path/to/DESIGN.md
6
+ → prints JSON with project_name, type, colors, color_tokens, default_theme, font_family, screens, sections
7
+ """
8
+ import re
9
+ import sys
10
+ import json
11
+ from pathlib import Path
12
+
13
+
14
+ def parse(design_md_path: str) -> dict:
15
+ path = Path(design_md_path)
16
+ if not path.exists():
17
+ return {
18
+ "project_name": "",
19
+ "type": "unknown",
20
+ "colors": {},
21
+ "color_tokens": {},
22
+ "default_theme": "light",
23
+ "font_family": None,
24
+ "screens": [],
25
+ "sections": [],
26
+ }
27
+
28
+ text = path.read_text(encoding="utf-8")
29
+ colors = _extract_colors(text)
30
+ color_tokens = _extract_color_tokens(text)
31
+
32
+ # Determine default_theme from surface token
33
+ surface_hex = color_tokens.get("surface") or color_tokens.get("background")
34
+ if not surface_hex:
35
+ # fall back to colors dict
36
+ for key in ("surface", "background", "bg"):
37
+ if key in colors:
38
+ surface_hex = colors[key]
39
+ break
40
+ default_theme = _surface_default_theme(surface_hex) if surface_hex else "light"
41
+
42
+ screens = _extract_screens(text)
43
+ sections = _extract_sections(text)
44
+
45
+ # Merge inline titles and descriptions from sections into the screens list.
46
+ # Sections can contain "- slug: Title | Description" or "- slug: description"
47
+ # entries that _extract_screens misses when slugs are only listed under ### headers.
48
+ section_titles = {}
49
+ section_descs = {}
50
+ for sec in sections:
51
+ for slug, title in sec.get("titles", {}).items():
52
+ section_titles[slug] = title
53
+ for slug, desc in sec.get("descriptions", {}).items():
54
+ section_descs[slug] = desc
55
+
56
+ if section_titles or section_descs:
57
+ existing_slugs = {s["slug"] for s in screens}
58
+ # Apply titles/descriptions to screens already in the list
59
+ for s in screens:
60
+ slug = s["slug"]
61
+ if not s.get("title") or s["title"] == _slug_to_title(slug):
62
+ if slug in section_titles:
63
+ s["title"] = section_titles[slug]
64
+ if not s.get("description") and slug in section_descs:
65
+ s["description"] = section_descs[slug]
66
+ # Add slugs that only appear inside sections (not in the flat screen list)
67
+ for sec in sections:
68
+ for slug in sec["slugs"]:
69
+ if slug not in existing_slugs:
70
+ screens.append({
71
+ "slug": slug,
72
+ "title": section_titles.get(slug) or _slug_to_title(slug),
73
+ "description": section_descs.get(slug, ""),
74
+ })
75
+ existing_slugs.add(slug)
76
+
77
+ return {
78
+ "project_name": _extract_project_name(text),
79
+ "type": _detect_type(text),
80
+ "colors": colors,
81
+ "color_tokens": color_tokens,
82
+ "default_theme": default_theme,
83
+ "font_family": _extract_typography(text),
84
+ "screens": screens,
85
+ "sections": sections,
86
+ }
87
+
88
+
89
+ def _extract_project_name(text: str) -> str:
90
+ """First H1 heading in the document."""
91
+ m = re.search(r"^#\s+(.+)$", text, re.MULTILINE)
92
+ return m.group(1).strip() if m else ""
93
+
94
+
95
+ def _detect_type(text: str) -> str:
96
+ """Returns 'mobile', 'web', or 'unknown'.
97
+
98
+ Priority:
99
+ 1. Explicit ``## Type`` section with 'mobile' or 'web' on the next line
100
+ 2. Keyword scoring across the full document
101
+ """
102
+ # 1. Explicit ## Type section — authoritative if present
103
+ m = re.search(r"^##\s+Type\s*\n\s*(\S+)", text, re.MULTILINE | re.IGNORECASE)
104
+ if m:
105
+ val = m.group(1).strip().lower()
106
+ if val in ("mobile", "web"):
107
+ return val
108
+
109
+ # 2. Keyword scoring fallback
110
+ lower = text.lower()
111
+ mobile_keywords = ["móvil", "movil", "mobile", "ios", "android", "app móvil", "aplicación móvil"]
112
+ web_keywords = ["web", "dashboard", "escritorio", "desktop", "browser", "navegador"]
113
+
114
+ mobile_score = sum(1 for kw in mobile_keywords if kw in lower)
115
+ web_score = sum(1 for kw in web_keywords if kw in lower)
116
+
117
+ if mobile_score > web_score:
118
+ return "mobile"
119
+ if web_score > mobile_score:
120
+ return "web"
121
+ return "unknown"
122
+
123
+
124
+ def _extract_colors(text: str) -> dict:
125
+ """Extract name:value pairs from the colors section."""
126
+ colors = {}
127
+ color_section = re.search(
128
+ r"##\s+(?:Colores?|Colors?)\s*\n(.*?)(?=\n##|\Z)",
129
+ text, re.IGNORECASE | re.DOTALL
130
+ )
131
+ if not color_section:
132
+ return colors
133
+
134
+ for line in color_section.group(1).splitlines():
135
+ # Formats: "- Primary: #FDD900" or "Primary: #FDD900"
136
+ m = re.search(r"[-*]?\s*(.+?):\s*(#[0-9A-Fa-f]{3,8}|rgb\(.+?\)|[a-z]+)\s*$", line, re.IGNORECASE)
137
+ if m:
138
+ key = m.group(1).strip().lower()
139
+ val = m.group(2).strip()
140
+ colors[key] = val
141
+
142
+ return colors
143
+
144
+
145
+ def _extract_color_tokens(text: str) -> dict:
146
+ """
147
+ Extract semantic color tokens in Stitch DESIGN.md format.
148
+
149
+ Matches: `token-name` (#XXXXXX) or token-name (#XXXXXX)
150
+ Returns dict with semantic roles:
151
+ accent → first token containing 'primary' (not 'on-primary')
152
+ surface → first token named 'surface' or containing 'background'/'bg'
153
+ + all raw tokens by name
154
+ """
155
+ tokens = {}
156
+
157
+ # Match backtick-wrapped: `token-name` (#XXXXXX)
158
+ for m in re.finditer(r"`([a-z][a-z0-9\-]+)`\s*\(#([0-9A-Fa-f]{6})\)", text, re.IGNORECASE):
159
+ tokens[m.group(1).lower()] = "#" + m.group(2).upper()
160
+
161
+ # Match bare: token-name (#XXXXXX) (only if not already captured)
162
+ for m in re.finditer(r"\b([a-z][a-z0-9\-]+)\s*\(#([0-9A-Fa-f]{6})\)", text, re.IGNORECASE):
163
+ key = m.group(1).lower()
164
+ if key not in tokens:
165
+ tokens[key] = "#" + m.group(2).upper()
166
+
167
+ # Build semantic roles
168
+ result = dict(tokens) # copy all raw tokens
169
+
170
+ # accent: first 'primary' token that isn't 'on-primary'
171
+ for name, val in tokens.items():
172
+ if "primary" in name and not name.startswith("on-"):
173
+ result["accent"] = val
174
+ break
175
+
176
+ # surface: 'surface' or 'background'/'bg'
177
+ for name, val in tokens.items():
178
+ if name == "surface" or "background" in name or name == "bg":
179
+ result["surface"] = val
180
+ break
181
+
182
+ return result
183
+
184
+
185
+ def _surface_default_theme(surface_hex: str) -> str:
186
+ """
187
+ Determine showcase default theme from app surface color luminance.
188
+
189
+ Dark app surface → use light showcase (for contrast).
190
+ Light app surface → use dark showcase (for contrast).
191
+ """
192
+ hex_clean = surface_hex.lstrip("#")
193
+ if len(hex_clean) != 6:
194
+ return "light"
195
+ try:
196
+ r = int(hex_clean[0:2], 16)
197
+ g = int(hex_clean[2:4], 16)
198
+ b = int(hex_clean[4:6], 16)
199
+ except ValueError:
200
+ return "light"
201
+
202
+ luminance = 0.299 * r + 0.587 * g + 0.114 * b
203
+ if luminance < 100:
204
+ return "light" # dark app → light showcase
205
+ if luminance > 155:
206
+ return "dark" # light app → dark showcase
207
+ return "light"
208
+
209
+
210
+ def _extract_typography(text: str) -> str | None:
211
+ """
212
+ Extract primary font family name from the Typography section of DESIGN.md.
213
+
214
+ Returns font name (e.g. 'Inter') or None if not found.
215
+ """
216
+ # Look for ## Typography or ## N. Typography section
217
+ typo_section = re.search(
218
+ r"##\s+(?:\d+\.\s+)?Typography\s*\n(.*?)(?=\n##|\Z)",
219
+ text, re.IGNORECASE | re.DOTALL
220
+ )
221
+
222
+ UI_LABELS = {"display", "headline", "title", "body", "label", "bold", "regular", "medium", "semibold"}
223
+
224
+ if typo_section:
225
+ section_text = typo_section.group(1)
226
+ # Look for bold text **FontName** in first few lines
227
+ for line in section_text.splitlines()[:6]:
228
+ m = re.search(r"\*\*([A-Z][a-zA-Z\s\+]+)\*\*", line)
229
+ if m:
230
+ candidate = m.group(1).strip()
231
+ words = candidate.split()
232
+ # Font names are 1-3 words, not UI labels
233
+ if 1 <= len(words) <= 3 and words[0].lower() not in UI_LABELS:
234
+ return candidate
235
+
236
+ # Broader search in full section
237
+ for m in re.finditer(r"\*\*([A-Z][a-zA-Z\s\+]+)\*\*", section_text):
238
+ candidate = m.group(1).strip()
239
+ words = candidate.split()
240
+ if 1 <= len(words) <= 3 and words[0].lower() not in UI_LABELS:
241
+ return candidate
242
+
243
+ # Fallback: search entire doc for font-family
244
+ m = re.search(r"font-family:\s*[\"']?([A-Z][a-zA-Z\s]+)[\"']?", text)
245
+ if m:
246
+ candidate = m.group(1).strip().rstrip(",;")
247
+ if candidate:
248
+ return candidate
249
+
250
+ return None
251
+
252
+
253
+ def _extract_screens(text: str) -> list:
254
+ """
255
+ Extract screen list with slug, title, description.
256
+
257
+ Supported formats:
258
+ 1. Markdown table: | slug | title | description |
259
+ 2. Numbered list: 1. splash_screen - Description
260
+ 3. Bullet list: - splash_screen — Description
261
+ 4. Simple pair: splash_screen: Description
262
+ """
263
+ # Attempt 1: markdown table
264
+ table_screens = _parse_table(text)
265
+ if table_screens:
266
+ return table_screens
267
+
268
+ # Attempt 2: numbered or bullet list inside a Screens section
269
+ list_screens = _parse_screen_list(text)
270
+ if list_screens:
271
+ return list_screens
272
+
273
+ return []
274
+
275
+
276
+ def _parse_table(text: str) -> list:
277
+ """Parse markdown table with slug/title/description columns."""
278
+ section = re.search(
279
+ r"##\s+(?:Pantallas?|Screens?)\s*\n(.*?)(?=\n##|\Z)",
280
+ text, re.IGNORECASE | re.DOTALL
281
+ )
282
+ if not section:
283
+ return []
284
+
285
+ section_text = section.group(1)
286
+ rows = []
287
+
288
+ for line in section_text.splitlines():
289
+ # Table rows must START with | (not bullet lines with "Title | Desc" format)
290
+ if not re.match(r"^\s*\|", line) or re.match(r"^\s*\|[-\s|]+\|\s*$", line):
291
+ continue
292
+ cols = [c.strip() for c in line.split("|") if c.strip()]
293
+ if len(cols) >= 2:
294
+ slug = _to_slug(cols[0])
295
+ title = cols[1] if len(cols) > 1 else _slug_to_title(slug)
296
+ desc = cols[2] if len(cols) > 2 else ""
297
+ rows.append({"slug": slug, "title": title, "description": desc})
298
+
299
+ # Skip header row if it contains "slug", "screen", etc.
300
+ if rows and rows[0]["slug"] in ("slug", "pantalla", "screen", "nombre", "name"):
301
+ rows = rows[1:]
302
+
303
+ return rows
304
+
305
+
306
+ def _parse_screen_list(text: str) -> list:
307
+ """Parse numbered or bullet lists of screens."""
308
+ section = re.search(
309
+ r"##\s+(?:Pantallas?|Screens?)\s*\n(.*?)(?=\n##|\Z)",
310
+ text, re.IGNORECASE | re.DOTALL
311
+ )
312
+ if not section:
313
+ return []
314
+
315
+ screens = []
316
+ for line in section.group(1).splitlines():
317
+ # "1. slug_name - Description" or "- slug_name — Description" or "- slug: Title | Description"
318
+ m = re.match(r"^\s*(?:\d+\.|[-*])\s+([a-zA-Z0-9_\-]+)\s*[-—:]\s*(.+)$", line)
319
+ if m:
320
+ slug = _to_slug(m.group(1))
321
+ raw = m.group(2).strip()
322
+ if " | " in raw:
323
+ title_part, desc_part = raw.split(" | ", 1)
324
+ title = title_part.strip()
325
+ desc = desc_part.strip()
326
+ else:
327
+ title = _slug_to_title(slug)
328
+ desc = raw
329
+ screens.append({"slug": slug, "title": title, "description": desc})
330
+
331
+ return screens
332
+
333
+
334
+ def _extract_sections(text: str) -> list:
335
+ """
336
+ Extract sections with their screen slugs if defined in DESIGN.md.
337
+
338
+ Expected format:
339
+ ### Onboarding
340
+ - splash_screen
341
+ - login
342
+ """
343
+ sections = []
344
+ section_block = re.search(
345
+ r"##\s+(?:Pantallas?|Screens?)\s*\n(.*?)(?=\n##\s+(?!#)|\Z)",
346
+ text, re.IGNORECASE | re.DOTALL
347
+ )
348
+ if not section_block:
349
+ return []
350
+
351
+ content = section_block.group(1)
352
+ current_section = None
353
+
354
+ for line in content.splitlines():
355
+ h3 = re.match(r"^###\s+(.+)$", line)
356
+ if h3:
357
+ current_section = {"name": h3.group(1).strip(), "slugs": []}
358
+ sections.append(current_section)
359
+ continue
360
+
361
+ if current_section:
362
+ m = re.match(r"^\s*[-*\d.]+\s*([a-zA-Z0-9_\-]+)\s*(?:[-—:]\s*(.+))?$", line)
363
+ if m:
364
+ slug = _to_slug(m.group(1))
365
+ current_section["slugs"].append(slug)
366
+ if m.group(2):
367
+ raw = m.group(2).strip()
368
+ # Support "Title | Description" format for mangled slugs
369
+ if " | " in raw:
370
+ title_part, desc_part = raw.split(" | ", 1)
371
+ current_section.setdefault("titles", {})[slug] = title_part.strip()
372
+ current_section.setdefault("descriptions", {})[slug] = desc_part.strip()
373
+ else:
374
+ current_section.setdefault("descriptions", {})[slug] = raw
375
+
376
+ return sections
377
+
378
+
379
+ def _to_slug(s: str) -> str:
380
+ """Normalize to snake_case slug."""
381
+ s = s.strip().lower()
382
+ s = re.sub(r"[\s\-]+", "_", s)
383
+ s = re.sub(r"[^a-z0-9_]", "", s)
384
+ return s
385
+
386
+
387
+ def _slug_to_title(slug: str) -> str:
388
+ """splash_screen → 'Splash Screen'"""
389
+ return slug.replace("_", " ").replace("-", " ").title()
390
+
391
+
392
+ if __name__ == "__main__":
393
+ if len(sys.argv) < 2:
394
+ print("Usage: python parse_design_md.py /path/to/DESIGN.md", file=sys.stderr)
395
+ sys.exit(1)
396
+ result = parse(sys.argv[1])
397
+ print(json.dumps(result, ensure_ascii=False, indent=2))
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: vscode-extension-dev
3
+ description: >
4
+ Guide for building VS Code extensions from scratch. Use when the user is creating,
5
+ scaffolding, designing, debugging, testing, bundling, or publishing a VS Code extension.
6
+ Covers all major API patterns: TreeView, QuickPick, Webview, StatusBar, commands,
7
+ configuration, SecretStorage, progress indicators, and esbuild bundling.
8
+ when_to_use: >
9
+ - User wants to create a new VS Code extension
10
+ - User asks about VS Code extension APIs (TreeView, Webview, QuickPick, etc.)
11
+ - User needs help with package.json contributes, activationEvents, or keybindings
12
+ - User is debugging extension activation, disposables, or memory leaks
13
+ - User asks about bundling extensions with esbuild or webpack
14
+ - User wants to publish an extension to the VS Code Marketplace or Open VSX
15
+ - User asks about Webview CSP, nonce, or postMessage communication
16
+ - User asks about SecretStorage or credential management in extensions
17
+ - User needs help with extension testing (@vscode/test-electron)
18
+ source: "VS Code Extension API documentation (https://code.visualstudio.com/api)"
19
+ anti_hallucination_note: >
20
+ ALL guidance in this skill comes from the official VS Code Extension API docs
21
+ and established community patterns. Do NOT invent API methods, event names,
22
+ or configuration keys. If unsure whether an API exists, say so explicitly.
23
+ Always verify imports come from the 'vscode' module.
24
+ ---
25
+
26
+ # VS Code Extension Development Skill
27
+
28
+ You are a VS Code extension development advisor. Base ALL guidance on the reference files below — not training data.
29
+
30
+ ## How to Use This Skill
31
+
32
+ 1. Read the relevant reference file(s) before answering
33
+ 2. Base ALL code on the reference content — not training data
34
+ 3. Use real TypeScript imports and correct `vscode` API signatures
35
+ 4. Do not invent API methods, events, or configuration keys not in the references
36
+
37
+ ## Scaffolding Workflow
38
+
39
+ 1. **Generate project**: `npx --package yo --package generator-code -- yo code`
40
+ 2. **Choose template**: TypeScript extension (recommended)
41
+ 3. **Choose bundler**: esbuild (recommended) or webpack
42
+ 4. **Project structure** created — see `references/architecture.md` for layout
43
+ 5. **Configure** `package.json` — see `references/package-json-schema.md`
44
+ 6. **Implement** — see `references/api-patterns.md` for working examples
45
+ 7. **Test** — see `references/architecture.md` for testing strategy
46
+ 8. **Publish** — see `references/publishing.md` for full workflow
47
+
48
+ ## UI Component Decision Matrix
49
+
50
+ | Need | Use | Why |
51
+ | --------------------------------- | -------------------- | ---------------------------------------------- |
52
+ | Hierarchical data in sidebar | TreeView | Native tree with expand/collapse, icons, badges |
53
+ | Quick selection from a list | QuickPick | Modal list with filtering, multi-select |
54
+ | Rich HTML interface | Webview Panel | Full HTML/CSS/JS, but heavier and needs CSP |
55
+ | Persistent status info | StatusBarItem | Always visible, clickable, lightweight |
56
+ | Simple text input | InputBox | Single-line input with validation |
57
+ | File/folder selection | showOpenDialog | Native OS file picker |
58
+ | Background task progress | withProgress | Notification or status bar progress |
59
+
60
+ ## Key Patterns
61
+
62
+ ### Lazy Activation
63
+ - Use `activationEvents` in `package.json` to defer activation
64
+ - Since VS Code 1.74+, commands in `contributes.commands` auto-generate `onCommand:` events
65
+ - Prefer specific events (`onLanguage:python`, `onView:myTreeView`) over `*`
66
+ - See `references/package-json-schema.md` for full activationEvents reference
67
+
68
+ ### Disposable Management
69
+ - Push ALL subscriptions to `context.subscriptions` in `activate()`
70
+ - Use `deactivate()` only for async cleanup (closing connections, stopping servers)
71
+ - Never rely on garbage collection — always dispose explicitly
72
+ - See `references/api-patterns.md` for the cleanup pattern
73
+
74
+ ### withProgress for Async Operations
75
+ - Use `ProgressLocation.Notification` for user-facing tasks
76
+ - Use `ProgressLocation.Window` for status bar progress
77
+ - Support cancellation via `CancellationToken`
78
+ - See `references/api-patterns.md` for working examples
79
+
80
+ ### SecretStorage for Credentials
81
+ - Use `context.secrets` (SecretStorage API) — never store tokens in settings
82
+ - Fires `onDidChange` event when secrets change
83
+ - See `references/api-patterns.md` for the credential manager pattern
84
+
85
+ ### Webview CSP and PostMessage
86
+ - Always set a Content Security Policy with nonce
87
+ - Use `webview.asWebviewUri()` for local resources
88
+ - Bidirectional communication via `postMessage` / `onDidReceiveMessage`
89
+ - See `references/api-patterns.md` for the full Webview pattern
90
+
91
+ ### esbuild Bundling
92
+ - Bundle extension into a single file for faster activation
93
+ - Mark `vscode` as external (it's provided by the runtime)
94
+ - See `references/package-json-schema.md` for scripts configuration
95
+
96
+ ## Reference Files
97
+
98
+ | File | Topics |
99
+ | ----------------------------------- | ------------------------------------------------------------------- |
100
+ | `references/package-json-schema.md` | contributes, activationEvents, engines, scripts, devDependencies |
101
+ | `references/api-patterns.md` | TreeView, Webview, QuickPick, StatusBar, SecretStorage, withProgress |
102
+ | `references/architecture.md` | Project structure, layered architecture, testing strategy |
103
+ | `references/publishing.md` | vsce, .vscodeignore, CI/CD, Open VSX, versioning |
104
+
105
+ ## Anti-Patterns to Avoid
106
+
107
+ - Using `*` activation event in production (activates on every VS Code start)
108
+ - Storing secrets in `configuration` instead of `SecretStorage`
109
+ - Forgetting to dispose subscriptions (causes memory leaks)
110
+ - Missing CSP in Webviews (security vulnerability)
111
+ - Bundling `node_modules` instead of using esbuild/webpack
112
+ - Using synchronous file I/O in the extension host (blocks the UI)
113
+ - Registering commands without corresponding `contributes.commands` entries
114
+ - Hardcoding `vscode.workspace.rootPath` (deprecated — use `workspaceFolders`)