EagleKit
CLI de proyectos con sistema de plugins por entry points de Python. Lo difícil no fueron los comandos: fue coordinar dos hooks de Git que son procesos separados, sin memoria compartida y con un humano que puede abortar entre los dos.
Poner [7][pagos-qa] delante de un commit parece una línea de bash. Son dos procesos que nunca coinciden en el tiempo.
EagleKit es un CLI de proyectos (ek) que crece con plugins instalables con pip. Los plugins de Git numeran commits por rama y espejan QA → Prod con cherry-pick. Lo difícil no fueron los comandos: fue que Git lanza cada hook como un proceso nuevo, sin memoria compartida, y un humano puede abortar entre los dos.
Un comando que no sabe qué subcomandos tendrá mañana
Pulsa los pasos o deja que avance. El core no importa a ningún plugin: los descubre en el momento de arrancar.
Un plugin entero es una función y una línea en pyproject.toml.
Un wrapper de shell, un proceso Python y tres sitios donde vive el estado
Lo que hace el core sin ningún plugin instalado.
ek add ., ek list, ek status. Workspaces para separar trabajo, personal y clientes.ek add genera ~/.eagle_projects con una variable por proyecto: cd $api. O ek cd api vía la función de shell..eagle/config.yaml; .eagle/branches/<rama>/ sobreescribe por nombre. String, lista o script..eagle/. 15 subcomandos entre todo y comment.local (.git/info/exclude), repo, global o none. La recomendada no toca el repo de nadie más.ek plugins lista cargados y fallidos con su error. Asistente de primer arranque con ek setup.Qué pasa dentro de git commit cuando el tracker está instalado
Cada hook es un proceso nuevo que Git lanza y descarta. Este es el flujo real del plugin público, paso a paso.
Ana lleva pagos en dos ramas
Un caso inventado con tres commits, marcado como ejemplo. Los comandos y los ficheros son los reales.
ek mirror create pagos crea pagos-qa y pagos-prod y las enlaza en .git/eaglekit_mirror/links.json. ek tracker init instala los dos hooks.[1][pagos-qa], [2][pagos-qa], [3][pagos-qa]. El segundo hook guarda índice, hash y UUID en branches/pagos-qa.json.ek mirror push lista los commits de QA cuyo UUID no está en pushed_logical_ids y los aplica con git cherry-pick, uno a uno. Antes de cada uno deja un fichero de metadata para que el hook conserve el UUID.pending_commits y se sale. Resuelves en el editor y sigues con ek mirror push --continue: el filtro por UUID no vuelve a aplicar el 1 ni el 2.sync_state.json guarda los pendientes y marca in_progress. Ana resuelve el conflicto, hace git cherry-pick --continue y ek mirror push --continue termina lo que faltaba.ek mirror status: 0 pendientes en cada sentido. Un hotfix hecho directamente en Prod vuelve a QA con ek mirror pull por el mismo mecanismo.Cinco veces en que el happy path no era el problema
Mismo patrón cada vez: lo que cualquiera haría primero, por qué no sirve, lo que se hizo. El detalle técnico queda plegado.
Diseñar el happy path tomó un día. Que nada se rompa cuando alguien aborta un commit a medias, hace amend, o el quinto de cinco cherry-picks conflictúa, tomó semanas. La complejidad de un CLI no está en los comandos: está en la resiliencia.
Dónde poner cada cosa, y por qué
Casi todas son la misma pregunta: ¿esto lo necesita quien solo quiere ek add . y ek list?
cd en el shell que lo lanzó. La función ek() intercepta ek cd api, pide la ruta con ek-core cd --path api y ejecuta el cd real. Todo lo demás pasa de largo a Python. Es el mismo patrón que nvm, pyenv y direnv.Profundizar
ek shell install añade la función entre marcadores en .zshrc o .bashrc; uninstall quita solo ese bloque. Además ek add regenera ~/.eagle_projects con export api="/ruta" por proyecto, así que cd $api funciona sin la función.
router.py que traduce ek deploy en ek-core run task deploy, y el README lo documenta, pero en v0.2.1 el entry point ek apunta al wrapper y no al router. Hoy el atajo no está cableado: la forma real es ek run task deploy.pipx inject eaglekit eaglekit-plugin-mirror y el comando existe; las dependencias del plugin las resuelve el empaquetado, no el CLI. Una carpeta ~/.eaglekit/plugins/ obligaría a inventar un formato, versionarlo y resolver imports a mano.Profundizar
El contrato es mínimo a propósito: un grupo, eaglekit.plugins, y una función register(app). El core no conoce ningún plugin por nombre y no hay lista central que mantener. El coste es el reto 05: el namespace lo elige cada plugin.
Profundizar
Cada plugin puede instalarse solo: declara eaglekit como dependencia y arrastra el core si no está. Los dos plugins públicos siguen Clean Architecture con capas domain, application, infrastructure y presentation: ARCHITECTURE.md del mirror.
flow.db en .git/eaglekit/: modo WAL para las escrituras desde hooks, SQL con índices para las consultas y una transacción por cherry-pick. SQLite viene con Python: sin servidor, sin dependencia nueva.Profundizar
Cinco decisiones que hoy tomaría al revés
Recap
ek que no conoce sus plugins: los descubre por entry points al arrancar, cada uno en su namespace, y un plugin roto solo produce un aviso.