---
name: swl:cron
description: Gestiona tareas programadas del sistema SWL. Crear, listar, pausar, reanudar y eliminar jobs recurrentes. Soporta 4 formatos de schedule (duración, intervalo, cron, timestamp ISO). Los jobs se almacenan en .planning/cron/jobs.json.
allowed_tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"]
user-invocable: true
version: "1.0.0"
---

# /swl:cron — Gestión de tareas programadas

Eres el gestor de tareas programadas del sistema SWL. Administras jobs que se ejecutan automáticamente según un schedule definido.

## Subcomandos

| Subcomando | Descripción |
|-----------|-------------|
| `list` | Lista todos los jobs con estado, schedule y próxima ejecución |
| `add` | Crear un nuevo job (guía interactiva) |
| `remove <id>` | Eliminar un job |
| `pause <id>` | Pausar un job sin eliminarlo |
| `resume <id>` | Reanudar un job pausado |
| `log [N]` | Mostrar últimas N ejecuciones |
| `start` | Iniciar el scheduler daemon |
| `run <id>` | Ejecutar un job manualmente (una vez, sin afectar schedule) |

## Formatos de schedule soportados

| Formato | Ejemplo | Descripción |
|---------|---------|-------------|
| Duración única | `30m`, `2h`, `1d` | Ejecutar una vez en N minutos/horas/días |
| Intervalo recurrente | `every 30m`, `every 2h` | Ejecutar cada N periódicamente |
| Expresión cron | `0 9 * * 1-5` | Cron estándar de 5 campos |
| Timestamp ISO | `2026-04-15T09:00` | Una vez a hora exacta |
| **Lenguaje natural (es-MX)** | `cada lunes a las 9am` | El comando lo traduce al formato técnico |

### Traducción de lenguaje natural

Si el usuario describe el schedule con una frase en español, tradúcela a cron
o a los formatos técnicos ANTES de guardar. Ejemplos de traducción:

| Frase del usuario | Traducción |
|---|---|
| "cada lunes a las 9am" / "todos los lunes 9 de la mañana" | `0 9 * * 1` |
| "cada día a las 8:30" | `30 8 * * *` |
| "cada hora" | `every 1h` |
| "cada 30 minutos" | `every 30m` |
| "de lunes a viernes a las 9am" | `0 9 * * 1-5` |
| "cada primer día del mes" / "el 1 de cada mes a medianoche" | `0 0 1 * *` |
| "cada domingo a las 23:00" | `0 23 * * 0` |
| "cada 2 horas en horario laboral" | `0 9-18/2 * * 1-5` |
| "en 30 minutos" (one-shot) | `30m` |
| "mañana a las 10am" (one-shot) | `<timestamp ISO calculado>` |
| "dentro de una semana" (one-shot) | `7d` |

**Reglas de traducción:**

1. **Días de la semana**: lunes=1, martes=2, …, sábado=6, domingo=0 (estándar cron).
2. **Horas**: interpretar "de la mañana"/"am" = 0-11; "de la tarde"/"pm" = 12-23.
3. **Zona horaria**: asumir la local del sistema salvo que el usuario especifique una.
4. **Ambigüedad**: si la frase es ambigua ("a veces", "pronto", "seguido"), pregunta
   al usuario antes de guardar. Nunca inventes un valor por "parecerse".
5. **Validación obligatoria**: después de traducir, MOSTRAR al usuario la expresión
   técnica resultante y la próxima ejecución estimada, y pedir confirmación:

   ```
   Traduje "cada lunes a las 9am" → `0 9 * * 1`
   Próxima ejecución: lunes 21 de abril 2026, 09:00:00 (hora local)
   ¿Confirmas? (sí / no / corrige la frase)
   ```

6. **No guardar** hasta confirmación explícita.

## Subcomando: list

```bash
node -e "const s=require('./gateway/cron/store'); const jobs=s.loadJobs(process.cwd()); console.log(JSON.stringify(jobs,null,2))"
```

Mostrar como tabla formateada:
```
=== Jobs programados ===

| ID | Nombre | Schedule | Estado | Próxima ejecución | Última |
|----|--------|----------|--------|-------------------|--------|
```

Si no hay jobs: "No hay tareas programadas. Usa `/swl:cron add` para crear una."

## Subcomando: add

Preguntar al usuario:
1. **Nombre**: descripción breve del job
2. **Comando**: qué ejecutar (ej: `node bin/swl-ses.js doctor --json`)
3. **Schedule**: puede ser en lenguaje natural (ej: "cada lunes a las 9am") o
   en formato técnico. Si es lenguaje natural, traducir según la tabla de la
   sección **Traducción de lenguaje natural** y CONFIRMAR con el usuario la
   expresión técnica resultante antes de continuar.
4. **Entrega**: dónde entregar el resultado (`local`, `telegram`, `discord`)

Crear el job:
```javascript
const store = require('./gateway/cron/store');
store.addJob(process.cwd(), {
  name: '[nombre]',
  schedule: '[schedule]',
  command: '[comando]',
  deliver: '[destino]',
});
```

Jobs sugeridos para el sistema SWL:
- **Salud diaria**: `node bin/swl-ses.js doctor` — schedule `0 9 * * 1-5`
- **Auditoría de deps semanal**: `npm audit --json` — schedule `0 10 * * 1`
- **Backup de instintos**: `cp instintos/proyecto.yaml .planning/backups/` — schedule `every 1d`

## Subcomando: remove <id>

```javascript
const store = require('./gateway/cron/store');
store.removeJob(process.cwd(), '[id]');
```

Confirmar antes de eliminar.

## Subcomando: pause / resume

```javascript
store.pauseJob(process.cwd(), '[id]');
store.resumeJob(process.cwd(), '[id]');
```

## Subcomando: log

```javascript
const store = require('./gateway/cron/store');
const log = store.readLog(process.cwd(), N);
```

Mostrar como tabla con fecha, job, status y output resumido.

## Subcomando: start

```bash
node gateway/cron/scheduler.js
```

Inicia el scheduler. Muestra PID y confirma que el lock fue adquirido.

## Subcomando: run <id>

Ejecuta un job manualmente sin afectar su schedule normal:
```javascript
const { executeJob, deliverResult } = require('./gateway/cron/scheduler');
const store = require('./gateway/cron/store');
const jobs = store.loadJobs(process.cwd());
const job = jobs.find(j => j.id === '[id]');
const result = executeJob(job, process.cwd());
deliverResult(job, result, process.cwd());
```

## Parser de lenguaje natural programático (experimental)

El módulo `scripts/lib/schedule-parser.js` proporciona parseo programático de
frases en inglés a expresiones cron, disponible para scripts y extensiones del
comando. No requiere dependencias externas.

Uso desde Node.js:
```javascript
const { parseNaturalSchedule, isCronDue } = require('./scripts/lib/schedule-parser');

// "every morning at 9am" → { cron: '0 9 * * *', descripcion: 'Diariamente a las 9:00 AM' }
const resultado = parseNaturalSchedule('every morning at 9am');

// Verificar si una expresión cron debe ejecutarse ahora
const pendiente = isCronDue('0 9 * * *', Date.now(), ultimaEjecucionMs);
```

Frases reconocidas:
- `"hourly"` → `0 * * * *`
- `"daily"` / `"every day"` → `0 9 * * *`
- `"weekly"` → `0 9 * * 1`
- `"every N minutes"` (1-59) → `*/N * * * *`
- `"every N hours"` (1-23) → `0 */N * * *`
- `"every morning at 9am"` / `"daily at 14:30"` → expresión con hora específica
- `"every monday at 9am"` / `"weekly on friday"` → expresión con día de semana
- Expresión cron cruda de 5 campos → passthrough sin modificar

Para frases en español, la traducción manual (tabla de la sección anterior)
sigue siendo el método principal — el parser solo opera en inglés.

## Reglas de comportamiento

- SIEMPRE confirmar antes de eliminar un job
- SIEMPRE mostrar el schedule en formato legible junto al formato técnico
- Los jobs con `deliver: telegram/discord` requieren que el gateway esté configurado
- El scheduler usa file lock exclusivo — solo una instancia puede correr a la vez
