Saltar al contenido
Aula en la nube
Proyecto 2 · CLI tareas con IA · Presencial y semipresencial

Tu propia herramienta de terminal: tareas

Vas a construir una CLI: un programa que se usa escribiendo órdenes en la terminal y que trabaja con tus ficheros — ordena descargas, renombra fotos, limpia temporales, resume documentos — mediante acciones que defines tú en un fichero acciones.json. Combina lo que sabe hacer el sistema operativo con lo que sabe hacer la IA, y se instala con una sola orden en Linux, Windows y macOS.

Y la construyes de otra manera: desde este proyecto trabajas de forma agéntica con OpenCode, un agente de programación open source conectado a los modelos de IA locales del centro. Tú decides qué se hace y apruebas cada cambio; el agente lee tu repo, propone y edita. El chat queda para consultas.

Por qué importa: los desarrolladores viven en la terminal. Git, los gestores de paquetes, los servidores, la integración continua y los propios agentes de IA son CLI. Quien sabe usarlas automatiza; quien sabe construirlas crea herramientas que usan otros.

Python 3.12
+ Typer + uv
acciones.json
tus acciones, fuera del código
Sistema + IA
ficheros reales y modelos
3 sistemas
Linux · Windows · macOS
OpenCode
agente del centro · modelos locales

Qué es una CLI

CLI (Command Line Interface, interfaz de línea de órdenes) es un programa que se controla escribiendo texto: tecleas una orden, pulsas Intro, el programa hace su trabajo, escribe un resultado y termina. Sin ventanas, sin botones. Lo contrario es una GUI (Graphical User Interface): ventanas, menús y ratón.

Gana la GUI (ventanas y ratón)

  • Explorar algo que no conoces: ves las opciones en pantalla.
  • Tareas visuales: retocar una foto, maquetar, editar vídeo a mano.
  • Usuarios que no quieren aprender órdenes.

Gana la CLI (texto y teclado)

  • Automatizar y repetir: la misma orden hoy, mañana y 300 veces.
  • Encadenar herramientas con tuberías (|): la salida de una es la entrada de otra.
  • Servidores sin pantalla: el 90 % de Internet se administra por SSH.
  • Scripts, tareas programadas (cron) e integración continua (CI): nadie hace clic ahí.
  • Dejar rastro: una orden se copia, se documenta y se repite exacta.

Terminal, shell y CLI: no es lo mismo

La ventana

Terminal

La aplicación que muestra texto y recoge lo que tecleas: Terminal de Linux o macOS, Windows Terminal, la terminal integrada de VS Code.

El intérprete

Shell

El programa que lee tu orden y la lanza: bash (Linux), zsh (macOS), PowerShell (Windows). Cada una tiene su sintaxis: por eso hay órdenes que cambian de un sistema a otro.

El programa

CLI

La herramienta que la shell ejecuta: git, curl, uv… y, dentro de dos semanas, tareas. Funciona igual desde cualquier shell.

Resumen: abres una terminal, dentro corre una shell, y en ella escribes órdenes que lanzan CLI.

Anatomía de una orden

Esta es una orden real de tu futura herramienta. Separada en piezas se entiende todo lo demás:

tareasprogramael comandoinstaladoejecutarsubcomandoqué hacer(un verbo)ordenar-descargasargumento 1qué acción ejecutar(de acciones.json)~/Descargasargumento 2sobre qué carpeta(opcional)--simularopción (flag)cómo: enseña quéharía, sin tocar

Se lee como una frase: «herramienta, haz esto, con esto, aquí, de esta manera».

Y estas son las otras piezas que tiene cualquier CLI seria (y tendrá la tuya):

Ayuda (--help)

Toda CLI decente se explica sola. Es lo primero que pruebas con una herramienta nueva y lo primero que se evalúa en la tuya.

Terminal
tareas --help
tareas ejecutar --help

Argumentos vs opciones

Argumento: dato posicional, normalmente obligatorio (QUÉ). Opción: empieza por - o --, tiene nombre y suele ser opcional (CÓMO). Un flag es una opción sí/no.

Terminal
tareas listar ~/Descargas # argumento
tareas listar ~/Descargas --json # + opción (flag)
tareas --config otro.json acciones # opción con valor

stdin, stdout y stderr

Tres canales: entrada estándar, salida estándar (resultados) y salida de error (avisos y fallos). Separarlos permite redirigir solo lo útil.

Terminal
tareas listar > lista.txt # stdout a un fichero
tareas validar 2> errores.txt # stderr a otro
tareas listar --json | jq length # tubería: stdout entra en jq

Códigos de salida

Al acabar, el programa devuelve un número: 0 = todo bien, distinto de 0 = algo falló. Los scripts y la CI deciden con él. En tu CLI: 0 bien · 1 fallo al ejecutar · 2 error de uso · 3 configuración inválida.

Terminal
tareas validar
echo $? # bash/zsh → 0, 1, 2 o 3
echo $LASTEXITCODE # PowerShell

Variables de entorno

Valores que el sistema le pasa al programa sin escribirlos en la orden: rutas, idioma, claves de API (nunca en el código).

Terminal
export TAREAS_MODELO=llama3 # bash/zsh
$env:TAREAS_MODELO = "llama3" # PowerShell

Fichero de configuración

Lo que cambia poco y no quieres teclear cada vez. En tu proyecto, las acciones viven en acciones.json, no en el código.

Terminal
tareas acciones # usa ./acciones.json
tareas --config ~/mis.json acciones

Formato de salida

Para personas: tablas y colores. Para máquinas: --json (estable, sin colores). Una buena CLI ofrece las dos.

Terminal
tareas listar ~/Descargas # tabla bonita
tareas listar ~/Descargas --json # para otro programa

Interactiva vs no interactiva

Antes de borrar, pregunta (¿Seguro? [s/N]). Pero en un script nadie responde: --si confirma sin preguntar. Y --simular enseña qué haría sin tocar nada.

Terminal
tareas ejecutar limpiar-temporales --simular
tareas ejecutar limpiar-temporales # pregunta
tareas ejecutar limpiar-temporales --si # no pregunta

Las CLI que usa el mundo

Muchas de las herramientas más potentes de la informática no tienen ventanas. Algunas que verás (o usarás) este curso:

Código y versiones

git

Control de versiones: la máquina del tiempo de tu código.

$ git log --oneline -5
gh

GitHub desde la terminal: repos, issues, PR y releases.

$ gh repo create cli-tareas --public
OpenCodeLa que usarás

Agente de código open source que vive en la terminal: lee tu repo, ejecuta órdenes (con tu permiso) y propone cambios. Primos: Claude Code, Codex CLI, Gemini CLI.

$ opencode

Red y descargas

curl

Habla HTTP: descarga ficheros, prueba APIs, instala herramientas.

$ curl -s https://api.github.com/users/octocat
yt-dlp

Descarga vídeo y audio de cientos de webs (respeta derechos y licencias).

$ yt-dlp -x --audio-format mp3 URL
rsync

Copias y copias de seguridad incrementales, en local o por red.

$ rsync -av ~/Proyectos/ /media/usb/copia/

Multimedia y documentos

ffmpeg

La navaja suiza del vídeo y el audio: convertir, recortar, extraer.

$ ffmpeg -i clase.mov -c:v libx264 clase.mp4
magick (ImageMagick)

Convierte, redimensiona y retoca imágenes por lotes.

$ magick foto.png -resize 50% foto.jpg
pandoc

Convierte documentos entre formatos: Markdown, Word, PDF, HTML.

$ pandoc apuntes.md -o apuntes.docx

Buscar y filtrar

rg (ripgrep)

Busca texto en miles de ficheros en milisegundos.

$ rg "TODO" src/
fzf

Buscador difuso interactivo: elige de una lista tecleando.

$ ls | fzf
jq

Lee, filtra y transforma JSON en la terminal.

$ jq ".acciones[].nombre" acciones.json

Sistema y entornos

docker

Ejecuta aplicaciones en contenedores aislados y reproducibles.

$ docker run --rm -it python:3.12
uv · pip · npm

Gestores de paquetes: instalan librerías y herramientas.

$ uv add typer
htop · btop

Monitor de procesos, CPU y memoria en la terminal.

$ btop

Ayuda e IA

tldr

Ejemplos prácticos de cualquier orden, sin leer el manual entero.

$ tldr tar
ollama

Descarga y ejecuta modelos de IA en tu propio equipo.

$ ollama run llama3.2 "Resume esto"
Truco para la vida: antes de buscar en Google cómo se usa una orden, prueba orden --help, tldr orden o man orden. Y si tu herramienta no se explica así de bien, aún no está terminada.

Ideas de acciones para tu herramienta

De menos a más difícil. Empieza por arriba; las de IA llegan en la semana 3. Tus acciones tienen que resolver un problema tuyo: mira tu carpeta de descargas y piensa qué te gustaría no volver a hacer a mano.

  1. 1
    Renombrarsistema●●●●●●

    Fotos con su fecha (2026-10-26_001.jpg), quitar espacios y tildes de nombres, numerar entregas.

  2. 2
    Ordenarsistema●●●●●●

    Descargas por tipo (PDF, imágenes, comprimidos, instaladores) o apuntes por asignatura según el nombre.

  3. 3
    Limpiar y detectarsistema●●●●●●

    Temporales con más de 30 días, duplicados por hash (mismo contenido, distinto nombre), informe de espacio en disco por carpeta.

  4. 4
    Resumir con IAIA●●●●●●

    Resumir PDFs o textos largos, generar el README de una carpeta de proyecto, convertir apuntes desordenados en Markdown limpio.

  5. 5
    Renombrar por contenido con IAIA●●●●●●

    La IA lee el principio de cada documento y propone un nombre que diga lo que es (con --simular primero).

  6. 6
    Acciones encadenadassistema + IA●●●●●●

    Ordenar → quitar duplicados → resumir lo nuevo → informe final. Una orden, varios pasos.

Cómo se desarrolla una CLI de verdad

Las herramientas profesionales no salen de un prompt gigante. Salen de este ciclo, paso a paso, con un commit al acabar cada uno. Es el que seguirás:

  1. 01

    Spec de 10 líneas

    Para quién es, qué problema resuelve, qué órdenes tendrá, qué NO hará. Si no cabe en 10 líneas, aún no sabes qué quieres.

    docs/spec.md
  2. 02

    Interfaz

    Las órdenes y opciones exactas, como las teclearía alguien. Se diseña antes que el código: es el contrato con quien la usa.

    tareas --help
  3. 03

    Núcleo (motor)

    La lógica que hace el trabajo, separada de la interfaz: el motor no sabe nada de Typer ni de colores. Así se prueba y se reutiliza.

    motor.py
  4. 04

    Errores y casos feos

    Carpeta que no existe, JSON mal escrito, fichero abierto, sin permisos, sin red, nombre con tildes. Mensajes claros, nunca un traceback.

    errores.py
  5. 05

    Pruebas

    pytest comprueba que cada acción hace lo que dice en una carpeta temporal. Si cambias algo y se rompe, te enteras antes que tu usuario.

    tests/
  6. 06

    Empaquetado e instalación

    pyproject.toml con su entry point: tu programa se convierte en un comando que se instala con una orden.

    uv tool install
  7. 07

    Release

    Versión etiquetada (v1.0.0), notas de cambios y README con instalación probada en otro sistema.

    git tag v1.0.0
  8. Y vuelta a empezar. Cada acción nueva repasa el ciclo en pequeño: spec → interfaz → motor → casos feos → prueba → commit.
El método del curso: tú escribes la spec y el acciones.json (decides qué hace), el agente (OpenCode) implementa con tu permiso, y tú auditas: lees el código, pruebas casos feos y haces commits pequeños con mensajes que cuentan el proceso. Guarda tus prompts clave en docs/prompts.md. Lo que no sabes explicar, no lo entregas.

Cómo vas a trabajar: con un agente

Hasta ahora le pedías código a un chat y lo copiabas. Desde este proyecto, el código lo escribe un agente dentro de tu repo, y tú diriges. Se usará todo el curso, en presencial y en semipresencial; este proyecto es la introducción.

Para consultas

Chat

Preguntas, responde con texto y tú copias. No ve tu repo ni ejecuta nada: si se equivoca, no se entera. Sigue valiendo para dudas y explicaciones.

Para construir

Agente (OpenCode)

Lee tus ficheros, propone un plan, edita y lanza órdenes —cada cambio te lo pide primero— y comprueba el resultado. Más potencia, más responsabilidad: tú apruebas y tú auditas.

El flujo de cada sesión (siempre el mismo)

  1. 1
    Tú decides

    Escribes la intención: la spec, tu acciones.json o el criterio de aceptación de la tarea.

  2. 2
    Plan

    Agente plan (Tab): «explícame cómo lo harías y qué ficheros tocarías». Lees el plan y lo corriges.

  3. 3
    Build aprobando diffs

    Agente build (Tab): «hazlo». Lees cada diff y cada orden antes de aceptarla.

  4. 4
    Auditas

    Lees el código y pruebas con --simular en ~/pruebas-tareas, con casos feos. ¿Mal? /undo o le explicas el fallo.

  5. 5
    Commit tuyo

    git add + git commit los haces tú, pequeños y con un mensaje que cuente el paso. El prompt clave, a docs/prompts.md.

  6. 6
    Sesión nueva

    /new para cada tarea y /compact si la conversación se alarga: contexto limpio, menos errores.

Qué configuras en tu repo

La conexión con los modelos del centro va en tu configuración global (con variables de entorno: la clave nunca acaba en un fichero que subas a git). En el repo van las reglas del juego para el agente:

FicheroCuándoPara qué
opencode.jsonSemana 1Permisos: qué puede hacer solo (leer, git status), qué te pregunta (editar, casi todas las órdenes) y qué nunca (git push, rm, leer .env).
AGENTS.mdSemana 1Lo genera /init y lo editas tú: stack, órdenes del proyecto y normas (pathlib, utf-8, nada de commits ni push).
.opencode/skills/acciones-json/SKILL.mdSemana 2Skill: las reglas de tu acciones.json, que el agente carga cuando toca acciones.
.opencode/commands/nueva-accion.mdSemana 2Comando /nueva-accion: añadir una acción siempre con los mismos pasos.
.opencode/agents/revisor.mdSemana 2Subagente revisor de solo lectura: @revisor busca errores y casos feos antes de cada commit.

En clase usamos OpenCode 1.18.35 (claves en singular: permission, agent…). Si un tutorial habla de permissions o agents en plural, es OpenCode 2 y su configuración no es compatible.

El gancho: la herramienta con la que vas a programar tu CLI es… una CLI. OpenCode tiene ayuda, opciones, subcomandos, fichero de configuración y códigos de salida: todo lo que estás a punto de construir. Úsala con ojo de quien la va a imitar.
GuíaOpenCode con los modelos del centro →Instalación (Linux, Windows y macOS), conexión, permisos, atajos y el flujo completo paso a paso.

Tecnologías para hacer una CLI

Casi cualquier lenguaje sirve. Cada uno paga un precio distinto:

OpciónA favorEn contraCLI famosas hechas así
Python · Typer / Click / argparseLa del cursoLegible, librerías para todo (ficheros, PDF, IA), multiplataforma, rapidísimo de escribir. argparse viene de serie; Typer genera ayuda y autocompletado desde los tipos.Necesita Python instalado (uv lo resuelve); arranque algo más lento; no da un único ejecutable de serie.aws-cli, yt-dlp, httpie, pip, Ansible
Node.js · commander / oclifEcosistema npm enorme; ideal si ya trabajas en JavaScript; buena integración con herramientas web.Necesita Node; node_modules pesados; asincronía que complica scripts sencillos.npm, Vercel CLI, Heroku CLI, Angular CLI
Go · cobraUn único binario sin dependencias, compila para cualquier sistema, arranque instantáneo.Más verboso; tipado y compilación que ralentizan el primer prototipo.kubectl, gh, docker, hugo
Rust · clapVelocidad máxima y seguridad de memoria; binarios únicos; clap es excelente.Curva de aprendizaje dura; compilaciones lentas.ripgrep, fd, bat, uv
Scripts Bash / PowerShellYa están instalados; perfectos para pegar 5 órdenes existentes.Frágiles al crecer, difíciles de probar y no multiplataforma (Bash ≠ PowerShell).instaladores, nvm, scripts de despliegue

Por qué Python 3 + Typer + uv

Legible

Puedes leer y auditar lo que escribe el agente aunque estés empezando.

Multiplataforma

El mismo código funciona en Linux, Windows y macOS si sigues cuatro reglas.

El agente lo domina

Python es el lenguaje en el que más código ha visto cualquier modelo: menos inventos.

Una orden para instalar

uv instala Python, dependencias y tu herramienta como comando global.

Typer hace el trabajo sucio

A partir de los tipos de tus funciones genera --help, validación de argumentos y autocompletado.

Rich incluido

Tablas, colores y barras de progreso que funcionan también en Windows Terminal.

Un «hola mundo» con Typer. Fíjate: no escribes nada sobre la ayuda ni sobre cómo leer los argumentos. Typer lo deduce de nombre: str, veces: int = 1 y gritar: bool = False.

hola.py
import typer
​
app = typer.Typer(help="Tu primera CLI con Typer.")
​
​
@app.command()
def hola(nombre: str, veces: int = 1, gritar: bool = False):
"""Saluda a NOMBRE tantas VECES como pidas."""
for _ in range(veces):
texto = f"Hola, {nombre}"
print(texto.upper() if gritar else texto)
​
​
if __name__ == "__main__":
app()
Terminal
uv run --with typer hola.py Ana --veces 2 --gritar
# HOLA, ANA
# HOLA, ANA
uv run --with typer hola.py --help
Salida de --help
Usage: hola.py [OPTIONS] {nombre}
​
Saluda a NOMBRE tantas VECES como pidas.
​
╭─ Arguments ──────────────────────────────────────────────────────────╮
│ * nombre <str> [required] │
╰──────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────╮
│ --veces <int> [default: 1] │
│ --gritar --no-gritar [default: no-gritar] │
│ --install-completion Install completion for │
│ the current shell. │
│ --show-completion Show completion for │
│ the current shell, to │
│ copy it or customize │
│ the installation. │
│ --help Show this message and │
│ exit. │
╰──────────────────────────────────────────────────────────────────────╯

Argumento obligatorio, opciones con valor por defecto, un flag con su contrario y autocompletado para tu shell. Gratis.

Multiplataforma: Linux primero, pero en todas partes

En el aula trabajas en Linux. Pero tu herramienta la instalará gente con Windows y macOS (y si eres de semipresencial, tu propio equipo puede ser cualquiera de los tres). Python lo pone fácil si respetas seis reglas:

Rutas con pathlib, nunca concatenando

MALruta = carpeta + "/" + nombre
BIENruta = Path(carpeta) / nombre

Windows usa \ y Linux /. Path elige el separador correcto por ti.

La ~ hay que expandirla

MALopen("~/Descargas/lista.txt")
BIENPath("~/Descargas").expanduser() / "lista.txt"

~ lo entiende la shell, no Python. Sin expanduser() busca una carpeta llamada «~».

Codificación explícita

MALPath("acciones.json").read_text()
BIENPath("acciones.json").read_text(encoding="utf-8")

En Windows la codificación por defecto puede no ser UTF-8: las tildes y la ñ se rompen.

Nada de shell=True ni os.system

MALos.system("mv " + origen + " " + destino)
BIENshutil.move(origen, destino)

mv no existe en PowerShell clásico, un nombre con espacios lo rompe y abre la puerta a inyecciones.

Saltos de línea normalizados

MAL(sin .gitattributes)
BIEN# .gitattributes * text=auto

Windows usa CRLF y Linux LF. Sin esto, git marca cambios fantasma en todas las líneas.

Colores con Rich

MALprint("\033[31mError\033[0m")
BIENconsole.print("[red]Error[/red]")

Los códigos ANSI a mano salen como basura en algunas consolas. Rich detecta lo que soporta cada una.

Y en la semana 4, GitHub Actions ejecutará tus pruebas en Ubuntu, Windows y macOS a la vez en cada git push: si algo solo funciona en tu equipo, te enterarás.

Equivalencias bash ↔ PowerShell

PowerShell tiene alias con los nombres de Linux (ls, cp, mv…), pero las opciones no siempre coinciden. Entre paréntesis, el nombre «de verdad» del cmdlet.

Quiero…bash / zsh (Linux, macOS)PowerShell (Windows)
Listar ficherosls -laGet-ChildItem (o ls)
Cambiar de carpetacd ~/Proyectoscd ~\Proyectos
¿Dónde estoy?pwdpwd (Get-Location)
Crear carpetamkdir -p pruebas/amkdir pruebas\a (New-Item -ItemType Directory)
Copiarcp origen destinocp origen destino (Copy-Item)
Mover / renombrarmv viejo nuevomv viejo nuevo (Move-Item)
Borrarrm fichero · rm -r carpetarm fichero · rm -r carpeta (Remove-Item)
Ver un ficherocat acciones.jsoncat acciones.json (Get-Content)
¿Dónde está un comando?which tareasGet-Command tareas · where.exe tareas
Leer una variableecho $HOMEecho $env:USERPROFILE
Código de salidaecho $?echo $LASTEXITCODE

Instalarla para que sea útil

Un script que solo funciona con python src/tareas/cli.py desde su carpeta es un ejercicio. Una herramienta se teclea por su nombre desde cualquier carpeta. ¿Cómo lo consigue git?

El PATH

Cuando escribes tareas, la shell busca un ejecutable con ese nombre en una lista de carpetas: la variable de entorno PATH. Un comando está «instalado» cuando hay un ejecutable suyo en alguna de ellas.

Terminal
echo $PATH # bash/zsh
$env:Path # PowerShell
which tareas # ¿cuál se ejecuta? (Get-Command en Windows)

El entry point

En pyproject.toml declaras qué función se ejecuta al teclear el comando. Al instalar, uv crea el ejecutable tareas en una carpeta del PATH (~/.local/bin en Linux y macOS).

pyproject.toml
[project.scripts]
tareas = "tareas.cli:app" # comando = "módulo:objeto"
Mientras desarrollas

Modo editable: cada cambio que guardas se nota al momento, sin reinstalar.

Terminal
cd cli-tareas
uv tool install --editable .
tareas --help
Para otra persona

Directamente desde tu repo público de GitHub. Con uv o, si no lo tiene, con pipx.

Terminal
uv tool install git+https://github.com/TU-USUARIO/cli-tareas
# o bien
pipx install git+https://github.com/TU-USUARIO/cli-tareas
Desinstalar

Una buena herramienta se va igual de limpia que llegó. Su configuración de usuario (si la hay) se borra aparte.

Terminal
uv tool uninstall tareas
uv tool list # comprobar que ya no está
Dónde vive cada cosaLinux / macOSWindows
El comando instalado~/.local/bin/tareas%USERPROFILE%\.local\bin\tareas.exe
acciones.json (por defecto)./acciones.json, en la carpeta donde lanzas la orden, o la que indiques con --config
Config de usuario (avance)~/.config/tareas/%APPDATA%\tareas\

La ruta de config de usuario no se escribe a mano: la librería platformdirs devuelve la correcta en cada sistema.

Cómo comprobar que funciona en otro sistema
  1. Pide a un compañero (mejor con otro sistema operativo) que siga solo tu README, sin ayuda tuya.
  2. Instala con la orden de uv tool install git+… y prueba tareas --help y tareas version desde otra carpeta.
  3. Crea una carpeta de pruebas con ficheros de ejemplo y lanza cada acción con --simular y luego de verdad.
  4. Prueba un nombre con tildes y espacios (Apuntes de Física.pdf) y una ruta con ~.
  5. Rompe a propósito el acciones.json: tareas validar debe decir qué falla y salir con código 3.
  6. Desinstala y comprueba que tareas ya no existe. Todo lo que haya fallado, al README o a un issue.

El proyecto: qué tiene que hacer tu CLI

El comando de ejemplo se llama tareas y el repo cli-tareas (público en GitHub). Puedes elegir otro nombre, pero estas son las órdenes de referencia:

OrdenQué hace
tareas --helpAyuda general con todas las órdenes (la genera Typer).
tareas versionMuestra la versión instalada (0.1.0 al empezar).
tareas listar [CARPETA]Tabla con nombre, tipo, tamaño y fecha de cada fichero. Por defecto, la carpeta actual.
tareas accionesLista las acciones definidas en acciones.json con su descripción.
tareas validarComprueba acciones.json y dice exactamente qué campo falla (código 3 si es inválido).
tareas ejecutar NOMBRE [CARPETA] --simularMuestra qué haría la acción sin tocar nada.
tareas ejecutar NOMBRE [CARPETA] --siLa ejecuta sin preguntar (para scripts).
tareas --config RUTA …Opción global: otro fichero de acciones (por defecto ./acciones.json).

Códigos de salida: 0 bien · 1 fallo al ejecutar · 2 error de uso (lo pone Typer) · 3 configuración inválida.

acciones.json: el corazón del proyecto

Las acciones no se escriben en el código: se describen en este fichero y el motor las interpreta. Este es el esquema de referencia; tú lo adaptas a tus acciones y lo documentas en tu README.

acciones.json
{
"version": 1,
"acciones": [
{ "nombre": "ordenar-descargas", "descripcion": "Mueve los PDF y documentos de Descargas a Documentos/PDF",
"tipo": "mover", "origen": "~/Descargas", "filtro": { "extensiones": [".pdf", ".docx"] }, "destino": "~/Documentos/PDF" },
{ "nombre": "renombrar-fotos", "descripcion": "Renombra fotos con su fecha: 2026-10-26_001.jpg",
"tipo": "renombrar", "filtro": { "extensiones": [".jpg", ".jpeg", ".png"] }, "plantilla": "{fecha:%Y-%m-%d}_{n:03}{ext}" },
{ "nombre": "limpiar-temporales", "descripcion": "Borra .tmp con más de 30 días",
"tipo": "borrar", "filtro": { "patron": ".*\\.tmp$", "mas_antiguo_que_dias": 30 }, "confirmar": true }
]
}
version
Versión del esquema del fichero. Si algún día cambias el formato, sabrás leer los antiguos.
acciones
Lista de acciones. Añadir una nueva no exige tocar el código: ese es el objetivo.
nombre
Identificador que se teclea: tareas ejecutar ordenar-descargas. Minúsculas y guiones, único.
descripcion
Lo que verá quien ejecute tareas acciones. Una frase que diga qué hace.
tipo
Qué clase de acción es: mover, renombrar, copiar, borrar… (y en la semana 3, las de IA). Cada tipo es una clase en acciones.py.
origen / destino
Carpetas sobre las que trabaja. Admiten ~ (se expande con expanduser). Si no hay origen, se usa la CARPETA de la orden.
filtro
Qué ficheros le afectan: extensiones, patron (expresión regular sobre el nombre), mas_antiguo_que_dias…
plantilla
Para renombrar: {fecha:%Y-%m-%d} es la fecha del fichero, {n:03} un contador a 3 cifras y {ext} la extensión original.
confirmar
true en acciones destructivas: pregunta antes de actuar salvo que pases --si.

Estructura del repositorio

Cada fichero tiene un único trabajo. La regla de oro: cli.py habla con la persona, motor.py y acciones.py hacen el trabajo, config.py desconfía del JSON.

cli-tareas/
cli-tareas/
├── pyproject.toml # [project.scripts] tareas = "tareas.cli:app"
├── README.md
├── .gitignore # .venv/, __pycache__/, .env, *.egg-info, dist/
├── .gitattributes # * text=auto
├── acciones.json # TUS acciones (lo escribes tú)
├── docs/adr/0001-configuracion-externa.md
├── src/tareas/
│ ├── __init__.py # __version__ = "0.1.0"
│ ├── __main__.py # permite: python -m tareas
│ ├── cli.py # interfaz Typer: órdenes, opciones, salida
│ ├── config.py # leer y VALIDAR acciones.json
│ ├── motor.py # clase Motor: ejecuta una Accion sobre una carpeta
│ ├── acciones.py # clases Accion (mover, renombrar, copiar, borrar…)
│ └── errores.py # TareasError, ConfigError, AccionNoValida
└── tests/ # semana 4
Avance · semana 3

La capa de IA

Acciones de tipo IA (resumir, generar README, renombrar por contenido) que llaman a un modelo — los mismos del centro con los que trabaja OpenCode, o el que configures. La clave va en un .env ignorado por git desde el primer commit, con un .env.example documentado.

Avance · semana 3

Modo sin red

Si no hay red o falta la clave, tu CLI no revienta con un traceback: lo explica en una línea y sigue ofreciendo todas las acciones que no necesitan IA.

Seguridad desde el día 1. .gitignore con .env antes del primer commit; nunca claves en el repo ni en el chat; acciones destructivas siempre con --simular primero y confirmación; y prueba siempre en ~/pruebas-tareas, nunca en tus ficheros reales hasta que funcione.

Planificación: las dos primeras semanas

Semana 126-30 oct

Sesión 1 · Arranque: la terminal y tu primera orden

Objetivo: Perderle el miedo a la terminal y tener un comando tareas que existe de verdad.

  • Moverte por carpetas, crear, copiar y borrar en tu terminal (bash o PowerShell).
  • Instalar uv y Python 3.12; crear el proyecto con uv init --package y añadir Typer.
  • Escribir tu spec de 10 líneas y el .gitignore (con .env) ANTES del primer commit.
  • Primeras órdenes: --help, version y listar con tabla Rich.
  • Instalarla con uv tool install --editable . y lanzarla desde cualquier carpeta.
Hitos de OpenCode
  • Instalar OpenCode 1.18.35 y conectarlo a los modelos del centro.
  • uv init y .gitignore a mano y primer commit ANTES de abrir OpenCode (/undo necesita git).
  • opencode.json de permisos en la raíz del repo.
  • /init → AGENTS.md, que revisas y editas tú.
  • plan → build para crear cli.py (version + listar), aprobando cada diff; /undo cuando algo sale mal.
Producto al acabar: Repo cli-tareas en GitHub con tareas --help, tareas version y tareas listar funcionando, opencode.json y AGENTS.md, y varios commits tuyos que cuentan el proceso.
Ir a la sesión →
Semana 22-6 nov

Sesión 2 · Acciones predefinidas: acciones.json y el motor

Objetivo: Que tu herramienta haga cosas útiles definidas en un fichero que escribes tú.

  • Diseñar tu acciones.json con al menos 3 acciones propias (no las del ejemplo).
  • config.py que valida el JSON y dice qué campo falla; errores.py con tus excepciones.
  • Clases Accion y Motor separadas de la interfaz; órdenes acciones, validar y ejecutar.
  • --simular siempre primero y confirmación antes de borrar; todo en ~/pruebas-tareas.
  • ADR 0001: por qué configuración externa y no acciones escritas en el código.
Hitos de OpenCode
  • Tu acciones.json lo escribes tú, sin IA.
  • Skill del proyecto acciones-json con las reglas del esquema (compruébala con /skills).
  • Comando personalizado /nueva-accion para añadir acciones siempre igual.
  • Subagente revisor de solo lectura (@revisor) y /review antes de cada commit.
Producto al acabar: 3 acciones propias funcionando con --simular, tareas validar con mensajes claros, ADR escrito, skill + comando + revisor en .opencode/ y commits pequeños.
Ir a la sesión →

Cómo se organiza cada grupo

Presencial
  • Es tu segundo proyecto: línea individual de 4 h/semana en el aula, en paralelo a la startup en grupo (las otras 4 h).
  • Equipos del aula con Linux: la terminal es bash desde el primer minuto.
  • Herramientas: terminal y OpenCode conectado a los modelos de IA locales del centro, en paralelo a la startup. El chat, solo para consultas.
  • Cada sesión acaba con commit y push: lo que no está en GitHub no existe.
Semipresencial
  • Quincena 3 = estas dos semanas (26 oct → 6 nov).
  • Comprobación de proceso el 6 nov: 3 acciones propias funcionando + ADR + commits reales.
  • Quincena 4 (9 → 20 nov): IA, pruebas, release y entrega el 20 nov.
  • Trabajas con OpenCode también en casa, por ejemplo en la terminal integrada de VS Code, conectado a los modelos del centro. El chat de Copilot de Microsoft 365 queda solo para consultas.
  • La terminal y cómo conectarte desde casa a los modelos del centro se explican en la tutoría colectiva de arranque, de presencia obligatoria (fecha en el aula virtual).
  • Tu equipo puede ser Windows, Linux o macOS: la sección multiplataforma es para ti.
Después · semana 3

OpenCode: MCP (servidor context7 para documentación de librerías, activado solo cuando hace falta). Tu CLI: capa IA que llama a los mismos modelos del centro desde Python, .env y modo sin red.

Después · semana 4

Pruebas que escribe el agente y auditas tú (pytest + GitHub Actions), opencode run no interactivo en scripts, README, release v1.0 y día de instalación cruzada.

Cómo se evalúa

Primero, la puerta de entrada: si falla cualquiera de estos mínimos, el proyecto no se evalúa (aunque el resto esté genial).

Después, 7 criterios con su peso. Esto es lo que hay que hacer para el Excelente en cada uno:

CriterioPesoNivel Excelente
Acciones predefinidas funcionando
Lo que tu herramienta hace de verdad con tus ficheros.
20 %5 o más acciones propias útiles, combinables, con salida clara y sin efectos sorpresa (simulación previa o confirmación antes de borrar o sobrescribir).
Configuración externa (acciones.json)
Las acciones viven en un fichero que escribes tú, no dentro del código.
15 %Además: añadir una acción no exige tocar el código y hay un ADR que justifica por qué configuración externa y no código.
Capa IA, secretos y modo sin red
IA donde aporta, sin filtrar claves y sin depender de ella para todo.
15 %Al menos 2 acciones con IA útiles; ninguna clave en el historial; si no hay red, la CLI lo explica y sigue con las acciones sin IA.
Robustez: excepciones y entradas feas
Qué pasa cuando el usuario se equivoca o el disco no es como esperabas.
15 %Excepciones propias (ConfigError, AccionNoValida…) con mensajes humanos; ningún traceback ante errores previsibles; aserciones en desarrollo.
Diseño en clases y módulos
Cómo está organizado el código por dentro.
10 %Responsabilidades separadas (interfaz, motor, acciones, proveedor IA), visibilidad cuidada y métodos estáticos con sentido (p. ej., fábricas desde la configuración).
Pruebas y calidad
Cómo demuestras que funciona y que sigue funcionando.
15 %Pruebas que cubren el camino feliz y 3 casos feos, ejecutadas en GitHub Actions y auditadas por el alumno (sabe explicar cada aserción).
Instalación, documentación y proceso
Que otra persona la instale y la use sin preguntarte nada.
10 %Se instala con una orden siguiendo el README; release v1.0 etiquetada con changelog; historial con ramas y PR legibles.

La rúbrica completa, con los cuatro niveles (Insuficiente, En desarrollo, Competente, Excelente) de cada criterio, está en el campus del aula. Léela antes de empezar, no después de entregar.