chocolatito-code 1.0.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 (140) hide show
  1. package/.mcp.json.ejemplo +19 -0
  2. package/COMPUTER-USE.md +484 -0
  3. package/LICENSE +21 -0
  4. package/README.md +186 -0
  5. package/dist/agent/context.d.ts +9 -0
  6. package/dist/agent/context.js +123 -0
  7. package/dist/agent/fileTracker.d.ts +16 -0
  8. package/dist/agent/fileTracker.js +53 -0
  9. package/dist/agent/loop.d.ts +25 -0
  10. package/dist/agent/loop.js +524 -0
  11. package/dist/agent/mentions.d.ts +11 -0
  12. package/dist/agent/mentions.js +43 -0
  13. package/dist/agent/retry.d.ts +17 -0
  14. package/dist/agent/retry.js +57 -0
  15. package/dist/agent/subagent.d.ts +2 -0
  16. package/dist/agent/subagent.js +116 -0
  17. package/dist/agent/syntaxValidator.d.ts +5 -0
  18. package/dist/agent/syntaxValidator.js +71 -0
  19. package/dist/agent/tracker.d.ts +19 -0
  20. package/dist/agent/tracker.js +38 -0
  21. package/dist/agent/undoManager.d.ts +37 -0
  22. package/dist/agent/undoManager.js +95 -0
  23. package/dist/config/constants.d.ts +77 -0
  24. package/dist/config/constants.js +146 -0
  25. package/dist/config/engine.d.ts +49 -0
  26. package/dist/config/engine.js +86 -0
  27. package/dist/config/env.d.ts +13 -0
  28. package/dist/config/env.js +103 -0
  29. package/dist/config/license.d.ts +39 -0
  30. package/dist/config/license.js +306 -0
  31. package/dist/config/permissions.d.ts +38 -0
  32. package/dist/config/permissions.js +239 -0
  33. package/dist/config/quota.d.ts +58 -0
  34. package/dist/config/quota.js +156 -0
  35. package/dist/hooks/manager.d.ts +60 -0
  36. package/dist/hooks/manager.js +193 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +470 -0
  39. package/dist/mcp/client.d.ts +55 -0
  40. package/dist/mcp/client.js +282 -0
  41. package/dist/mcp/manager.d.ts +26 -0
  42. package/dist/mcp/manager.js +167 -0
  43. package/dist/memory/manager.d.ts +9 -0
  44. package/dist/memory/manager.js +59 -0
  45. package/dist/prompts/systemPrompt.d.ts +5 -0
  46. package/dist/prompts/systemPrompt.js +177 -0
  47. package/dist/sessions/manager.d.ts +21 -0
  48. package/dist/sessions/manager.js +54 -0
  49. package/dist/skills/manager.d.ts +14 -0
  50. package/dist/skills/manager.js +86 -0
  51. package/dist/tools/browserCdp.d.ts +26 -0
  52. package/dist/tools/browserCdp.js +610 -0
  53. package/dist/tools/browserExtension.d.ts +58 -0
  54. package/dist/tools/browserExtension.js +479 -0
  55. package/dist/tools/browserLive.d.ts +12 -0
  56. package/dist/tools/browserLive.js +265 -0
  57. package/dist/tools/changeDir.d.ts +5 -0
  58. package/dist/tools/changeDir.js +63 -0
  59. package/dist/tools/computerUse.d.ts +58 -0
  60. package/dist/tools/computerUse.js +567 -0
  61. package/dist/tools/createImage.d.ts +1 -0
  62. package/dist/tools/createImage.js +34 -0
  63. package/dist/tools/definitions.d.ts +8 -0
  64. package/dist/tools/definitions.js +401 -0
  65. package/dist/tools/deleteFile.d.ts +1 -0
  66. package/dist/tools/deleteFile.js +17 -0
  67. package/dist/tools/editFile.d.ts +5 -0
  68. package/dist/tools/editFile.js +88 -0
  69. package/dist/tools/findFiles.d.ts +1 -0
  70. package/dist/tools/findFiles.js +33 -0
  71. package/dist/tools/getSystemInfo.d.ts +1 -0
  72. package/dist/tools/getSystemInfo.js +23 -0
  73. package/dist/tools/gitTools.d.ts +3 -0
  74. package/dist/tools/gitTools.js +47 -0
  75. package/dist/tools/grepSearch.d.ts +1 -0
  76. package/dist/tools/grepSearch.js +37 -0
  77. package/dist/tools/listDir.d.ts +1 -0
  78. package/dist/tools/listDir.js +33 -0
  79. package/dist/tools/moveCopyFile.d.ts +1 -0
  80. package/dist/tools/moveCopyFile.js +21 -0
  81. package/dist/tools/runCommand.d.ts +1 -0
  82. package/dist/tools/runCommand.js +61 -0
  83. package/dist/tools/runner.d.ts +5 -0
  84. package/dist/tools/runner.js +177 -0
  85. package/dist/tools/safety.d.ts +17 -0
  86. package/dist/tools/safety.js +33 -0
  87. package/dist/tools/todoTool.d.ts +8 -0
  88. package/dist/tools/todoTool.js +27 -0
  89. package/dist/tools/toolDefsComputer.d.ts +13 -0
  90. package/dist/tools/toolDefsComputer.js +205 -0
  91. package/dist/tools/truncator.d.ts +11 -0
  92. package/dist/tools/truncator.js +24 -0
  93. package/dist/tools/viewFile.d.ts +1 -0
  94. package/dist/tools/viewFile.js +45 -0
  95. package/dist/tools/visionBridge.d.ts +37 -0
  96. package/dist/tools/visionBridge.js +107 -0
  97. package/dist/tools/webFetch.d.ts +1 -0
  98. package/dist/tools/webFetch.js +25 -0
  99. package/dist/tools/webSearch.d.ts +1 -0
  100. package/dist/tools/webSearch.js +35 -0
  101. package/dist/tools/win/hostClient.d.ts +31 -0
  102. package/dist/tools/win/hostClient.js +158 -0
  103. package/dist/tools/win/hostScript.d.ts +1 -0
  104. package/dist/tools/win/hostScript.js +925 -0
  105. package/dist/tools/writeFile.d.ts +1 -0
  106. package/dist/tools/writeFile.js +13 -0
  107. package/dist/ui/banner.d.ts +10 -0
  108. package/dist/ui/banner.js +165 -0
  109. package/dist/ui/diffViewer.d.ts +1 -0
  110. package/dist/ui/diffViewer.js +60 -0
  111. package/dist/ui/interrupt.d.ts +17 -0
  112. package/dist/ui/interrupt.js +156 -0
  113. package/dist/ui/markdownStream.d.ts +23 -0
  114. package/dist/ui/markdownStream.js +0 -0
  115. package/dist/ui/modes.d.ts +28 -0
  116. package/dist/ui/modes.js +57 -0
  117. package/dist/ui/permissionPrompt.d.ts +35 -0
  118. package/dist/ui/permissionPrompt.js +271 -0
  119. package/dist/ui/prompt.d.ts +7 -0
  120. package/dist/ui/prompt.js +466 -0
  121. package/dist/ui/reasoningStream.d.ts +6 -0
  122. package/dist/ui/reasoningStream.js +38 -0
  123. package/dist/ui/renderer.d.ts +10 -0
  124. package/dist/ui/renderer.js +142 -0
  125. package/dist/ui/spinner.d.ts +24 -0
  126. package/dist/ui/spinner.js +111 -0
  127. package/dist/ui/theme.d.ts +36 -0
  128. package/dist/ui/theme.js +37 -0
  129. package/dist/ui/typeAhead.d.ts +31 -0
  130. package/dist/ui/typeAhead.js +87 -0
  131. package/extension/README.md +74 -0
  132. package/extension/background.js +575 -0
  133. package/extension/content.js +446 -0
  134. package/extension/manifest.json +36 -0
  135. package/extension/popup.html +22 -0
  136. package/extension/popup.js +12 -0
  137. package/package.json +66 -0
  138. package/skills/complex-refactor/SKILL.md +13 -0
  139. package/skills/deep-debugger/SKILL.md +14 -0
  140. package/skills/software-architect/SKILL.md +20 -0
@@ -0,0 +1,446 @@
1
+ /**
2
+ * Script de contenido de Chocolatito Code.
3
+ *
4
+ * Corre DENTRO de la pagina, asi que no necesita foco, ni raton, ni que la
5
+ * ventana este delante. Es lo que permite que el agente trabaje mientras el
6
+ * usuario esta jugando o en otra aplicacion.
7
+ *
8
+ * Responsabilidades:
9
+ * - construir el snapshot de elementos accionables con referencias
10
+ * - pulsar y escribir de forma que los frameworks (React) SI se enteren
11
+ * - pintar el aviso flotante que indica que se esta trabajando aqui
12
+ */
13
+
14
+ (() => {
15
+ if (window.__chocoContentReady) return;
16
+ window.__chocoContentReady = true;
17
+
18
+ const IS_TOP = window.top === window;
19
+
20
+ // ======================================================== aviso en pagina
21
+
22
+ let bannerHost = null;
23
+ let bannerBody = null;
24
+ let bannerHideTimer = null;
25
+
26
+ function ensureBanner() {
27
+ if (!IS_TOP) return null;
28
+ if (bannerHost && document.documentElement.contains(bannerHost)) return bannerHost;
29
+
30
+ bannerHost = document.createElement("div");
31
+ bannerHost.id = "__chocolatito_banner__";
32
+ // Shadow DOM: los estilos de la pagina no pueden romper el aviso, ni el
33
+ // aviso ensuciar la pagina.
34
+ const shadow = bannerHost.attachShadow({ mode: "closed" });
35
+ const style = document.createElement("style");
36
+ style.textContent = `
37
+ .wrap {
38
+ position: fixed; right: 16px; bottom: 16px; z-index: 2147483647;
39
+ display: flex; align-items: center; gap: 10px;
40
+ max-width: 380px; padding: 10px 14px;
41
+ background: rgba(24, 18, 16, 0.94);
42
+ border: 1px solid rgba(217, 119, 87, 0.55);
43
+ border-left: 3px solid #D97757;
44
+ border-radius: 10px;
45
+ box-shadow: 0 6px 24px rgba(0,0,0,0.38);
46
+ font: 13px/1.4 -apple-system, "Segoe UI", system-ui, sans-serif;
47
+ color: #F3EAE4;
48
+ pointer-events: none;
49
+ transition: opacity .25s ease, transform .25s ease;
50
+ }
51
+ .wrap.hide { opacity: 0; transform: translateY(6px); }
52
+ .dot {
53
+ width: 9px; height: 9px; border-radius: 50%;
54
+ background: #D97757; flex: 0 0 auto;
55
+ animation: pulse 1.15s ease-in-out infinite;
56
+ }
57
+ @keyframes pulse { 0%,100% { opacity:1; transform:scale(1);} 50% { opacity:.35; transform:scale(.75);} }
58
+ .txt { min-width: 0; }
59
+ .title { font-weight: 600; letter-spacing: .2px; }
60
+ .action {
61
+ color: #C9B8AF; font-size: 12px; margin-top: 1px;
62
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
63
+ }
64
+ @media (prefers-reduced-motion: reduce) { .dot { animation: none; } }
65
+ `;
66
+ const wrap = document.createElement("div");
67
+ wrap.className = "wrap";
68
+ wrap.innerHTML =
69
+ '<div class="dot"></div><div class="txt">' +
70
+ '<div class="title">Chocolatito Code trabajando aqui</div>' +
71
+ '<div class="action"></div></div>';
72
+ shadow.appendChild(style);
73
+ shadow.appendChild(wrap);
74
+ bannerBody = { wrap, action: wrap.querySelector(".action") };
75
+ (document.body || document.documentElement).appendChild(bannerHost);
76
+ return bannerHost;
77
+ }
78
+
79
+ function showBanner(text) {
80
+ if (!IS_TOP) return;
81
+ try {
82
+ ensureBanner();
83
+ if (!bannerBody) return;
84
+ bannerBody.action.textContent = text || "";
85
+ bannerBody.wrap.classList.remove("hide");
86
+ clearTimeout(bannerHideTimer);
87
+ // Se desvanece solo si el agente deja de trabajar, para no molestar.
88
+ bannerHideTimer = setTimeout(() => {
89
+ if (bannerBody) bannerBody.wrap.classList.add("hide");
90
+ }, 12000);
91
+ } catch (_) {}
92
+ }
93
+
94
+ function hideBanner() {
95
+ if (bannerHost && bannerHost.parentNode) bannerHost.parentNode.removeChild(bannerHost);
96
+ bannerHost = null;
97
+ bannerBody = null;
98
+ }
99
+
100
+ // ======================================================== snapshot
101
+
102
+ const SELECTOR = [
103
+ "a[href]", "button", "input", "textarea", "select", "summary",
104
+ '[role="button"]', '[role="link"]', '[role="textbox"]', '[role="tab"]',
105
+ '[role="menuitem"]', '[role="option"]', '[role="checkbox"]', '[role="radio"]',
106
+ '[role="combobox"]', '[role="switch"]', '[contenteditable="true"]', "[onclick]",
107
+ ].join(",");
108
+
109
+ function accessibleName(el) {
110
+ const aria = el.getAttribute("aria-label");
111
+ if (aria && aria.trim()) return aria.trim();
112
+ const by = el.getAttribute("aria-labelledby");
113
+ if (by) {
114
+ const l = document.getElementById(by);
115
+ if (l && l.textContent.trim()) return l.textContent.trim();
116
+ }
117
+ if (el.placeholder && el.placeholder.trim()) return el.placeholder.trim();
118
+ const title = el.getAttribute("title");
119
+ if (title && title.trim()) return title.trim();
120
+ if (el.tagName === "IMG" && el.alt) return el.alt.trim();
121
+ const txt = el.innerText || el.textContent || "";
122
+ return txt.replace(/\s+/g, " ").trim();
123
+ }
124
+
125
+ function roleOf(el) {
126
+ const r = el.getAttribute("role");
127
+ if (r) return r;
128
+ const tag = el.tagName.toLowerCase();
129
+ if (tag === "a") return "link";
130
+ if (tag === "button") return "button";
131
+ if (tag === "textarea") return "textbox";
132
+ if (tag === "select") return "combobox";
133
+ if (tag === "input") {
134
+ const t = (el.type || "text").toLowerCase();
135
+ if (t === "checkbox" || t === "radio") return t;
136
+ if (t === "submit" || t === "button") return "button";
137
+ return "textbox";
138
+ }
139
+ if (el.isContentEditable) return "textbox";
140
+ return tag;
141
+ }
142
+
143
+ /** Recoge todas las raices, atravesando shadow DOM. */
144
+ function allRoots() {
145
+ const roots = [document];
146
+ for (let i = 0; i < roots.length && i < 400; i++) {
147
+ const all = roots[i].querySelectorAll("*");
148
+ for (let j = 0; j < all.length; j++) {
149
+ if (all[j].shadowRoot) roots.push(all[j].shadowRoot);
150
+ }
151
+ }
152
+ return roots;
153
+ }
154
+
155
+ function norm(s) {
156
+ return (s || "")
157
+ .normalize("NFD")
158
+ .replace(/[̀-ͯ]/g, "")
159
+ .toLowerCase();
160
+ }
161
+
162
+ function buildSnapshot(startIndex, filter) {
163
+ if (startIndex === 0) refIdentity.clear();
164
+ const out = [];
165
+ let idx = startIndex;
166
+ const f = norm(filter);
167
+ const seen = new Set();
168
+
169
+ for (const root of allRoots()) {
170
+ const nodes = root.querySelectorAll(SELECTOR);
171
+ for (const el of nodes) {
172
+ if (seen.has(el)) continue;
173
+ seen.add(el);
174
+
175
+ const rect = el.getBoundingClientRect();
176
+ if (rect.width < 2 || rect.height < 2) continue;
177
+
178
+ const st = window.getComputedStyle(el);
179
+ if (st.visibility === "hidden" || st.display === "none" || Number(st.opacity) === 0) continue;
180
+ if (rect.bottom < 0 || rect.right < 0) continue;
181
+
182
+ const name = accessibleName(el).slice(0, 120);
183
+ const role = roleOf(el);
184
+ const value = el.value ? String(el.value).slice(0, 80) : "";
185
+ if (!name && !value && role !== "textbox") continue;
186
+
187
+ if (f && norm(name + " " + value + " " + role).indexOf(f) < 0) continue;
188
+
189
+ idx++;
190
+ el.setAttribute("data-choco-ref", String(idx));
191
+ refIdentity.set(idx, { role, name, idx: out.length });
192
+ out.push({
193
+ ref: idx,
194
+ role,
195
+ name,
196
+ value,
197
+ disabled: el.disabled === true || el.getAttribute("aria-disabled") === "true",
198
+ inView: rect.top >= 0 && rect.top < window.innerHeight,
199
+ });
200
+ }
201
+ }
202
+ return out;
203
+ }
204
+
205
+ /**
206
+ * Identidad de cada ref del ultimo snapshot: ref -> { role, name, idx }.
207
+ *
208
+ * Hace falta porque las apps que re-renderizan (Flow lo hace cada pocos
209
+ * segundos) destruyen el nodo y con el el atributo data-choco-ref. El ref
210
+ * quedaba invalido a los pocos segundos y la accion fallaba con "ref no
211
+ * encontrado" aunque el boton siguiera ahi, con el mismo nombre y en el mismo
212
+ * sitio. Guardando la identidad se puede volver a localizar.
213
+ */
214
+ const refIdentity = new Map();
215
+
216
+ function findByRef(ref) {
217
+ for (const root of allRoots()) {
218
+ const el = root.querySelector('[data-choco-ref="' + ref + '"]');
219
+ if (el) return el;
220
+ }
221
+
222
+ // El nodo se fue en un re-render: se busca otro con la misma identidad.
223
+ const id = refIdentity.get(ref);
224
+ if (!id) return null;
225
+
226
+ const candidatos = [];
227
+ for (const root of allRoots()) {
228
+ const nodes = root.querySelectorAll(SELECTOR);
229
+ for (const el of nodes) {
230
+ if (roleOf(el) !== id.role) continue;
231
+ if (accessibleName(el).slice(0, 120) !== id.name) continue;
232
+ const r = el.getBoundingClientRect();
233
+ if (r.width < 2 || r.height < 2) continue;
234
+ candidatos.push(el);
235
+ }
236
+ }
237
+ if (candidatos.length === 0) return null;
238
+
239
+ // Con varios iguales se elige el que ocupaba la misma posicion en la lista.
240
+ const elegido = candidatos[Math.min(id.idx, candidatos.length - 1)];
241
+ elegido.setAttribute("data-choco-ref", String(ref));
242
+ return elegido;
243
+ }
244
+
245
+ // ======================================================== acciones
246
+
247
+ function centerOf(el) {
248
+ const r = el.getBoundingClientRect();
249
+ return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
250
+ }
251
+
252
+ /**
253
+ * Pulsa sin raton real. Se emite la secuencia completa de eventos porque
254
+ * muchos componentes escuchan pointerdown/mouseup y no solo click.
255
+ */
256
+ function clickElement(el) {
257
+ try {
258
+ el.scrollIntoView({ block: "center", inline: "center", behavior: "instant" });
259
+ } catch (_) {
260
+ try { el.scrollIntoView(); } catch (_) {}
261
+ }
262
+ const c = centerOf(el);
263
+ const base = { bubbles: true, cancelable: true, composed: true, view: window, clientX: c.x, clientY: c.y, button: 0 };
264
+ const ptr = Object.assign({ pointerId: 1, isPrimary: true, pointerType: "mouse" }, base);
265
+
266
+ el.dispatchEvent(new PointerEvent("pointerover", ptr));
267
+ el.dispatchEvent(new MouseEvent("mouseover", base));
268
+ el.dispatchEvent(new PointerEvent("pointermove", ptr));
269
+ el.dispatchEvent(new MouseEvent("mousemove", base));
270
+ el.dispatchEvent(new PointerEvent("pointerdown", ptr));
271
+ el.dispatchEvent(new MouseEvent("mousedown", base));
272
+ try { el.focus({ preventScroll: true }); } catch (_) {}
273
+ el.dispatchEvent(new PointerEvent("pointerup", ptr));
274
+ el.dispatchEvent(new MouseEvent("mouseup", base));
275
+ el.dispatchEvent(new MouseEvent("click", base));
276
+ return true;
277
+ }
278
+
279
+ /**
280
+ * Escribe de forma que React se entere.
281
+ *
282
+ * Asignar el.value directamente NO sirve: React guarda su propio estado y no
283
+ * ve el cambio, asi que el campo parece relleno pero el boton de enviar sigue
284
+ * deshabilitado. El truco es llamar al setter nativo del prototipo y despues
285
+ * emitir el evento 'input', que es lo que React escucha.
286
+ */
287
+ function setNativeValue(el, value) {
288
+ const proto =
289
+ el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype :
290
+ el instanceof HTMLInputElement ? HTMLInputElement.prototype : null;
291
+ if (proto) {
292
+ const desc = Object.getOwnPropertyDescriptor(proto, "value");
293
+ if (desc && desc.set) { desc.set.call(el, value); return true; }
294
+ }
295
+ return false;
296
+ }
297
+
298
+ function typeInto(el, text, replace) {
299
+ try { el.scrollIntoView({ block: "center", behavior: "instant" }); } catch (_) {}
300
+ try { el.focus({ preventScroll: true }); } catch (_) {}
301
+
302
+ if (el.isContentEditable) {
303
+ if (replace) {
304
+ const sel = window.getSelection();
305
+ const range = document.createRange();
306
+ range.selectNodeContents(el);
307
+ sel.removeAllRanges();
308
+ sel.addRange(range);
309
+ }
310
+ // execCommand emite beforeinput/input igual que si se hubiera tecleado.
311
+ const ok = document.execCommand("insertText", false, text);
312
+ if (!ok) {
313
+ el.textContent = replace ? text : (el.textContent || "") + text;
314
+ el.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true, data: text }));
315
+ }
316
+ return true;
317
+ }
318
+
319
+ const next = replace ? text : (el.value || "") + text;
320
+ el.dispatchEvent(new InputEvent("beforeinput", { bubbles: true, composed: true, data: text }));
321
+ if (!setNativeValue(el, next)) el.value = next;
322
+ el.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true, data: text }));
323
+ el.dispatchEvent(new Event("change", { bubbles: true }));
324
+ return true;
325
+ }
326
+
327
+ function pressKey(el, key) {
328
+ const target = el || document.activeElement || document.body;
329
+ const map = { Enter: 13, Escape: 27, Tab: 9, Backspace: 8, Delete: 46, ArrowUp: 38, ArrowDown: 40 };
330
+ const code = map[key] || 0;
331
+ const init = { key, code: key, keyCode: code, which: code, bubbles: true, cancelable: true, composed: true };
332
+ target.dispatchEvent(new KeyboardEvent("keydown", init));
333
+ target.dispatchEvent(new KeyboardEvent("keypress", init));
334
+ target.dispatchEvent(new KeyboardEvent("keyup", init));
335
+ // Si es un formulario y la tecla es Enter, se intenta enviar de verdad.
336
+ if (key === "Enter" && target.form && typeof target.form.requestSubmit === "function") {
337
+ try { target.form.requestSubmit(); } catch (_) {}
338
+ }
339
+ return true;
340
+ }
341
+
342
+ // ======================================================== mensajes
343
+
344
+ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
345
+ try {
346
+ switch (msg.cmd) {
347
+ case "ping":
348
+ sendResponse({ ok: true, top: IS_TOP, url: location.href });
349
+ break;
350
+
351
+ case "banner":
352
+ if (msg.text === null) hideBanner();
353
+ else showBanner(msg.text);
354
+ sendResponse({ ok: true });
355
+ break;
356
+
357
+ case "snapshot": {
358
+ const items = buildSnapshot(msg.startIndex || 0, msg.filter || "");
359
+ sendResponse({ ok: true, items, url: location.href, title: document.title, top: IS_TOP });
360
+ break;
361
+ }
362
+
363
+ case "click": {
364
+ const el = findByRef(msg.ref);
365
+ if (!el) { sendResponse({ ok: false, error: "ref no encontrado en este frame" }); break; }
366
+ const label = accessibleName(el).slice(0, 60);
367
+ clickElement(el);
368
+ sendResponse({ ok: true, label });
369
+ break;
370
+ }
371
+
372
+ case "type": {
373
+ const el = findByRef(msg.ref);
374
+ if (!el) { sendResponse({ ok: false, error: "ref no encontrado en este frame" }); break; }
375
+ typeInto(el, msg.text, msg.replace !== false);
376
+ // Se devuelve el valor real tras escribir, para poder verificar.
377
+ const after = el.isContentEditable ? (el.textContent || "") : (el.value || "");
378
+ if (msg.submit) pressKey(el, "Enter");
379
+ sendResponse({ ok: true, value: String(after).slice(0, 200) });
380
+ break;
381
+ }
382
+
383
+ // Devuelve el centro del elemento en coordenadas del viewport, que es lo
384
+ // que necesita el protocolo DevTools para entregar un clic real.
385
+ case "rect": {
386
+ const el = findByRef(msg.ref);
387
+ if (!el) { sendResponse({ ok: false, error: "ref no encontrado en este frame" }); break; }
388
+ try { el.scrollIntoView({ block: "center", behavior: "instant" }); } catch (_) {}
389
+ const r = el.getBoundingClientRect();
390
+ sendResponse({
391
+ ok: true,
392
+ x: Math.round(r.left + r.width / 2),
393
+ y: Math.round(r.top + r.height / 2),
394
+ label: accessibleName(el).slice(0, 60),
395
+ editable: el.isContentEditable || "value" in el,
396
+ });
397
+ break;
398
+ }
399
+
400
+ // Solo lee el valor actual, para verificar despues de escribir.
401
+ case "value": {
402
+ const el = findByRef(msg.ref);
403
+ if (!el) { sendResponse({ ok: false, error: "ref no encontrado" }); break; }
404
+ sendResponse({
405
+ ok: true,
406
+ value: String(el.isContentEditable ? el.textContent || "" : el.value || "").slice(0, 300),
407
+ });
408
+ break;
409
+ }
410
+
411
+ case "press": {
412
+ const el = msg.ref ? findByRef(msg.ref) : null;
413
+ pressKey(el, msg.keys || "Enter");
414
+ sendResponse({ ok: true });
415
+ break;
416
+ }
417
+
418
+ case "getText":
419
+ sendResponse({
420
+ ok: true,
421
+ title: document.title,
422
+ url: location.href,
423
+ text: (document.body ? document.body.innerText : "").slice(0, 8000),
424
+ });
425
+ break;
426
+
427
+ case "eval": {
428
+ let out;
429
+ try {
430
+ out = String(eval(msg.script));
431
+ } catch (e) {
432
+ out = "Error JS: " + (e && e.message ? e.message : e);
433
+ }
434
+ sendResponse({ ok: true, result: out });
435
+ break;
436
+ }
437
+
438
+ default:
439
+ sendResponse({ ok: false, error: "comando desconocido: " + msg.cmd });
440
+ }
441
+ } catch (err) {
442
+ sendResponse({ ok: false, error: String((err && err.message) || err) });
443
+ }
444
+ return true;
445
+ });
446
+ })();
@@ -0,0 +1,36 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "Chocolatito Code",
4
+ "version": "1.0.0",
5
+ "description": "Deja que Chocolatito Code trabaje dentro de tu Chrome, en segundo plano y con tus sesiones ya iniciadas.",
6
+ "permissions": [
7
+ "tabs",
8
+ "tabGroups",
9
+ "scripting",
10
+ "debugger",
11
+ "webNavigation",
12
+ "storage"
13
+ ],
14
+ "host_permissions": [
15
+ "<all_urls>"
16
+ ],
17
+ "background": {
18
+ "service_worker": "background.js"
19
+ },
20
+ "content_scripts": [
21
+ {
22
+ "matches": [
23
+ "<all_urls>"
24
+ ],
25
+ "js": [
26
+ "content.js"
27
+ ],
28
+ "run_at": "document_idle",
29
+ "all_frames": true
30
+ }
31
+ ],
32
+ "action": {
33
+ "default_title": "Chocolatito Code",
34
+ "default_popup": "popup.html"
35
+ }
36
+ }
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <meta charset="utf-8" />
3
+ <style>
4
+ body {
5
+ margin: 0;
6
+ width: 260px;
7
+ padding: 14px;
8
+ font: 13px/1.45 -apple-system, "Segoe UI", system-ui, sans-serif;
9
+ background: #191211;
10
+ color: #F3EAE4;
11
+ }
12
+ h1 { margin: 0 0 10px; font-size: 14px; letter-spacing: .2px; }
13
+ .row { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
14
+ .dot { width: 9px; height: 9px; border-radius: 50%; background: #6B5B54; }
15
+ .dot.on { background: #4ADE80; }
16
+ .muted { color: #B5A49C; font-size: 12px; }
17
+ code { background: #2A1F1C; padding: 1px 5px; border-radius: 4px; font-size: 11px; }
18
+ </style>
19
+ <h1>Chocolatito Code</h1>
20
+ <div class="row"><span class="dot" id="dot"></span><span id="state">Comprobando…</span></div>
21
+ <div class="row muted" id="hint"></div>
22
+ <script src="popup.js"></script>
@@ -0,0 +1,12 @@
1
+ // Estado de la conexion con la CLI. El badge lo pone el service worker; aqui
2
+ // solo se traduce a algo legible para el usuario.
3
+ chrome.action.getBadgeText({}, (text) => {
4
+ const on = text === "on";
5
+ document.getElementById("dot").className = "dot" + (on ? " on" : "");
6
+ document.getElementById("state").textContent = on
7
+ ? "Conectado a la CLI"
8
+ : "Sin conexion con la CLI";
9
+ document.getElementById("hint").innerHTML = on
10
+ ? "El agente puede trabajar en tus pestanas en segundo plano."
11
+ : 'Arranca <code>chocolatito</code> en la terminal y esta pestana se conectara sola.';
12
+ });
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "chocolatito-code",
3
+ "version": "1.0.0",
4
+ "description": "Agente autónomo de programación para la terminal, con control real del ordenador y del navegador. Desarrollado por Chocolatito.",
5
+ "type": "module",
6
+ "bin": {
7
+ "chocolatito": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "extension",
12
+ "skills",
13
+ "LICENSE",
14
+ "README.md",
15
+ "COMPUTER-USE.md",
16
+ ".mcp.json.ejemplo"
17
+ ],
18
+ "engines": {
19
+ "node": ">=20.0.0"
20
+ },
21
+ "os": [
22
+ "win32",
23
+ "darwin",
24
+ "linux"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "dev": "tsc --watch",
29
+ "start": "node ./dist/index.js",
30
+ "test": "node --test tests/*.test.mjs",
31
+ "prepublishOnly": "npm run build && npm test"
32
+ },
33
+ "keywords": [
34
+ "ai",
35
+ "cli",
36
+ "coding-agent",
37
+ "computer-use",
38
+ "browser-automation",
39
+ "chocolatito",
40
+ "espectro-engine"
41
+ ],
42
+ "author": "Chocolatito",
43
+ "license": "MIT",
44
+ "homepage": "https://chocolatito.space",
45
+ "dependencies": {
46
+ "@inquirer/prompts": "^7.2.1",
47
+ "boxen": "^8.0.1",
48
+ "chalk": "^5.4.1",
49
+ "diff": "^7.0.0",
50
+ "dotenv": "^16.4.7",
51
+ "execa": "^9.5.2",
52
+ "fast-glob": "^3.3.3",
53
+ "marked": "^15.0.12",
54
+ "marked-terminal": "^7.3.0",
55
+ "openai": "^4.85.4",
56
+ "ora": "^8.2.0",
57
+ "puppeteer-core": "^25.9.0",
58
+ "ws": "^8.21.3"
59
+ },
60
+ "devDependencies": {
61
+ "@types/diff": "^7.0.0",
62
+ "@types/node": "^22.13.5",
63
+ "@types/ws": "^8.18.1",
64
+ "typescript": "^5.7.3"
65
+ }
66
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: complex-refactor
3
+ description: Guía para refactorización de gran escala, modernización de código legado y migración segura de arquitecturas sin romper funcionalidad.
4
+ ---
5
+
6
+ # ⚡ Skill: Complex Refactoring & Modernization
7
+
8
+ Usa este skill cuando reorganices módulos, migres a TypeScript estricto o desacoples sistemas.
9
+
10
+ ## 1. Reglas de Oro del Refactoring Seguro
11
+ - **Tests como Red de Seguridad**: Asegúrate de tener pruebas antes de mover código.
12
+ - **Pasos Atómicos**: Haz cambios pequeños y verifica la compilación tras cada uno.
13
+ - **Preservación de Comportamiento**: No cambies lógica y arquitectura al mismo tiempo.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: deep-debugger
3
+ description: Metodología de depuración científica paso a paso para diagnosticar y solucionar bugs complejos, memory leaks y condiciones de carrera.
4
+ ---
5
+
6
+ # 🔬 Skill: Deep Debugging & Root Cause Analysis
7
+
8
+ Usa este skill para resolver errores esquivos, excepciones intermitentes o fallos de compilación.
9
+
10
+ ## 1. Protocolo Científico de Depuración en 4 Pasos
11
+ 1. **Reproducción Sistemática**: Ejecuta el test o comando para ver el error exacto.
12
+ 2. **Aislamiento y Trazado**: Localiza la línea precisa del stack trace y rastrea el flujo de datos.
13
+ 3. **Corrección Quirúrgica**: Modifica solo la causa raíz sin alterar contratos colaterales.
14
+ 4. **Verificación de Regresión**: Ejecuta la suite de tests completa para asegurar que no se introdujeron nuevos fallos.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: software-architect
3
+ description: Guía experta para diseño de sistemas complejos, patrones de diseño (SOLID, DDD, Clean Architecture), APIs y bases de datos escalables.
4
+ ---
5
+
6
+ # 🏛️ Skill: Software Architect & Systems Engineering
7
+
8
+ Usa este skill cuando diseñes o crees software complejo desde cero o planees arquitecturas de gran escala.
9
+
10
+ ## 1. Principios Fundamentales de Arquitectura
11
+ - **Clean Architecture & Hexagonal**: Separa la lógica de negocio pura de los adaptadores externos.
12
+ - **SOLID & DRY**: Alta cohesión, bajo acoplamiento e interfaces explícitas.
13
+ - **Modelado de Tipos**: Modela tipos e interfaces antes de implementar la lógica.
14
+
15
+ ## 2. Metodología de Creación de Software Complejo
16
+ 1. **Definir el Dominio**: Interfaces, contratos de datos y tipos inmutables.
17
+ 2. **Capa de Servicios**: Casos de uso desacoplados.
18
+ 3. **Capa de Infraestructura**: Adaptadores de persistencia y red.
19
+ 4. **Capa de Presentación / API / CLI**: Controladores y comandos.
20
+ 5. **Estrategia de Tests**: Diseñar para testabilidad con pruebas unitarias y de integración.