Yurieff/metrics-pack

Convierte los datos de ingresos de una startup en MRR (ingresos recurrentes mensuales), cohortes, NRR (retención neta de ingresos) y una diapositiva para inversores.

¿Qué es metrics-pack?

Este skill automatiza la creación de un paquete de métricas de ingresos para startups, destinado a analistas de fondos. A partir de archivos de actualizaciones mensuales, exportaciones de ingresos por cliente o un registro de contratos, el skill ejecuta un script Python (`metrics.py`) que calcula el MRR (ingreso recurrente mensual), las cohortes de clientes, la NRR (retención neta de ingresos) y produce un archivo CSV con los contratos excluidos. Los resultados se presentan en un informe en inglés sencillo (`metrics.md`), cuatro archivos CSV (`mrr.csv`, `cohorts.csv`, `nrr.csv`, `excluded.csv`) y una diapositiva PNG de formato 16:9 (`metrics-slide.png`). Es compatible con cualquier plataforma (Claude Code, Cursor, Codex, etc.) que pueda ejecutar Python 3.9+ y leer archivos CSV y Markdown. El skill es invocado cuando el usuario frases como 'build the metrics pack for X', 'MRR, cohorts and NRR for X' o '/metrics-pack X'. Una característica clave es que todo el cálculo aritmético se delega al script Python, evitando cálculos manuales en el texto. Además, maneja correctamente los meses facturados frente a los contratados pero aún no facturados, e informa 'cannot compute yet' para la NRR a 12 meses cuando ningún cliente ha completado doce meses.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Yurieff/fundraising-skill-pack/tree/HEAD/skills/metrics-pack

Installed? Explore more Investigación y análisis de datos skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Metrics Pack

Lo que estás haciendo

Se te ha pedido convertir los datos de ingresos de una startup en los números que un analista de fondos lee primero: MRR, cohortes, NRR – en una sola diapositiva. Los números provienen de metrics.py, nunca de tu cabeza. Tú eliges la entrada, ejecutas el helper, lees sus CSV y escribes las palabras alrededor de ellos.

  ENTRADA                    HELPER (metrics.py)            TÚ
  ─────                      ───────────────────            ───
  updates/  (a)  ──┐
  exportación cliente (b) ─┼──►  mrr.csv  cohorts.csv  ──►  metrics.md   (inglés sencillo)
  registro contratos (c) ┘    nrr.csv  excluded.csv      metrics-slide.png (una diapositiva)

Entregables en <company-slug>/metrics-pack/, relativo al directorio de trabajo actual:

  1. metrics.md – el informe. Comienza con "In plain English."
  2. mrr.csv, cohorts.csv, nrr.csv (y excluded.csv para un registro de contratos) – escritos por el helper, sin que tú los modifiques.
  3. metrics-slide.png – una diapositiva 16:9.

Abreviaturas usadas en este archivo: MRR = ingresos recurrentes mensuales; NRR = retención neta de ingresos; CSV = archivo de valores separados por comas; PNG = archivo de imagen; ISO = el formato de fecha internacional (YYYY-MM-DD, o YYYY-MM para un mes); EUR = euro; USD = dólar estadounidense; --as-of = la opción del helper que nombra el último mes facturado.

Entradas

De $ARGUMENTS o la conversación: una empresa y, opcionalmente, un archivo. Tres tipos de entrada, en este orden de preferencia:

EntradaQué esCómo entra al helper
(b) exportación por clienteCSV customer_id,month,revenue, mes en formato YYYY-MM--revenue <csv>
(c) registro de contratosCSV con una fila de encabezado Client, Contract value, Start month, Term (months); las líneas por encima del encabezado se omiten; meses como May-26; valores como "1,550,000"--contracts <csv>
(a) <company-slug>/updates/archivos de actualización mensual con una línea de ingresos, sin detalle del clienteleído como lo hace revenue-chart (Paso 2 allí), escribe customer_id,month,revenue con un cliente total, luego --revenue

Con la entrada (a) no hay clientes. El helper entonces escribe cohorts.csv y nrr.csv con solo un encabezado y deja las celdas new, expansion, contraction y churned de mrr.csv vacías, porque con un solo cliente solo repetirían el crecimiento total. Ignora esas columnas, no muestres ningún mapa de calor e imprime "12-month NRR: cannot compute yet" (razón: sin detalle del cliente). Lo mismo aplica para cualquier archivo --revenue con un único id de cliente.

Si la empresa es ambigua (dos empresas comparten el nombre), pregunta cuál antes de leer nada.


Paso 1 – Ficha de empresa y entrada

La ficha de empresa

<company-slug>/company-profile.md – léela primero. Si falta, créala. Si un campo está vacío y lo aprendes, complétalo y actualiza updated / updated_by. Nunca sobrescribas un campo completado: si encuentras un valor diferente, agrega una fila a ## Conflicts con ambos valores y fuentes. La ficha termina con una sección ## Conflicts (crea una si falta) cuyo encabezado de tabla es exactamente | Field | Card value | Card source | New value | New source | Found by | Date |.

CampoValores permitidos
Trabajo realizado para el clienteuna oración, en palabras propias de la empresa, citado, con URL
Quién pagatexto libre
Modelo de negocioB2B · B2C · B2G
Modelo de ingresossuscripción · transacción · anuncios · único · n.a.
Mercadoexactamente uno: Fintech & insurance · Health & bio · Commerce & retail · Logistics & supply chain · AI & data infrastructure · Enterprise software · Cybersecurity · Legal, compliance & governance · Sales & marketing · HR & talent · Education · Media, gaming & entertainment · Real estate & construction · Energy, climate & agriculture · Industrial & robotics · Consumer lifestyle & services · Other
Verticaltexto libre
Etapapre-seed · seed · Series A · Series B+ · n.a.
Geografíapaís de la sede; países donde vende
Ronda en cursomonto + moneda + instrumento, o "not stated"
Métricas disponiblesdónde (updates/, exportaciones), o "none"

Frontmatter: company, slug, website, updated (fecha ISO), updated_by (nombre del skill). La tabla de la ficha tiene una tercera columna, Source, completada para cada campo.

Toma el nombre de la empresa, el modelo de negocio, el modelo de ingresos y la geografía de la ficha; la geografía sugiere la moneda que esperar. Completa "Métricas disponibles" con el archivo que uses. Si el modelo de ingresos no es suscripción, di en el informe que MRR asume ingresos recurrentes.

Encuentra la entrada (tabla anterior) y cópiala en <company-slug>/metrics-pack/input/ para que la ejecución sea auditable. Nunca comprometas o publiques un archivo a nivel de cliente a menos que el propietario haya dicho que puede ser público. Moneda: tómala del archivo; un registro de contratos a menudo no indica ninguna. Si no se indica ninguna, escribe "currency not stated" en todas partes y nunca elijas una.

Hoy y --as-of. --as-of es el último mes que realmente se ha facturado – normalmente el mes actual, a partir de la fecha de hoy (ISO). Los meses posteriores son contratados: firmados, aún no facturados.


Paso 2 – Ejecutar el helper

Usa /usr/bin/python3 o cualquier Python 3.9+; metrics.py solo necesita la biblioteca estándar y no realiza llamadas de red. Se encuentra al lado de este archivo (skills/metrics-pack/metrics.py; en un plugin instalado, ${CLAUDE_PLUGIN_ROOT}/skills/metrics-pack/metrics.py).

python3 <ruta>/metrics.py --contracts <register.csv> --out-dir <company-slug>/metrics-pack \
        --as-of 2026-11 --min-term 3
python3 <ruta>/metrics.py --revenue <export.csv>     --out-dir <company-slug>/metrics-pack \
        --as-of 2026-11

Lo que escribe:

ArchivoColumnasSignificado
mrr.csvmonth, mrr, new, expansion, contraction, churned, statusuna fila por mes calendario; status es billed hasta --as-of, contracted después
cohorts.csvcohort, month_offset, revenuecohorte = el primer mes de un cliente con ingresos; desplazamiento = meses desde entonces
nrr.csvstart_month, months, nrrNRR a 12 meses para cada mes de inicio donde se pueda calcular solo a partir de meses facturados (un mes después de --as-of nunca cuenta); solo encabezado si no se puede calcular ninguno. También solo encabezado para una exportación de un solo cliente y para un registro de contratos sin renovaciones registradas; para un registro, solo se listan los meses de inicio en los que algún cliente tiene una renovación registrada (regla en el Paso 2)
excluded.csvclient, value, start, termcontratos más cortos que --min-term meses (por defecto 3)

Si el helper se detiene con error:, nombra la columna o valor incorrecto. Corrige la entrada en la fuente (o pregunta al propietario) – nunca edites alrededor de ella.

Lo que asume el helper (indica cada uno en el informe):

  • El valor del contrato es el total durante el plazo, distribuido uniformemente: valor ÷ plazo por mes, desde el mes de inicio. Si el propietario dice que es por mes o por año, detente y pregunta – la respuesta cambia cada número.
  • Un contrato es recurrente solo si su plazo es de al menos --min-term meses. Los más cortos son no recurrentes: reportados en excluded.csv, no en MRR.
  • Nada se renueva a menos que una nueva fila lo indique. Cuando el plazo de un contrato termina, el helper muestra los ingresos que se van como churned. En un registro de contratos que es fin de contrato, no abandono probado – dilo para cada mes de ese tipo.
  • Un registro de contratos registra contratos, no renovaciones. El fin del contrato se lee como ingresos perdidos, por lo que el helper calcula nrr.csv a partir de un registro solo para los meses de inicio en los que algún cliente tiene una renovación registrada. Regla: una renovación es un contrato recurrente posterior (plazo de al menos --min-term meses) del mismo cliente cuyo mes de inicio es al menos un mes después del mes de finalización de un contrato recurrente anterior (mes de finalización = mes de inicio + plazo - 1; por lo tanto, un contrato consecutivo que comienza el mes después del final cuenta, y se permite un intervalo). Un segundo contrato que se superpone con el primero (una venta adicional o complementaria) no es una renovación. Si ningún cliente tiene una renovación, nrr.csv solo tiene encabezado y la respuesta es "cannot compute yet". Si algunos la tienen, la cifra refleja solo las renovaciones que el registro documenta y tanto el informe como la diapositiva deben decirlo.
  • Un cliente que se detiene y luego regresa cuenta como new nuevamente.

Paso 3 – Leer las salidas

Lee los CSV con el módulo csv. Cada número que escribes proviene de ellos. No calcules nada en el texto; si necesitas un número que los CSV no tengan (por ejemplo, una proporción), calcúlalo con un comando y cita la salida del comando.

  1. MRR. El mrr del último mes facturado; el mes máximo y su status; la división facturado/contratado. Nombra el primer y último mes en los datos.
  2. Agotamiento. Los meses posteriores al último inicio de contrato solo pierden ingresos, porque nada en los datos se renueva. Márcalos como agotamiento de contratos sin renovación, nunca como abandono.
  3. Cohortes. Cuenta las cohortes con al menos dos meses de ingresos facturados. El mapa de calor en la diapositiva necesita al menos dos de esas cohortes; de lo contrario, omítelo y explica por qué.
  4. NRR. Primero verifica la entrada. Para la entrada (a) o una exportación de un solo cliente, ignora nrr.csv, cohorts.csv y las columnas de movimiento: la respuesta es "cannot compute yet". De lo contrario, si nrr.csv tiene filas de datos, cítalas; para un registro de contratos agrega "counts only renewals recorded in the register". Si solo tiene el encabezado, la respuesta es "12-month NRR cannot be computed yet": ningún cliente tiene un mes de ingresos doce meses después de su inicio. Nunca lo estimes, nunca lo completes desde una ventana más corta. Da la razón en una línea (por ejemplo, "every contract in the register starts in 2026, so none has run twelve months and renewed").
  5. Excluidos. Número de contratos excluidos, su valor total y la regla que los excluyó.

Paso 4 – metrics-slide.png

Necesita matplotlib y numpy (python3 -c "import matplotlib, numpy"). Si faltan, pregunta antes de instalar algo; la ruta limpia es un entorno virtual desechable. La base es la plantilla de gráfico en revenue-chart (tema oscuro, verificación de colisiones de etiquetas).

Una diapositiva 16:9, en este diseño:

 ┌─────────────────────────────────────────────────────────────┐
 │ <Empresa> – métricas de ingresos        a partir del <fecha ISO>       │
 ├──────────────────────────────┬──────────────────────────────┤
 │ Línea de MRR: sólido = facturado, │ mapa de calor de cohortes (solo meses │
 │ discontinua + sombreado = contratado │ facturados, % del mes 0) – o el   │
 │                              │ panel de NRR si no hay mapa de calor       │
 ├──────────────────────────────┴──────────────────────────────┤
 │ NRR: <cifra>  o  "12-month NRR: cannot compute yet"         │
 │ tres conclusiones de una línea                               │
 └─────────────────────────────────────────────────────────────┘

Reglas para la diapositiva:

  • Contratado nunca se estiliza como facturado. Los meses posteriores a --as-of usan una línea discontinua, marcadores huecos y una banda sombreada etiquetada "contracted, not yet billed".
  • Para un registro de contratos, una cifra de NRR mostrada lleva la advertencia "counts only renewals recorded in the register" (el script la agrega cuando source = "register").
  • La línea de NRR muestra la cifra de nrr.csv, o las palabras exactas "12-month NRR: cannot compute yet".
  • Mapa de calor solo si al menos dos cohortes tienen al menos dos meses facturados.
  • Tres conclusiones de una línea, cada una trazable a una fila de CSV, ninguna es una recomendación o predicción.
  • Moneda en el título del eje; "currency not stated" si la fuente no proporcionó ninguna.
  • Todas las cifras en la diapositiva son leídas de los CSV por el script, no escritas a mano. Las conclusiones son el único texto escrito: verifica cada número en ellas contra los CSV después de dibujar.
import csv, os
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np

# CONFIGURAR -------------------------------------------------------------
company  = "Empresa"
folder   = "<company-slug>/metrics-pack"
currency = "currency not stated"      # o "EUR", "USD" ...
as_of    = "2026-11"                  # mismo valor que --as-of
source   = "register"                 # "register" (--contracts) o "export" (--revenue)
takeaways = [                         # tres líneas, cada una verificada contra los CSV
    "Conclusión 1",
    "Conclusión 2",
    "Conclusión 3",
]
# -------------------------------------------------------------------------

def read(name):
    with open(os.path.join(folder, name), newline="", encoding="utf-8") as fh:
        return list(csv.DictReader(fh))

mrr, coh, nrr_rows = read("mrr.csv"), read("cohorts.csv"), read("nrr.csv")
months = [r["month"] for r in mrr]
vals = [float(r["mrr"]) for r in mrr]
billed = [r["status"] == "billed" for r in mrr]
x = np.arange(len(months))
nb = sum(billed)                                   # los meses facturados vienen primero

BG, FG, MUTED = '#0f1117', '#e0e0e0', '#9aa0b4'
BILLED, CONTR = '#4fc3f7', '#f2b84b'

fig = plt.figure(figsize=(13.333, 7.5), facecolor=BG)         # 16:9
gs = fig.add_gridspec(3, 2, height_ratios=[0.5, 5.2, 1.9], width_ratios=[1.35, 1],
                      hspace=0.35, wspace=0.18, left=0.06, right=0.97, top=0.97, bottom=0.04)
ax_t = fig.add_subplot(gs[0, :]); ax_t.axis('off')
ax_t.text(0, 0.5, f"{company} – métricas de ingresos", color='#fff', fontsize=20,
          fontweight='bold', va='center')
ax_t.text(1, 0.5, f"facturado hasta {as_of}", color=MUTED, fontsize=11, ha='right', va='center')

# --- Línea de MRR ---
ax = fig.add_subplot(gs[1, 0]); ax.set_facecolor(BG)
if nb < len(months):
    ax.axvspan(nb - 0.5, len(months) - 0.5, color=CONTR, alpha=0.10, zorder=0)
    ax.text(nb - 0.35, 0.97, "contratado,\naún no facturado", color=CONTR, fontsize=9,
            transform=ax.get_xaxis_transform(), va='top')
ax.plot(x[:nb], vals[:nb], color=BILLED, lw=2.6, marker='o', ms=5, label='MRR facturado', zorder=3)
if nb < len(months):
    j = max(nb - 1, 0)                                         # unir los dos segmentos
    ax.plot(x[j:], vals[j:], color=CONTR, lw=2.2, ls='--',
            label='MRR contratado (no facturado)', zorder=3)
    ax.scatter(x

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills relacionados