¿Qué hace Auditoría de iconos Koobiq en Figma?
Esta habilidad verifica que un componente de icono en el archivo Figma de iconos Koobiq siga las
convenciones estructurales de la biblioteca, para que figma:sync (el pipeline @figma-export) produzca un
SVG limpio y correcto. Detectar problemas en Figma es mucho más barato que descubrirlos después de la exportación, porque
la exportación transforma silenciosamente lo que encuentra — un nombre de capa incorrecto, un nodo extra, o una
coordenada fuera de la cuadrícula se convierte en un SVG roto sin ningún error.
Es una auditoría de solo lectura por defecto: inspecciona y luego reporta una lista de verificación clara de aprobado/fallo con hallazgos específicos y procesables. Solo modifica el archivo Figma si el usuario te pide explícitamente que arregles algo (ver "Ofrecer correcciones").
Prerrequisitos
-
El MCP de Figma debe estar conectado. Esta habilidad lo utiliza a través de
get_metadata(resumen de la estructura),use_figma(inspección precisa mediante la API de Plugin) yget_screenshot(comprobación visual de cordura). -
Antes de cualquier llamada a
use_figma, carga la habilidadfigma-use— documenta las reglas de la API de Plugin y previene fallos comunes difíciles de depurar. -
La clave del archivo y las páginas de tamaño están fijas (verificadas contra el documento activo). Para saltarte un viaje de ida y vuelta de descubrimiento de páginas, salta directamente a una página por su id de nodo:
tamaño id de nodo de página 16 1320:1738724 42149:86032 42149:86248 42149:86164 42149:863Clave del archivo:
WvUarBj8tO256vML4PkBLl. Cada nombre de página es el sufijo de tamaño; un icono llamadofoo_24vive en la página24. Documento: https://www.figma.com/design/WvUarBj8tO256vML4PkBLl/%F0%9F%9F%A5-koobiq-%C2%B7-icons?node-id=1320-17387- Esta clave de archivo es la fuente de verdad — coincide con
config.mjs. El enlace "Draw an icon" enpackages/icons/README.mdapunta a un archivo diferente e incorrecto — ignóralo y confía en la clave anterior. get_metadatasin unnodeIdsolo lista la primera página (ICONS, que no contiene componentes de icono) — no enumera las páginas de tamaño. No lo uses para descubrir páginas; usa los ids de nodo anteriores.
- Esta clave de archivo es la fuente de verdad — coincide con
Si no se puede acceder al archivo
Si una llamada a Figma falla — error de autenticación/permiso, "el nodo no existe", un archivo vacío o inesperado, o el MCP no responde — detente y comunícalo al usuario en lugar de adivinar. Dile qué falló y pídele que haga una de las siguientes acciones:
- proporcionar un enlace de Figma diferente o correcto (la clave del archivo en
config.mjspuede estar desactualizada, o pegaron un enlace a otro archivo o a una rama), o - volver a verificar el
FIGMA_TOKENen el.envdel repositorio (el token de accesofigma-export/ MCP puede faltar, haber caducado o haber sido revocado), o - confirmar que su cuenta de Figma tiene acceso al archivo (es un archivo de diseño privado — es posible que necesiten ser invitados o usar un token vinculado a una cuenta con permiso).
No fabriques un resultado de auditoría a partir de suposiciones; una comprobación solo tiene sentido contra el archivo activo.
Lecturas en paralelo y contexto de página
Las llamadas de lectura de Figma pueden ejecutarse en paralelo, pero solo antes de la primera edición de Figma de la tarea. Después de que haya comenzado cualquier edición, ejecuta las llamadas de Figma una a la vez.
Las lecturas en paralelo que cambian de página pueden interferir entre sí. En una ejecución observada, una lectura que apuntaba a la página 24 devolvió datos de la página 16 (el mismo recuento de componentes, nombres incorrectos), y la auditoría reportó las variantes de 24px como faltantes. Protege cada lectura que cambie de página:
// Úsalo en cada lectura que cambie de página. Devuelve datos solo para los nodos que pertenecen a PAGE_ID.
const PAGE_ID = "42149:860"; // id de nodo de página de la tabla anterior
const NAMES = ["foo_24"]; // nombres de componentes a buscar
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("desajuste de contexto de página antes de la lectura");
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const hits = figma.currentPage
.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => NAMES.includes(n.name) && pageOf(n) && pageOf(n).id === PAGE_ID)
.map((n) => ({ name: n.name, id: n.id, w: n.width, h: n.height }));
if (figma.currentPage.id !== PAGE_ID) throw new Error("el contexto de página cambió durante la lectura");
return { pageId: PAGE_ID, hits };
Si un guardia lanza una excepción, descarta ese resultado y vuelve a ejecutar la lectura. Una afirmación de "falta" (por ejemplo, "no hay variante de 24px") debe basarse en una lectura protegida de esa página, o en una búsqueda por id de nodo. Nunca reportes ausencia a partir de una lectura que lanzó una excepción o de una página que no verificaste.
Paso 1 — Resolver el objetivo
El usuario puede darte cualquiera de los siguientes:
- Una URL o id de nodo de Figma (ej.
node-id=44731-6643) → inspecciona exactamente ese nodo, y luego también busca sus variantes de tamaño hermanas por nombre para la comprobación cruzada de variantes. - Un nombre de icono (ej.
floating-window, ofloating-window_16) → encuentra cada variante de tamaño en las páginas de iconos y verifícalas todas. Los iconos nuevos a menudo no están publicados e incluso pueden no tener nombre, así que si una coincidencia de nombre falla, lista los componentes en las páginas relevantes y pide al usuario que señale el nodo, o inspecciona por id de nodo.
Los nombres de página son iguales al sufijo de tamaño. Barrido de tamaño: verifica las cinco páginas (16 / 24 / 32 / 48 / 64) para
cada nombre solicitado, porque un icono puede existir en tamaños que el usuario no mencionó. Usa los ids de nodo de página
de la tabla de Prerrequisitos, una llamada use_figma por página, cada una con el guardia de "Lecturas en paralelo y contexto de página". Ejecútalas en paralelo solo antes de cualquier edición de Figma. Reporta el resultado como una tabla
(nombre × tamaño → id de nodo o falta).
Paso 2 — Inspeccionar
Ejecuta este script de solo lectura (adapta los targets/id de página). Devuelve todo lo que las reglas a continuación necesitan.
Acompáñalo con un get_screenshot para que puedas confirmar que el glifo realmente se renderiza.
// Pégalo en use_figma (skillNames: "figma-use"). Establece PAGE_ID y NAMES.
// Ids de nodo de página: 16=1320:17387 24=42149:860 32=42149:862 48=42149:861 64=42149:863
const PAGE_ID = "1320:17387"; // página cuyo nombre es el tamaño, ej. "16"
const NAMES = ["floating-window_16"]; // nombre(s) de componente(s) a auditar en esta página
figma.skipInvisibleInstanceChildren = false; // false: la regla G debe ver también las capas ocultas
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("desajuste de contexto de página antes de la lectura");
const toHex = (c) => {
const h = (n) => Math.round(n * 255).toString(16).padStart(2, "0").toUpperCase();
return "#" + h(c.r) + h(c.g) + h(c.b);
};
const describeFill = (n) => {
if (!n.fills || n.fills === figma.mixed) return n.fills === figma.mixed ? "MIXED" : null;
return n.fills.filter((f) => f.visible !== false).map((f) => ({
type: f.type,
hex: f.type === "SOLID" ? toHex(f.color) : undefined,
opacity: f.opacity,
boundVar: f.boundVariables && f.boundVariables.color ? f.boundVariables.color.id : null,
}));
};
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const all = page.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => pageOf(n) && pageOf(n).id === PAGE_ID);
const report = [];
for (const name of NAMES) {
const c = all.find((x) => x.name === name);
if (!c) { report.push({ name, found: false }); continue; }
report.push({
name: c.name, id: c.id, type: c.type,
width: c.width, height: c.height, x: c.x, y: c.y,
layoutMode: c.layoutMode,
clipsContent: c.clipsContent,
description: c.description,
children: c.children.map((ch) => ({
name: ch.name, type: ch.type,
w: ch.width, h: ch.height, x: ch.x, y: ch.y,
constraints: ch.constraints,
fills: describeFill(ch),
styles: { fill: ch.fillStyleId, stroke: ch.strokeStyleId, effect: ch.effectStyleId },
boundVariableFields: ch.boundVariables ? Object.keys(ch.boundVariables) : [],
})),
});
}
if (figma.currentPage.id !== PAGE_ID) throw new Error("el contexto de página cambió durante la lectura");
return report;
Para validar el contrato de color de forma fiable, también lee un icono hermano conocido como bueno del mismo tamaño
(ej. list_16) de la misma manera y compara la clave de la variable vinculada de la forma principal (Regla D).
El valor hexadecimal nominal del contrato y el valor realmente almacenado en el archivo difieren (ver Regla D), por lo que
comparar con un vecino real es más fiable que comparar con un valor hexadecimal literal.
Paso 3 — Verificar contra las reglas
Reporta cada una como ✅ aprobado / ❌ fallo con el valor observado y, en caso de fallo, lo que debería ser.
A. Tipo de nodo y nombre
-
El nodo es un
COMPONENT(o unCOMPONENT_SETde variantes de tamaño), no unFRAME,GROUPoINSTANCE. Solo los componentes se exportan. -
Primer carácter — verifica esto primero, cambia lo que significa el nombre:
- Empieza con
.o_→ un componente interno, intencionalmente NO exportado. ElfilterComponentde la exportación en.figmaexportrc.mjselimina cualquier nombre que coincida con^[._]. Esto es por diseño, no un error — no lo audites como un icono roto. Solo señálalo si el usuario esperaba que este nodo se exportara (entonces la solución es renombrarlo sin el./_inicial). - Empieza con
-→ inválido, no permitido. Señálalo como un fallo. - Empieza con un dígito → permitido pero desaconsejado. Aún se exporta (ej.
3-columns_32), pero un nombre debería preferiblemente comenzar con una letra del alfabeto inglés — sugiere al usuario que lo renombre. Recomendación suave, no un bloqueante. - Empieza con una letra minúscula del alfabeto inglés → el caso preferido.
- Empieza con
-
Formato del nombre — un nombre de icono exportable debe coincidir con esta expresión regular:
^[a-z0-9]+(-[a-z0-9]+)*_(16|24|32|48|64)$Solo letras minúsculas del alfabeto inglés y dígitos; palabras unidas por un solo guión (sin
--, sin guión inicial/final); luego exactamente un guión bajo; luego un sufijo de tamaño válido. Sin mayúsculas, espacios, cirílico/otros alfabetos, u otros signos de puntuación.- ✅
floating-window_16,api_24,arrow-up-to-bracket_16(y, formato válido pero dígito desaconsejado,3-columns_32) - ❌
Floating-Window_16(mayúsculas) ·плавающее-окно_16(no inglés) ·floating_window_16/icon__16(dos guiones bajos) ·floating--window_16(doble guión) ·floating-window-16(guión en lugar de guión bajo antes del tamaño) ·-floating-window_16(guión inicial) ·floating-window_20(tamaño no en el conjunto permitido) ·floating-window_16(espacio final)
- ✅
-
Sufijo
-o= variante de contorno. El nombre sin-oes la variante rellena. Un par comocloud_24/cloud-o_24ocloud-arrow-up_24/cloud-arrow-up-o_24es un icono en dos estilos. Eso es válido y no es un conflicto ni un duplicado; no pidas un renombramiento por ello. -
El sufijo numérico debe ser igual al tamaño real del componente (Regla B). Un
..._16que es 24×24 es un error.
B. Geometría y diseño
- Sin diseño automático:
layoutMode === "NONE". El diseño automático recoloca los hijos y corrompe la exportación. - Fijo y cuadrado:
width === height === size. El tamaño debe ser un número entero de píxeles (alineado a la cuadrícula de píxeles); los tamaños fraccionarios de componente significan que el icono no se dibujó en la cuadrícula. - Recortar contenido DESACTIVADO:
clipsContent === false. La opción "Recortar contenido" debe estar deshabilitada. Debe ser lo mismo (desactivado) para cada variante de tamaño del icono. Cuando está activada, cualquier ilustración que se extienda más allá de los límites del marco se recorta silenciosamente en la exportación, produciendo un icono recortado o truncado.
C. Estructura raíz (aquí es donde se esconden la mayoría de los errores reales)
- Icono de un solo color: exactamente un hijo raíz — un
VECTORllamadoshape. - Icono de dos colores: exactamente dos hijos raíz —
shape(primario) yshape-2(secundario). Nada más. - Sin otros nodos en la raíz del componente: sin marcos, grupos, rectángulos, slices, vectores duplicados o capas auxiliares ocultas perdidos. Cada nodo extra termina en el SVG exportado.
- Las restricciones de cada forma son
SCALEen ambos ejes (horizontal: "SCALE",vertical: "SCALE") para que la ilustración se escale con el marco.- Gravedad: advertencia (higiene), no un bloqueante de exportación. Las restricciones controlan cómo el componente se redimensiona en Figma, no los datos de la ruta exportada. Arréglelas con las otras ediciones Figma aprobadas, porque las instancias y el redimensionamiento dependen de ellas.
- Idealmente, cada forma es una única ruta aplanada (ver los pasos "Draw an icon" del README del repositorio: contornear, aplanar, regla de relleno Non-zero). Señala múltiples subrutas o grupos no aplanados como una advertencia.
D. Contrato de color de relleno
El contrato (README del repositorio "Guía de zonas de color SVG"):
- Zona primaria
shape→ negro#21222C. La exportación elimina sufillpara que el SVG heredecurrentColor. - Zona secundaria
shape-2→ rojo#E21D03. La exportación lo reescribe afill="currentColor".
Realidad práctica: en el archivo de producción, el relleno primario está vinculado a la variable compartida Icon/Contrast,
que se resuelve a #000819 con una opacidad de pintura de 0.85 — no un #21222C literal. Por lo tanto:
- Clave de referencia:
91eb54e0eae961c540c994192c4eed18e4ea1825—Icon/Contrastde🟥 koobiq · components 19(clave de colecciónc78fab428cf2003c9aa53d86b132a2fa95acb2e2). - Compara la clave de la variable vinculada de la pintura primaria con esa referencia
(
(await figma.variables.getVariableByIdAsync(boundId)).key), y verifica que la opacidad de la pintura sea 0.85. No compares nombres:Icon/Contrastexiste en varias bibliotecas y colecciones. La única variable permitida en un icono es esta, por clave. Una variable con el mismo nombre pero una clave diferente (de otra biblioteca o colección) es un fallo, no una advertencia. Solución: volver a vincular a la variable de la clave de referencia de la misma colección de biblioteca. - El
shape-2secundario debe ser o un#E21D03literal sólido (la exportación lo coincide como un valor hexadecimal literal —secondaryZoneColorHexen.figmaexportrc.mjs), o estar vinculado por clave aIcon/Errorde🟥 koobiq · components 19(clavee736157dcefc2a3e016ec2ddd41a7dc8cf8a5bc6). Para el caso de la variable, no se verifica la opacidad de la pintura. Cualquier otro relleno falla. - Un relleno primario que es un color literal simple que no coincide con ningún hermano, o un relleno secundario que no es
#E21D03, se exportará con el color real en lugar decurrentColor, rompiendo la capacidad del icono de heredar el color.
E. Metadatos de descripción
Cada componente debe tener un campo de descripción no vacío. El texto de la descripción debe ser un
número de la issue de GitHub correspondiente (sin el prefijo #). No importa si la issue
está cerrada o abierta, pero debe tener el formato 12345. No es necesario verificar la existencia real
de la issue. Una descripción vacía o un texto que no sea un número son un fallo.
F. Coherencia entre variantes de tamaño
Después de inspeccionar cada variante de tamaño para el mismo icono base (por ejemplo, cloud en 16–64px), verifica:
- Coherencia de nombre: todas las variantes comparten el mismo nombre base y solo difieren en el sufijo de tamaño numérico. Si
algunas se han quedado en un nombre antiguo (por ejemplo,
alertvswarning), señálalo. - Coherencia de la estructura raíz: el mismo número de hijos raíz en todos los tamaños y los mismos nombres de hijos.
Una variante que tiene
shapeyshape-2mientras que otras solo tienenshapees una señal de alarma. - Coherencia de relleno en el primario: la clave de variable / valor hexadecimal y la opacidad del
shapeprimario deben coincidir en todos los tamaños. Si una variante usa un relleno literal sinIcon/Contrast, es un fallo. - Coherencia de
clipsContent: debe serfalse(oundefined/ausente) en todas las variantes. Si alguna tienetrue(oclipsContentactivado), señálalo como un fallo. - Coherencia de descripción: todas las variantes deben compartir el mismo número de issue. Si difieren, señala la discrepancia.
- Nota sobre cobertura de tamaño: no es necesario que existan los cinco tamaños. Es perfectamente válido tener solo
cloud_16ycloud_24sin los demás. Simplemente reporta qué tamaños están presentes.
G. Capas ocultas
figma.skipInvisibleInstanceChildren debe establecerse en false para que la inspección pueda ver las capas ocultas.
Si un componente raíz tiene hijos ocultos (por ejemplo, un shape oculto además del shape visible), la exportación
puede incluir o no esos hijos dependiendo de cómo se exporte el SVG. Señala los hijos ocultos como una advertencia.
Una capa oculta que es un rectángulo de relleno de documento / máscara o una referencia de imagen (que la exportación ignora) no
necesita ser reportada.
Ofrecer correcciones
Por defecto, la auditoría es de solo lectura. No modifiques el archivo Figma ni llames a use_figma en modo
edición a menos que el usuario diga explícitamente "arréglalo" o "aplica las correcciones". Si
el usuario pide correcciones, ejecútalas en el orden en que aparecen arriba (A → G) porque algunas
correcciones tempranas pueden cambiar los nombres de los nodos que los pasos posteriores necesitan.
Las correcciones permitidas (solo bajo petición explícita):
- Renombrar el componente a un nombre que coincida con el regex y tenga el sufijo de tamaño correcto.
- Desactivar Recortar contenido (
clipsContent = false). - Restablecer las restricciones de las formas hijas a Escala en ambos ejes.
- Volver a vincular el relleno primario a la variable
Icon/Contrast(clave de referencia anterior) con opacidad 0.85. - Volver a vincular
shape-2a#E21D03literal o a la variableIcon/Error(clave anterior). - Establecer la descripción al número de issue proporcionado por el usuario (si el usuario da un número). No reordenes, agregues, elimines ni fusiones hijos raíz, y no fusiones capas ni invoques complementos (por ejemplo, flatten, outline stroke). El usuario debe hacer eso manualmente en Figma.
Límites de velocidad y robustez
- Las llamadas a la API de Figma pueden fallar de forma intermitente. Si una llamada de solo lectura falla y aún no se ha realizado ninguna edición, espera 1 segundo y vuelve a intentarlo hasta 3 veces. Si la llamada es de edición y falla, informa del error — no reintentes las ediciones automáticamente (podrían aplicarse parcialmente).
- Si el usuario pega un enlace de nodo de Figma que incluye un parámetro
node-id, extráelo. Si el enlace es a un archivo diferente (la clave del archivo no coincide conWvUarBj8tO256vML4PkBLl), informa que el enlace es para un archivo diferente y no se puede auditar. No intentes abrir otros archivos. - Las incorporaciones anuales de nuevas variantes de tamaño (por ejemplo, 40px, 72px) romperán esta habilidad. Si los ids de página en la tabla de Prerrequisitos no devuelven las páginas de iconos esperadas, informa al usuario que la habilidad puede estar desactualizada y que necesita actualizar la tabla de ids de página o las comprobaciones de tamaño de sufijo.
Resumen visual
Ejemplo de reporte de salida (marcado sencillo, sin tablas complejas):
## Auditoría de iconos Koobiq: `floating-window`
### Barrido de tamaño
| nombre | 16 | 24 | 32 | 48 | 64 |
| --- | --- | --- | --- | --- | --- |
| floating-window_16 | 1320:17387 | 42149:860 | 42149:862 | 42149:861 | 42149:863 |
### A. Tipo de nodo y nombre
✅ `floating-window_16` es un COMPONENT
✅ El nombre coincide con el regex
✅ El sufijo de tamaño 16 coincide con el tamaño real 16
### B. Geometría y diseño
✅ layoutMode: NONE
✅ 16 × 16, entero alineado a píxeles
✅ clipsContent: false
### C. Estructura raíz
✅ 1 hijo raíz: `shape`
✅ Restricciones: [SCALE, SCALE]
### D. Contrato de color de relleno
✅ `shape` vinculado a Icon/Contrast (clave correcta, opacidad 0.85)
⚠️ `shape-2` no presente (2 tonos no requerido)
### E. Metadatos de descripción
✅ Descripción: "44731"
### F. Coherencia entre variantes
✅ Todas las variantes tienen el mismo nombre base y estructura
⚠️ La variante de 24px no existe (nueva, no se requiere)
### G. Capas ocultas
✅ No se encontraron capas ocultas debajo de la raíz