Implementa issue 0025: catálogo central de automatizaciones cron y scaffolder.
- crons/: directorio de automatizaciones nombradas con README explicando la
convención. Incluye dos ejemplos listos para usar:
· good-morning (send_message, 0 9 * * *) — saludo diario
· daily-summary (llm_prompt, 0 18 * * *) — resumen generado por LLM
- dev-scripts/cron/new.sh: scaffolder interactivo — pregunta nombre,
descripción, tipo de acción y cron expression; crea schedule.yaml +
archivo de prompt vacío; imprime el bloque YAML para copiar en config.yaml.
- dev-scripts/cron/list.sh: lista todas las automatizaciones del catálogo
con nombre, tipo, cron y descripción en formato tabular.
- dev-scripts/cron/apply.sh: añade la automatización al config.yaml del
agente indicado usando yq si está disponible; si no, imprime el bloque
YAML para copiar a mano (sin dependencias obligatorias).
- shell/cron/scheduler.go: exporta Fire(ctx, sc) para disparo inmediato
de un schedule sin esperar al timer cron — útil en tests y CLI.
- shell/cron/scheduler_test.go: cuatro tests nuevos para Fire()
(send_message inline, llm_prompt, sin output_room, sin LLM).
TestScheduler_SkipsInvalidSchedule y TestFire_LLMPrompt_NoLLM_Skips
reemplazados por versiones instantáneas usando Fire en lugar de
@every 100ms + sleep, eliminando ~700ms de tiempo de test.
Nuevo paquete shell/cron con dos archivos:
shell/cron/scheduler.go — Scheduler struct con método Start(ctx) que:
- Registra todas las entradas de config.ScheduleCfg como jobs de robfig/cron
- Omite schedules sin output_room o sin action.kind (warn en log)
- Bloquea hasta que ctx sea cancelado, luego detiene el cron limpiamente
- Recibe MatrixSender, CompleteFunc y *slog.Logger como dependencias (sin importar agents/)
shell/cron/actions.go — ejecutores para fase 1:
- send_message: resuelve contenido desde Message (inline) o Template (archivo .md),
luego llama a matrix.SendMarkdown
- llm_prompt: resuelve prompt desde Prompt o Template, llama al LLM y envía
la respuesta al room configurado; no-op silencioso si no hay LLM
resolveContent() prioriza texto inline sobre ruta de archivo, lo que permite
tanto mensajes cortos en YAML como prompts largos en archivos .md separados.
Fase 2 (run_tool) y fase 3 (inter-bot) quedan pendientes según el issue.