Guía Práctica: Usando el Multi-Agente de Cursor 2.0 para Refactorizar y Generar Tests

Guía Práctica: Usando el Multi-Agente de Cursor 2.0 para Refactorizar y Generar Tests

Refactor y tests en paralelo con Cursor 2.0: el atajo real a código más limpio

Los equipos que iteran rápido no escriben más código: escriben mejor código con menos fricción. La nueva interfaz multi‑agente de Cursor 2.0 permite orquestar tareas en paralelo —por ejemplo, refactorizar un módulo y generar tests de unidad— sin esperar a que termine una para iniciar la otra. En esta guía te muestro, paso a paso, cómo hacerlo con un ejemplo práctico en Node.js.

¿Por qué multi‑agente para refactor + tests?
  • Paralelismo real: un agente refactoriza mientras otro redacta y valida tests. Menos tiempo en cola, más tiempo entregando valor.
  • Aislamiento seguro: cada agente trabaja en su propio contexto (worktree/entorno), evitando conflictos.
  • Feedback temprano: los tests se escriben con la intención de la nueva API, forzando mejores contratos.
  • Menos deuda: refactor y cobertura avanzan juntos, no como tareas separadas “para después”.
Lo que vas a construir

Partiremos de un módulo Node.js con cierta deuda técnica (funciones con efectos colaterales, nombres ambiguos y validaciones mezcladas). Usaremos Cursor 2.0 para:

  • Refactorizar a una API más expresiva, pura y testeable.
  • Generar y ajustar tests con Jest en paralelo.
  • Revisar cambios y aplicar diffs de múltiples archivos en una sola vista.
Requisitos
  • Node.js 18+ y npm.
  • Cursor 2.0 actualizado con la interfaz multi‑agente habilitada.
  • Git inicializado en el repo (se recomienda activar worktrees para aislamiento).
Repo de ejemplo

Estructura mínima:

  • /src/priceCalculator.js
  • /package.json
  • /__tests__/ (se generará)
Código inicial (antes del refactor)

Este módulo acumula responsabilidades y tiene validaciones dispersas. Es perfecto para que el agente de refactor lo mejore.

// src/priceCalculator.js (versión inicial)
function applyDiscount(price, coupon) {
    if (!price || price < 0) return 0;
    if (!coupon) return price;
    if (coupon.type === 'percent') {
        return Math.round(price - (price * coupon.value) / 100);
    }
    if (coupon.type === 'absolute') {
        const v = price - coupon.value;
        return v < 0 ? 0 : v;
    }
    // side effect (logging arbitrario)
    console.log('Unknown coupon type');
    return price;
}

function calcTotal(items, coupon) {
    // items: [{ price, qty }]
    if (!Array.isArray(items)) return 0;
    let total = 0;
    for (let i = 0; i < items.length; i++) {
        const it = items[i];
        total += (it.price || 0) * (it.qty || 1);
    }
    return applyDiscount(total, coupon);
}

module.exports = { calcTotal, applyDiscount };
Configura Jest rápidamente

Agrega Jest y un script de test:

{
    "name": "cursor-multi-agent-demo",
    "version": "1.0.0",
    "type": "commonjs",
    "scripts": {
        "test": "jest --passWithNoTests"
    },
    "devDependencies": {
        "jest": "^29.7.0"
    }
}

Instala dependencias: npm i

Crea tus agentes en Cursor 2.0
  • Agente 1 – Refactor (objetivo: pureza, legibilidad, validaciones claras, sin side effects ni console.log en flujo de dominio).
  • Agente 2 – Tests (objetivo: generar suite de Jest con casos nominales, bordes y errores; preparar mocks si fueran necesarios).

Prompts sugeridos para iniciar cada agente:

  • Refactor Agent: “Refactoriza src/priceCalculator.js para lograr funciones puras, validar entradas con mensajes útiles, documentar TS JSDoc y separar cálculo de descuentos. No cambies behavior salvo donde haya bugs evidentes.”
  • Tests Agent: “Genera tests con Jest para calcTotal y applyDiscount. Cubre percent/absolute, valores límite (precio 0, cupón desconocido), múltiples ítems y qty inválido.”
Ejecuta en paralelo con aislamiento
  • Habilita trabajo aislado (p. ej., Git worktrees o entornos sandbox) para cada agente. Así, los cambios no colisionan.
  • Activa el Plan mode para que Cursor descomponga la tarea en pasos y archivos a tocar.
  • Deja corriendo ambos agentes. Observa cómo uno propone la nueva API mientras el otro adapta los tests a esa API.
Revisión y aplicación de cambios

Cuando ambos agentes concluyan una iteración, usa el panel de review diffs de Cursor:

  • Revisa cambios por archivo en una sola vista.
  • Evalúa si el contrato de la función refactorizada coincide con lo que cubren los tests.
  • Aplica cambios selectivamente o solicita otra pasada al agente correspondiente.
Resultado del refactor (después)
// src/priceCalculator.js (refactorizado)
/**
* @typedef {Object} Coupon
* @property {('percent'|'absolute')} type
* @property {number} value
*/

/** Valida que sea un número finito y >= 0 */
function asNonNegativeNumber(n, fallback = 0) {
    const v = Number.isFinite(n) ? n : fallback;
    return v < 0 ? 0 : v;
}

/** Aplica el cupón de forma pura, sin efectos colaterales */
function applyDiscount(price, coupon) {
    const p = asNonNegativeNumber(price, 0);
    if (!coupon) return p;
    if (coupon.type === 'percent') {
        const pct = asNonNegativeNumber(coupon.value, 0);
        const discounted = p - (p * pct) / 100;
        return Math.round(discounted < 0 ? 0 : discounted);
    }
    if (coupon.type === 'absolute') {
        const abs = asNonNegativeNumber(coupon.value, 0);
        const diff = p - abs;
        return diff < 0 ? 0 : diff;
    }
    // Cupón desconocido => no aplica
    return p;
}

/**
* Calcula el total de items con qty y price, luego aplica descuento.
* @param {{price:number, qty?:number}[]} items
* @param {Coupon=} coupon
*/
function calcTotal(items, coupon) {
    if (!Array.isArray(items) || items.length === 0) return 0;
    const sum = items.reduce((acc, it) => {
        const price = asNonNegativeNumber(it?.price, 0);
        const qty = asNonNegativeNumber(it?.qty ?? 1, 1);
        return acc + price * qty;
    }, 0);
    return applyDiscount(sum, coupon);
}

module.exports = { calcTotal, applyDiscount, asNonNegativeNumber };
Suite de tests generada por el agente
// __tests__/priceCalculator.test.js
const { calcTotal, applyDiscount } = require('../src/priceCalculator');

describe('applyDiscount', () => {
    test('sin cupón devuelve el mismo precio', () => {
        expect(applyDiscount(100, null)).toBe(100);
    });

    test('percent aplica porcentaje y redondea', () => {
        expect(applyDiscount(200, { type: 'percent', value: 10 })).toBe(180);
    });

    test('absolute resta valor sin ir bajo cero', () => {
        expect(applyDiscount(50, { type: 'absolute', value: 80 })).toBe(0);
    });

    test('cupón desconocido no altera el precio', () => {
        expect(applyDiscount(120, { type: 'weird', value: 10 })).toBe(120);
    });
});

describe('calcTotal', () => {
    test('lista vacía => 0', () => {
        expect(calcTotal([], null)).toBe(0);
    });

    test('suma price*qty y aplica cupón percent', () => {
        const items = [{ price: 100, qty: 2 }, { price: 50 }];
        expect(calcTotal(items, { type: 'percent', value: 10 })).toBe(225);
    });

    test('valores inválidos se normalizan a 0/1', () => {
        const items = [{ price: -10, qty: -3 }, { price: 30, qty: NaN }];
        expect(calcTotal(items, null)).toBe(30);
    });
});
Ejecuta y valida
  • Corre npm test en la terminal sandbox de Cursor para validar sin salir del editor.
  • Si falla algún caso, pide al agente de tests que explique el fallo y proponga el ajuste mínimo (en test o en implementación).
  • Cuando todo pase en verde, aplica cambios y realiza el commit.
Buenas prácticas para prompts y orquestación
  • Contratos claros: define en el prompt del agente de refactor la API pública esperada y restricciones (pureza, errores manejados, tipos).
  • Desacople: pide separar validación, cálculo y side effects en funciones distintas. Los tests respiran mejor.
  • Iteraciones cortas: activa el planning y limita el alcance por PR: 1 módulo + 1 suite de tests por iteración.
  • Contexto suficiente: comparte al agente archivos relacionados (p. ej., README o casos de negocio) para decisiones informadas.
Más allá del “hola mundo” de los agentes
  • Browser integrado: si trabajas en frontend, permite a un agente validar el comportamiento de la UI en tiempo real.
  • Voice mode: dispara correcciones rápidas sin romper el flujo de teclado.
  • LSP optimizados: notarás diagnósticos más ágiles en TypeScript/JS, útiles en refactors amplios.
  • Auditoría y seguridad: usa terminales sandbox para comandos y activa reglas de ejecución compartidas en equipo.
Integración con CI (opcional, recomendado)

Agrega una verificación mínima en tu pipeline (GitHub Actions, GitLab CI o similar) para que no se fusionen cambios sin tests verdes. De esta forma, el multi‑agente no solo acelera localmente, también eleva el estándar del repo.

Solución de problemas comunes
  • Conflictos entre agentes: usa worktrees/ramas separadas y aplica diffs de forma incremental en review.
  • Tests “frágiles”: solicita al agente criterios más deterministas o inyecta dependencias para evitar acoplamientos.
  • Cobertura superficial: exige casos de borde y mutación leve (p. ej., cambiar entradas) para fortalecer la suite.
¿Y en Laravel?

El mismo patrón aplica con Pest o PHPUnit. Un agente refactoriza un Service o Controller y otro genera pruebas con factories y fakes. Define prompts equivalentes y deja que corran en paralelo; revisa diffs y ejecuta php artisan test en la terminal sandbox de Cursor.

Checklist final para llevarte a producción
  • Refactor pequeño, atómico, centrado en una unidad funcional.
  • Tests que documenten intención y bordes: percent/absolute, entradas inválidas, acumulación.
  • Review visual de diffs multiarchivo y commit con mensaje claro.
  • CI con regla: “no merge si tests fallan”.
Cierre

El multi‑agente de Cursor 2.0 no es solo “más rápido”: cambia el patrón de trabajo. Cuando refactor y tests avanzan a la par, el resultado es una base de código más confiable, menos regresiones y un equipo que itera con confianza. ¿Te gustó este flujo? Déjame tus dudas en comentarios, comparte con tu equipo o sigue el blog para más guías aplicadas.

Add a comment

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Prev Next