elsabro 2.1.0 → 2.3.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 (64) hide show
  1. package/agents/elsabro-orchestrator.md +113 -0
  2. package/commands/elsabro/add-phase.md +17 -0
  3. package/commands/elsabro/add-todo.md +111 -53
  4. package/commands/elsabro/audit-milestone.md +19 -0
  5. package/commands/elsabro/check-todos.md +210 -31
  6. package/commands/elsabro/complete-milestone.md +20 -1
  7. package/commands/elsabro/debug.md +19 -0
  8. package/commands/elsabro/discuss-phase.md +18 -1
  9. package/commands/elsabro/execute.md +511 -58
  10. package/commands/elsabro/insert-phase.md +18 -1
  11. package/commands/elsabro/list-phase-assumptions.md +17 -0
  12. package/commands/elsabro/new-milestone.md +19 -0
  13. package/commands/elsabro/new.md +19 -0
  14. package/commands/elsabro/pause-work.md +19 -0
  15. package/commands/elsabro/plan-milestone-gaps.md +20 -1
  16. package/commands/elsabro/plan.md +264 -36
  17. package/commands/elsabro/progress.md +203 -79
  18. package/commands/elsabro/quick.md +19 -0
  19. package/commands/elsabro/remove-phase.md +17 -0
  20. package/commands/elsabro/research-phase.md +18 -1
  21. package/commands/elsabro/resume-work.md +19 -0
  22. package/commands/elsabro/start.md +399 -98
  23. package/commands/elsabro/verify-work.md +138 -5
  24. package/hooks/confirm-destructive.sh +145 -0
  25. package/hooks/hooks-config.json +81 -0
  26. package/hooks/lint-check.sh +238 -0
  27. package/hooks/post-edit-test.sh +189 -0
  28. package/package.json +3 -2
  29. package/references/SYSTEM_INDEX.md +241 -0
  30. package/references/command-flow.md +352 -0
  31. package/references/enforcement-rules.md +331 -0
  32. package/references/error-contracts-tests.md +1171 -0
  33. package/references/error-contracts.md +3102 -0
  34. package/references/error-handling-instructions.md +26 -12
  35. package/references/parallel-worktrees.md +293 -0
  36. package/references/state-sync.md +381 -0
  37. package/references/task-dispatcher.md +388 -0
  38. package/references/tasks-integration.md +380 -0
  39. package/scripts/setup-parallel-worktrees.sh +319 -0
  40. package/skills/api-microservice.md +765 -0
  41. package/skills/api-setup.md +76 -3
  42. package/skills/auth-setup.md +46 -6
  43. package/skills/chrome-extension.md +584 -0
  44. package/skills/cicd-setup.md +1206 -0
  45. package/skills/cli-tool.md +884 -0
  46. package/skills/database-setup.md +41 -5
  47. package/skills/desktop-app.md +1351 -0
  48. package/skills/expo-app.md +35 -2
  49. package/skills/full-stack-app.md +543 -0
  50. package/skills/memory-update.md +207 -0
  51. package/skills/mobile-app.md +813 -0
  52. package/skills/nextjs-app.md +33 -2
  53. package/skills/payments-setup.md +76 -1
  54. package/skills/review.md +331 -0
  55. package/skills/saas-starter.md +639 -0
  56. package/skills/sentry-setup.md +41 -7
  57. package/skills/techdebt.md +289 -0
  58. package/skills/testing-setup.md +1218 -0
  59. package/skills/tutor.md +219 -0
  60. package/templates/.planning/notes/.gitkeep +0 -0
  61. package/templates/CLAUDE.md.template +48 -0
  62. package/templates/error-handling-config.json +79 -2
  63. package/templates/mistakes.md.template +52 -0
  64. package/templates/patterns.md.template +114 -0
@@ -0,0 +1,584 @@
1
+ ---
2
+ name: chrome-extension
3
+ description: Crear extension de Chrome con Manifest V3, content scripts, background service worker, popup UI, Storage API y publicacion en Chrome Web Store.
4
+ tags: [chrome, extension, browser, manifest-v3, typescript, vite]
5
+ difficulty: intermediate
6
+ estimated_time: 35min
7
+ ---
8
+
9
+ # Skill: Extension de Chrome
10
+
11
+ <when_to_use>
12
+ Usar cuando el usuario menciona:
13
+ - "extension de Chrome"
14
+ - "extension de navegador"
15
+ - "browser extension"
16
+ - "plugin de Chrome"
17
+ - "addon"
18
+ </when_to_use>
19
+
20
+ <pre_requisites>
21
+ ## Pre-requisitos
22
+
23
+ - Chrome o Chromium
24
+ - Node.js 18+ (para build tools)
25
+ - Conocimiento basico de JavaScript/TypeScript
26
+ </pre_requisites>
27
+
28
+ <project_structure>
29
+ ## Estructura de Proyecto
30
+
31
+ ```
32
+ my-extension/
33
+ ├── src/
34
+ │ ├── background/
35
+ │ │ └── index.ts # Service worker
36
+ │ ├── content/
37
+ │ │ ├── index.ts # Content script
38
+ │ │ └── styles.css # Estilos inyectados
39
+ │ ├── popup/
40
+ │ │ ├── index.html # Popup HTML
41
+ │ │ ├── index.ts # Popup logic
42
+ │ │ └── styles.css # Popup styles
43
+ │ ├── options/
44
+ │ │ ├── index.html # Options page
45
+ │ │ └── index.ts
46
+ │ └── utils/
47
+ │ ├── storage.ts # Storage helpers
48
+ │ └── messaging.ts # Message helpers
49
+ ├── public/
50
+ │ ├── icons/
51
+ │ │ ├── icon16.png
52
+ │ │ ├── icon48.png
53
+ │ │ └── icon128.png
54
+ │ └── manifest.json
55
+ ├── dist/ # Build output
56
+ ├── package.json
57
+ ├── tsconfig.json
58
+ ├── vite.config.ts
59
+ └── README.md
60
+ ```
61
+ </project_structure>
62
+
63
+ <setup_steps>
64
+ ## Pasos de Setup
65
+
66
+ ### Paso 1: Inicializar proyecto
67
+
68
+ ```bash
69
+ mkdir my-extension && cd my-extension
70
+ npm init -y
71
+ npm install -D vite typescript @crxjs/vite-plugin@beta
72
+ ```
73
+
74
+ ### Paso 2: Crear manifest.json
75
+
76
+ Crear `public/manifest.json`:
77
+
78
+ ```json
79
+ {
80
+ "manifest_version": 3,
81
+ "name": "My Extension",
82
+ "version": "1.0.0",
83
+ "description": "Una extension increible para Chrome",
84
+ "permissions": [
85
+ "storage",
86
+ "activeTab",
87
+ "scripting",
88
+ "contextMenus"
89
+ ],
90
+ "host_permissions": [
91
+ "https://*/*",
92
+ "http://*/*"
93
+ ],
94
+ "background": {
95
+ "service_worker": "src/background/index.ts",
96
+ "type": "module"
97
+ },
98
+ "content_scripts": [
99
+ {
100
+ "matches": ["https://*/*", "http://*/*"],
101
+ "js": ["src/content/index.ts"],
102
+ "css": ["src/content/styles.css"],
103
+ "run_at": "document_end"
104
+ }
105
+ ],
106
+ "action": {
107
+ "default_popup": "src/popup/index.html",
108
+ "default_icon": {
109
+ "16": "icons/icon16.png",
110
+ "48": "icons/icon48.png",
111
+ "128": "icons/icon128.png"
112
+ },
113
+ "default_title": "My Extension"
114
+ },
115
+ "options_page": "src/options/index.html",
116
+ "icons": {
117
+ "16": "icons/icon16.png",
118
+ "48": "icons/icon48.png",
119
+ "128": "icons/icon128.png"
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### Paso 3: Configurar TypeScript
125
+
126
+ Crear `tsconfig.json`:
127
+
128
+ ```json
129
+ {
130
+ "compilerOptions": {
131
+ "target": "ES2020",
132
+ "module": "ESNext",
133
+ "moduleResolution": "bundler",
134
+ "strict": true,
135
+ "esModuleInterop": true,
136
+ "skipLibCheck": true,
137
+ "forceConsistentCasingInFileNames": true,
138
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
139
+ "types": ["chrome"]
140
+ },
141
+ "include": ["src/**/*"],
142
+ "exclude": ["node_modules", "dist"]
143
+ }
144
+ ```
145
+
146
+ Instalar tipos de Chrome:
147
+
148
+ ```bash
149
+ npm install -D @types/chrome
150
+ ```
151
+
152
+ ### Paso 4: Configurar Vite
153
+
154
+ Crear `vite.config.ts`:
155
+
156
+ ```typescript
157
+ import { defineConfig } from "vite";
158
+ import { crx } from "@crxjs/vite-plugin";
159
+ import manifest from "./public/manifest.json";
160
+
161
+ export default defineConfig({
162
+ plugins: [crx({ manifest })],
163
+ build: {
164
+ outDir: "dist",
165
+ emptyOutDir: true,
166
+ },
167
+ });
168
+ ```
169
+
170
+ ### Paso 5: Crear Background Service Worker
171
+
172
+ Crear `src/background/index.ts`:
173
+
174
+ ```typescript
175
+ // Background service worker para Manifest V3
176
+
177
+ // Cuando la extension se instala
178
+ chrome.runtime.onInstalled.addListener((details) => {
179
+ console.log("Extension instalada:", details.reason);
180
+
181
+ // Crear context menu
182
+ chrome.contextMenus.create({
183
+ id: "myExtensionMenu",
184
+ title: "Procesar con My Extension",
185
+ contexts: ["selection"],
186
+ });
187
+
188
+ // Inicializar storage con valores por defecto
189
+ chrome.storage.local.set({
190
+ enabled: true,
191
+ settings: {
192
+ theme: "light",
193
+ notifications: true,
194
+ },
195
+ });
196
+ });
197
+
198
+ // Manejar click en context menu
199
+ chrome.contextMenus.onClicked.addListener((info, tab) => {
200
+ if (info.menuItemId === "myExtensionMenu" && info.selectionText) {
201
+ console.log("Texto seleccionado:", info.selectionText);
202
+
203
+ // Enviar mensaje al content script
204
+ if (tab?.id) {
205
+ chrome.tabs.sendMessage(tab.id, {
206
+ type: "PROCESS_SELECTION",
207
+ text: info.selectionText,
208
+ });
209
+ }
210
+ }
211
+ });
212
+
213
+ // Escuchar mensajes del content script o popup
214
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
215
+ console.log("Mensaje recibido:", message, "de:", sender);
216
+
217
+ switch (message.type) {
218
+ case "GET_SETTINGS":
219
+ chrome.storage.local.get(["settings"], (result) => {
220
+ sendResponse(result.settings || {});
221
+ });
222
+ return true; // Indica respuesta asincrona
223
+
224
+ case "SAVE_SETTINGS":
225
+ chrome.storage.local.set({ settings: message.payload }, () => {
226
+ sendResponse({ success: true });
227
+ });
228
+ return true;
229
+
230
+ default:
231
+ sendResponse({ error: "Unknown message type" });
232
+ }
233
+ });
234
+ ```
235
+
236
+ ### Paso 6: Crear Content Script
237
+
238
+ Crear `src/content/index.ts`:
239
+
240
+ ```typescript
241
+ // Content script - se ejecuta en cada pagina
242
+
243
+ console.log("Content script cargado en:", window.location.href);
244
+
245
+ // Escuchar mensajes del background o popup
246
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
247
+ console.log("Content script recibio:", message);
248
+
249
+ switch (message.type) {
250
+ case "PROCESS_SELECTION":
251
+ processSelection(message.text);
252
+ sendResponse({ success: true });
253
+ break;
254
+
255
+ case "PING":
256
+ sendResponse({ pong: true, url: window.location.href });
257
+ break;
258
+
259
+ case "GET_PAGE_INFO":
260
+ sendResponse({
261
+ title: document.title,
262
+ url: window.location.href,
263
+ });
264
+ break;
265
+
266
+ default:
267
+ sendResponse({ error: "Unknown message type" });
268
+ }
269
+ });
270
+
271
+ // Funcion para procesar texto seleccionado (usando DOM APIs seguras)
272
+ function processSelection(text: string) {
273
+ // Crear tooltip usando DOM APIs seguras (evita innerHTML)
274
+ const existing = document.getElementById("my-extension-tooltip");
275
+ if (existing) existing.remove();
276
+
277
+ const tooltip = document.createElement("div");
278
+ tooltip.id = "my-extension-tooltip";
279
+ tooltip.style.cssText = `
280
+ position: fixed;
281
+ top: 20px;
282
+ right: 20px;
283
+ background: white;
284
+ border: 1px solid #ccc;
285
+ border-radius: 8px;
286
+ padding: 16px;
287
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
288
+ z-index: 999999;
289
+ max-width: 300px;
290
+ font-family: system-ui, -apple-system, sans-serif;
291
+ `;
292
+
293
+ const title = document.createElement("h4");
294
+ title.textContent = "My Extension";
295
+ title.style.cssText = "margin: 0 0 8px 0; font-size: 14px;";
296
+
297
+ const content = document.createElement("p");
298
+ const displayText = text.length > 100 ? text.substring(0, 100) + "..." : text;
299
+ content.textContent = "Seleccionaste: " + displayText;
300
+ content.style.cssText = "margin: 0; font-size: 13px; color: #666;";
301
+
302
+ const closeBtn = document.createElement("button");
303
+ closeBtn.textContent = "Cerrar";
304
+ closeBtn.style.cssText = `
305
+ margin-top: 12px;
306
+ padding: 6px 12px;
307
+ background: #007AFF;
308
+ color: white;
309
+ border: none;
310
+ border-radius: 4px;
311
+ cursor: pointer;
312
+ `;
313
+ closeBtn.addEventListener("click", () => tooltip.remove());
314
+
315
+ tooltip.appendChild(title);
316
+ tooltip.appendChild(content);
317
+ tooltip.appendChild(closeBtn);
318
+ document.body.appendChild(tooltip);
319
+
320
+ // Auto-cerrar despues de 5 segundos
321
+ setTimeout(() => tooltip.remove(), 5000);
322
+ }
323
+ ```
324
+
325
+ ### Paso 7: Crear Popup UI
326
+
327
+ Crear `src/popup/index.html`:
328
+
329
+ ```html
330
+ <!DOCTYPE html>
331
+ <html lang="es">
332
+ <head>
333
+ <meta charset="UTF-8">
334
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
335
+ <title>My Extension</title>
336
+ <link rel="stylesheet" href="styles.css">
337
+ </head>
338
+ <body>
339
+ <div class="container">
340
+ <header class="header">
341
+ <h1>My Extension</h1>
342
+ </header>
343
+
344
+ <main class="content">
345
+ <div class="toggle-row">
346
+ <span>Habilitado</span>
347
+ <label class="toggle">
348
+ <input type="checkbox" id="enabledToggle" checked>
349
+ <span class="slider"></span>
350
+ </label>
351
+ </div>
352
+
353
+ <div class="form-group">
354
+ <label for="input">Texto:</label>
355
+ <input type="text" id="input" placeholder="Escribe algo...">
356
+ </div>
357
+
358
+ <div class="buttons">
359
+ <button id="saveBtn" class="btn btn-primary">Guardar</button>
360
+ <button id="actionBtn" class="btn btn-secondary">Ejecutar</button>
361
+ </div>
362
+
363
+ <div id="status" class="status"></div>
364
+ </main>
365
+
366
+ <footer class="footer">
367
+ <a href="#" id="optionsLink">Configuracion</a>
368
+ </footer>
369
+ </div>
370
+
371
+ <script type="module" src="index.ts"></script>
372
+ </body>
373
+ </html>
374
+ ```
375
+
376
+ Crear `src/popup/styles.css`:
377
+
378
+ ```css
379
+ * { box-sizing: border-box; margin: 0; padding: 0; }
380
+
381
+ body {
382
+ width: 320px;
383
+ font-family: system-ui, -apple-system, sans-serif;
384
+ font-size: 14px;
385
+ }
386
+
387
+ .header {
388
+ padding: 16px;
389
+ background: linear-gradient(135deg, #667eea, #764ba2);
390
+ color: white;
391
+ }
392
+
393
+ .header h1 { font-size: 18px; }
394
+
395
+ .content { padding: 16px; }
396
+
397
+ .toggle-row {
398
+ display: flex;
399
+ justify-content: space-between;
400
+ margin-bottom: 16px;
401
+ padding-bottom: 16px;
402
+ border-bottom: 1px solid #eee;
403
+ }
404
+
405
+ .toggle {
406
+ position: relative;
407
+ width: 48px;
408
+ height: 26px;
409
+ }
410
+
411
+ .toggle input { opacity: 0; width: 0; height: 0; }
412
+
413
+ .slider {
414
+ position: absolute;
415
+ cursor: pointer;
416
+ inset: 0;
417
+ background: #ccc;
418
+ border-radius: 26px;
419
+ transition: 0.3s;
420
+ }
421
+
422
+ .slider:before {
423
+ position: absolute;
424
+ content: "";
425
+ height: 20px;
426
+ width: 20px;
427
+ left: 3px;
428
+ bottom: 3px;
429
+ background: white;
430
+ border-radius: 50%;
431
+ transition: 0.3s;
432
+ }
433
+
434
+ input:checked + .slider { background: #667eea; }
435
+ input:checked + .slider:before { transform: translateX(22px); }
436
+
437
+ .form-group { margin-bottom: 16px; }
438
+ .form-group label { display: block; margin-bottom: 6px; }
439
+ .form-group input {
440
+ width: 100%;
441
+ padding: 10px;
442
+ border: 1px solid #ddd;
443
+ border-radius: 6px;
444
+ }
445
+
446
+ .buttons { display: flex; gap: 8px; }
447
+
448
+ .btn {
449
+ flex: 1;
450
+ padding: 10px;
451
+ border: none;
452
+ border-radius: 6px;
453
+ cursor: pointer;
454
+ }
455
+
456
+ .btn-primary { background: #667eea; color: white; }
457
+ .btn-secondary { background: #f0f0f0; }
458
+
459
+ .status { margin-top: 12px; padding: 8px; border-radius: 6px; display: none; }
460
+ .status.success { display: block; background: #d4edda; color: #155724; }
461
+ .status.error { display: block; background: #f8d7da; color: #721c24; }
462
+
463
+ .footer { padding: 12px; background: #f9f9f9; text-align: center; }
464
+ .footer a { color: #667eea; text-decoration: none; }
465
+ ```
466
+
467
+ Crear `src/popup/index.ts`:
468
+
469
+ ```typescript
470
+ const input = document.getElementById("input") as HTMLInputElement;
471
+ const enabledToggle = document.getElementById("enabledToggle") as HTMLInputElement;
472
+ const saveBtn = document.getElementById("saveBtn") as HTMLButtonElement;
473
+ const actionBtn = document.getElementById("actionBtn") as HTMLButtonElement;
474
+ const status = document.getElementById("status") as HTMLDivElement;
475
+ const optionsLink = document.getElementById("optionsLink") as HTMLAnchorElement;
476
+
477
+ // Cargar estado inicial
478
+ document.addEventListener("DOMContentLoaded", async () => {
479
+ const result = await chrome.storage.local.get(["enabled", "savedText"]);
480
+ enabledToggle.checked = result.enabled !== false;
481
+ input.value = result.savedText || "";
482
+ });
483
+
484
+ // Toggle habilitado
485
+ enabledToggle.addEventListener("change", async () => {
486
+ await chrome.storage.local.set({ enabled: enabledToggle.checked });
487
+ showStatus(enabledToggle.checked ? "Habilitado" : "Deshabilitado", "success");
488
+ });
489
+
490
+ // Guardar
491
+ saveBtn.addEventListener("click", async () => {
492
+ const value = input.value.trim();
493
+ if (!value) {
494
+ showStatus("Escribe algo primero", "error");
495
+ return;
496
+ }
497
+ await chrome.storage.local.set({ savedText: value });
498
+ showStatus("Guardado", "success");
499
+ });
500
+
501
+ // Ejecutar
502
+ actionBtn.addEventListener("click", async () => {
503
+ try {
504
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
505
+ if (!tab?.id) {
506
+ showStatus("No hay tab activa", "error");
507
+ return;
508
+ }
509
+ const response = await chrome.tabs.sendMessage(tab.id, { type: "PING" });
510
+ showStatus("Pagina: " + response.url, "success");
511
+ } catch {
512
+ showStatus("Error de comunicacion", "error");
513
+ }
514
+ });
515
+
516
+ // Opciones
517
+ optionsLink.addEventListener("click", (e) => {
518
+ e.preventDefault();
519
+ chrome.runtime.openOptionsPage();
520
+ });
521
+
522
+ function showStatus(message: string, type: "success" | "error") {
523
+ status.textContent = message;
524
+ status.className = "status " + type;
525
+ setTimeout(() => { status.className = "status"; }, 3000);
526
+ }
527
+ ```
528
+
529
+ ### Paso 8: Scripts de package.json
530
+
531
+ ```json
532
+ {
533
+ "scripts": {
534
+ "dev": "vite",
535
+ "build": "vite build"
536
+ }
537
+ }
538
+ ```
539
+ </setup_steps>
540
+
541
+ <verification>
542
+ ## Verificacion
543
+
544
+ ### 1. Build
545
+ ```bash
546
+ npm run dev
547
+ ```
548
+
549
+ ### 2. Cargar en Chrome
550
+ 1. Abre `chrome://extensions/`
551
+ 2. Activa "Modo desarrollador"
552
+ 3. Click "Cargar extension sin empaquetar"
553
+ 4. Selecciona la carpeta `dist`
554
+
555
+ ### 3. Probar popup
556
+ - Click en el icono de la extension
557
+ - Escribe algo y guarda
558
+
559
+ ### 4. Probar content script
560
+ - Abre DevTools en cualquier pagina
561
+ - Deberia aparecer "Content script cargado"
562
+ </verification>
563
+
564
+ <publishing>
565
+ ## Publicar en Chrome Web Store
566
+
567
+ 1. Ir a https://chrome.google.com/webstore/devconsole
568
+ 2. Pagar fee de $5
569
+ 3. Crear ZIP: `cd dist && zip -r ../extension.zip .`
570
+ 4. Subir y completar listing
571
+ 5. Enviar para revision
572
+ </publishing>
573
+
574
+ <common_issues>
575
+ ## Problemas Comunes
576
+
577
+ ### "Service worker not found"
578
+ - Verificar path en manifest.json
579
+ - Rebuild y recargar extension
580
+
581
+ ### "Content script not running"
582
+ - Verificar "matches" en manifest
583
+ - Recargar pagina web
584
+ </common_issues>