From 3043518fecbb7afc54ce26936dbda54dd64b5ec5 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sat, 28 Mar 2026 23:23:30 +0100 Subject: [PATCH] docs: CLAUDE.md actualizado con fn run, tipos Go en functions/, bash functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentación de fn run para todos los lenguajes, nueva ubicación de tipos Go, sección de uso por agentes. Añadidas funciones Bash del registry (shell, infra, core, pipelines). Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/CLAUDE.md | 52 +++++++++++- .../infra/assert_docker_container_running.md | 38 +++++++++ .../infra/assert_docker_container_running.sh | 19 +++++ bash/functions/infra/docker_cp_file.md | 41 ++++++++++ bash/functions/infra/docker_cp_file.sh | 33 ++++++++ .../pipelines/setup_metabase_volume.md | 65 +++++++++++++++ .../pipelines/setup_metabase_volume.sh | 82 +++++++++++++++++++ bash/functions/shell/assert_command_exists.md | 36 ++++++++ bash/functions/shell/assert_command_exists.sh | 18 ++++ bash/functions/shell/assert_file_exists.md | 38 +++++++++ bash/functions/shell/assert_file_exists.sh | 20 +++++ 11 files changed, 438 insertions(+), 4 deletions(-) create mode 100644 bash/functions/infra/assert_docker_container_running.md create mode 100644 bash/functions/infra/assert_docker_container_running.sh create mode 100644 bash/functions/infra/docker_cp_file.md create mode 100644 bash/functions/infra/docker_cp_file.sh create mode 100644 bash/functions/pipelines/setup_metabase_volume.md create mode 100755 bash/functions/pipelines/setup_metabase_volume.sh create mode 100644 bash/functions/shell/assert_command_exists.md create mode 100644 bash/functions/shell/assert_command_exists.sh create mode 100644 bash/functions/shell/assert_file_exists.md create mode 100644 bash/functions/shell/assert_file_exists.sh diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index be1aa3e8..ac9608ce 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -55,9 +55,12 @@ sqlite3 registry.db ".schema" ``` fn-registry/ - functions/{domain}/ # .go + .md por funcion (core, finance, datascience, cybersecurity) + functions/{domain}/ # .go + .md por funcion Y tipo Go (core, finance, datascience, cybersecurity) functions/pipelines/ # Composiciones, siempre impuras - types/{domain}/ # .go + .md por tipo + types/{domain}/ # Solo .md de tipos (los .go viven en functions/{domain}/) + python/functions/ # .py + .md por funcion Python + python/types/ # .py + .md por tipo Python + bash/functions/ # .sh + .md por funcion Bash (core, infra, io, shell) frontend/ # pnpm + vite + react + tailwind + shadcn frontend/functions/ # .tsx/.ts + .md (core para TS puro, ui para componentes React) frontend/types/ # .ts + .md por tipo @@ -91,6 +94,23 @@ fn list [-d domain] [-k kind] fn show fn add -k function # Template +# Ejecutar funciones y pipelines (fn run) +fn run [args...] # Ejecuta por ID o nombre +fn run init_metabase --project test # Go pipeline (go run .) +fn run setup_metabase_volume # Bash pipeline (bash ) +fn run metabase_setup_py_infra # Python (python/.venv/bin/python3 ) +fn run my_component_ts_core # TypeScript (frontend/node_modules/.bin/tsx ) +fn run filter_slice_go_core # Go function con tests (go test -v) +fn run docker_pull_image_go_infra # Go function sin tests (go vet) +# Despacho por lenguaje: +# go (con main.go en dir) → go run . +# go (con tests) → go test -v -count=1 -tags fts5 ./pkg/ +# go (sin tests) → go vet -tags fts5 ./pkg/ +# py → python/.venv/bin/python3 +# bash → bash +# ts → frontend/node_modules/.bin/tsx +# Si el nombre es ambiguo, muestra los IDs para desambiguar. + # Proposals fn proposal add --kind new_function --title "..." --created-by agent [--target-id ] fn proposal list [-k kind] [-s status] @@ -110,12 +130,30 @@ fn ops assertion result add|list `FN_REGISTRY_ROOT` env var permite que `fn ops` acceda a registry.db desde cualquier directorio. +### Uso de fn run por agentes + +`fn run` permite ejecutar directamente funciones y pipelines del registry desde la terminal. Usar para: +- Lanzar pipelines con sus argumentos: `./fn run init_metabase --project fn_registry` +- Correr tests de funciones Go: `./fn run filter_slice_go_core` +- Ejecutar scripts Python/Bash del registry sin montar paths manualmente +- Verificar que funciones Go compilan correctamente (go vet) + +Entornos usados automaticamente: +- Python: `python/.venv/bin/python3` (venv del proyecto) +- TypeScript: `frontend/node_modules/.bin/tsx` (node del proyecto) +- Go: `go run .` / `go test` / `go vet` con `CGO_ENABLED=1 -tags fts5` +- Bash: `bash` del sistema + --- ## Añadir funciones 1. Consulta la BD para verificar que no existe algo similar -2. Crea dos archivos: `functions/{domain}/{name}.go` + `functions/{domain}/{name}.md` +2. Crea dos archivos segun el lenguaje: + - Go: `functions/{domain}/{name}.go` + `.md` + - Python: `python/functions/{domain}/{name}.py` + `.md` + - Bash: `bash/functions/{domain}/{name}.sh` + `.md` + - TypeScript: `frontend/functions/{domain}/{name}.ts` + `.md` 3. Ejecuta `./fn index` y verifica con `./fn show {id}` Frontmatter del .md — ver template completo en `docs/templates/` o con `fn add -k function`. @@ -132,7 +170,13 @@ Reglas de integridad (el indexer las valida): ## Añadir tipos -Dos archivos: `types/{domain}/{name}.go` + `types/{domain}/{name}.md`. Ver template en `docs/templates/`. +Dos archivos en directorios separados: +- **Codigo Go:** `functions/{domain}/{name}.go` (junto a las funciones, mismo paquete Go) +- **Metadata .md:** `types/{domain}/{name}.md` con `file_path` apuntando a `functions/{domain}/{name}.go` + +Los `.go` de tipos viven en `functions/{domain}/` para que Go los compile en el mismo paquete que las funciones que los usan. Los `.md` se mantienen en `types/{domain}/` para que el indexer los identifique como tipos. + +Ver template en `docs/templates/`. --- diff --git a/bash/functions/infra/assert_docker_container_running.md b/bash/functions/infra/assert_docker_container_running.md new file mode 100644 index 00000000..734792b9 --- /dev/null +++ b/bash/functions/infra/assert_docker_container_running.md @@ -0,0 +1,38 @@ +--- +name: assert_docker_container_running +kind: function +lang: bash +domain: infra +version: "1.0.0" +purity: impure +signature: "assert_docker_container_running(container_name: string) -> void" +description: "Verifica que un contenedor Docker está corriendo. Sale con exit code 1 si no está activo, con mensaje a stderr." +tags: [assert, docker, container, running, validation, infra, bash] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/infra/assert_docker_container_running.sh" +--- + +## Ejemplo + +```bash +source functions/infra/assert_docker_container_running.sh + +assert_docker_container_running metabase +echo "Contenedor activo, continuando..." +``` + +## Notas + +Usa `docker ps --format '{{.Names}}'` con grep anclado (`^name$`) para evitar matches parciales (ej: "metabase" no matchea "metabase-test"). + +Output limpio: void en éxito. El mensaje de error en stderr no incluye lista de contenedores activos — eso es responsabilidad del pipeline/caller. + +Requiere que `docker` esté en PATH. Combinar con `assert_command_exists` antes de llamar. diff --git a/bash/functions/infra/assert_docker_container_running.sh b/bash/functions/infra/assert_docker_container_running.sh new file mode 100644 index 00000000..fc0a4a7c --- /dev/null +++ b/bash/functions/infra/assert_docker_container_running.sh @@ -0,0 +1,19 @@ +# assert_docker_container_running +# -------------------------------- +# Verifica que un contenedor Docker está corriendo. +# No produce output a stdout en caso de éxito. +# Sale con exit code 1 si el contenedor no está corriendo, +# con mensaje descriptivo a stderr. +# +# USO (sourced): +# source assert_docker_container_running.sh +# assert_docker_container_running metabase + +assert_docker_container_running() { + local container_name="$1" + + if ! docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then + echo "assert_docker_container_running: el contenedor '$container_name' no está corriendo" >&2 + return 1 + fi +} diff --git a/bash/functions/infra/docker_cp_file.md b/bash/functions/infra/docker_cp_file.md new file mode 100644 index 00000000..892dc922 --- /dev/null +++ b/bash/functions/infra/docker_cp_file.md @@ -0,0 +1,41 @@ +--- +name: docker_cp_file +kind: function +lang: bash +domain: infra +version: "1.0.0" +purity: impure +signature: "docker_cp_file(local_path: string, container_name: string, dest_path: string) -> string" +description: "Copia un archivo local a un contenedor Docker y verifica que el tamaño coincide. Imprime JSON con local_size y remote_size a stdout. Sale con exit code 1 si docker cp falla o los tamaños difieren." +tags: [docker, cp, copy, file, container, transfer, infra, bash] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/infra/docker_cp_file.sh" +--- + +## Ejemplo + +```bash +source functions/infra/docker_cp_file.sh + +result=$(docker_cp_file /home/lucas/fn_registry/registry.db metabase /registry.db) +echo "$result" +# {"local_size":524288,"remote_size":524288} + +local_size=$(echo "$result" | grep -o '"local_size":[0-9]*' | cut -d: -f2) +``` + +## Notas + +La verificación de tamaño usa `docker exec stat -c%s` sobre el contenedor destino. Si `stat` no está disponible en el contenedor, `remote_size` será -1 y la función fallará. + +Output a stdout: JSON minificado con campos `local_size` y `remote_size` (enteros, bytes). + +Usa `printf` en lugar de `echo` para garantizar que no haya newline extra en el JSON. diff --git a/bash/functions/infra/docker_cp_file.sh b/bash/functions/infra/docker_cp_file.sh new file mode 100644 index 00000000..adf96d1f --- /dev/null +++ b/bash/functions/infra/docker_cp_file.sh @@ -0,0 +1,33 @@ +# docker_cp_file +# -------------- +# Copia un archivo local a un contenedor Docker y verifica que el tamaño coincide. +# Imprime JSON con local_size y remote_size a stdout si la copia es exitosa. +# Sale con exit code 1 si docker cp falla o si los tamaños no coinciden. +# +# USO (sourced): +# source docker_cp_file.sh +# result=$(docker_cp_file /ruta/local.db metabase /dest/path.db) + +docker_cp_file() { + local local_path="$1" + local container_name="$2" + local dest_path="$3" + + if ! docker cp "$local_path" "${container_name}:${dest_path}" 2>/dev/null; then + echo "docker_cp_file: fallo al copiar '$local_path' a '${container_name}:${dest_path}'" >&2 + return 1 + fi + + local local_size + local_size=$(stat -c%s "$local_path") + + local remote_size + remote_size=$(docker exec "$container_name" stat -c%s "$dest_path" 2>/dev/null || echo "-1") + + if [ "$local_size" != "$remote_size" ]; then + echo "docker_cp_file: tamaños no coinciden (local=${local_size}, remoto=${remote_size})" >&2 + return 1 + fi + + printf '{"local_size":%s,"remote_size":%s}' "$local_size" "$remote_size" +} diff --git a/bash/functions/pipelines/setup_metabase_volume.md b/bash/functions/pipelines/setup_metabase_volume.md new file mode 100644 index 00000000..89d49ac8 --- /dev/null +++ b/bash/functions/pipelines/setup_metabase_volume.md @@ -0,0 +1,65 @@ +--- +name: setup_metabase_volume +kind: pipeline +lang: bash +domain: pipelines +version: "1.0.0" +purity: impure +signature: "setup_metabase_volume([registry_db_path: string], [container_name: string], [dest_path: string]) -> void" +description: "Copia registry.db al contenedor Docker de Metabase verificando existencia del archivo, disponibilidad de docker, estado del contenedor y coincidencia de tamaños. Todos los argumentos son opcionales con defaults razonables." +tags: [metabase, docker, setup, launcher, pipeline, bash, infra] +uses_functions: + - assert_file_exists_bash_shell + - assert_command_exists_bash_shell + - assert_docker_container_running_bash_infra + - docker_cp_file_bash_infra +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/pipelines/setup_metabase_volume.sh" +--- + +## Ejemplo + +```bash +# Con defaults +./functions/pipelines/setup_metabase_volume.sh + +# Con argumentos explícitos +./functions/pipelines/setup_metabase_volume.sh \ + /home/lucas/fn_registry/registry.db \ + metabase \ + /registry.db +``` + +## Flujo + +1. `assert_file_exists` — verifica que `registry.db` existe localmente y obtiene su tamaño +2. `assert_command_exists` — verifica que `docker` está disponible en PATH +3. `assert_docker_container_running` — verifica que el contenedor destino está activo; si falla, muestra lista de contenedores activos +4. `docker_cp_file` — ejecuta `docker cp` y verifica que los tamaños local y remoto coinciden + +## Notas + +El pipeline usa `set -euo pipefail` — cualquier fallo en una función individual detiene la ejecución. + +Las funciones individuales se sourcean desde sus rutas en el registry, relativas a `REGISTRY_ROOT` detectado automáticamente desde la ubicación del script. + +Defaults: +- `REGISTRY_DB_PATH`: `/home/lucas/fn_registry/registry.db` +- `CONTAINER_NAME`: `metabase` +- `DEST_PATH`: `/registry.db` + +Nota de persistencia: `docker cp` copia al contenedor en ejecución. Si el contenedor se reinicia, el archivo se pierde. Para persistencia real, montar el directorio como volumen en docker-compose: + +```yaml +volumes: + - /home/lucas/fn_registry:/fn_registry:ro +``` + +Y usar `--registry-db-path /fn_registry/registry.db`. diff --git a/bash/functions/pipelines/setup_metabase_volume.sh b/bash/functions/pipelines/setup_metabase_volume.sh new file mode 100755 index 00000000..8468458b --- /dev/null +++ b/bash/functions/pipelines/setup_metabase_volume.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# setup_metabase_volume +# --------------------- +# Copia registry.db al contenedor Docker de Metabase. +# Compone: assert_file_exists + assert_command_exists + +# assert_docker_container_running + docker_cp_file +# +# USO: +# ./setup_metabase_volume.sh [REGISTRY_DB_PATH] [CONTAINER_NAME] [DEST_PATH] +# +# ARGUMENTOS (opcionales, con defaults): +# REGISTRY_DB_PATH Ruta local al registry.db +# Default: /home/lucas/fn_registry/registry.db +# CONTAINER_NAME Nombre del contenedor Docker de Metabase +# Default: metabase +# DEST_PATH Ruta destino dentro del contenedor +# Default: /registry.db + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REGISTRY_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" + +source "$REGISTRY_ROOT/bash/functions/shell/assert_file_exists.sh" +source "$REGISTRY_ROOT/bash/functions/shell/assert_command_exists.sh" +source "$REGISTRY_ROOT/bash/functions/infra/assert_docker_container_running.sh" +source "$REGISTRY_ROOT/bash/functions/infra/docker_cp_file.sh" + +REGISTRY_DB_PATH="${1:-/home/lucas/fn_registry/registry.db}" +CONTAINER_NAME="${2:-metabase}" +DEST_PATH="${3:-/registry.db}" + +echo "[setup_metabase_volume] Configuracion:" +echo " registry.db local : $REGISTRY_DB_PATH" +echo " contenedor Docker : $CONTAINER_NAME" +echo " ruta en contenedor : $DEST_PATH" +echo "" + +# 1. Verificar archivo local +local_size=$(assert_file_exists "$REGISTRY_DB_PATH") +echo "[setup_metabase_volume] Archivo local encontrado: ${local_size} bytes" + +# 2. Verificar que docker esta disponible +assert_command_exists docker +echo "[setup_metabase_volume] docker disponible en PATH." + +# 3. Verificar que el contenedor esta corriendo +if ! assert_docker_container_running "$CONTAINER_NAME"; then + echo "" >&2 + echo "Contenedores activos:" >&2 + docker ps --format " {{.Names}}\t{{.Status}}\t{{.Image}}" >&2 + echo "" >&2 + echo "Si el contenedor se llama diferente, pasa el nombre como segundo argumento:" >&2 + echo " ./setup_metabase_volume.sh $REGISTRY_DB_PATH " >&2 + exit 1 +fi +echo "[setup_metabase_volume] Contenedor '$CONTAINER_NAME' encontrado y activo." + +# 4. Copiar archivo y verificar tamaños +echo "[setup_metabase_volume] Copiando $REGISTRY_DB_PATH -> ${CONTAINER_NAME}:${DEST_PATH} ..." +result=$(docker_cp_file "$REGISTRY_DB_PATH" "$CONTAINER_NAME" "$DEST_PATH") + +remote_size=$(echo "$result" | grep -o '"remote_size":[0-9]*' | cut -d: -f2) +echo "[setup_metabase_volume] OK Copia completada y verificada." +echo "[setup_metabase_volume] Tamanio: ${local_size} bytes (local) = ${remote_size} bytes (remoto)" + +echo "" +echo "---------------------------------------------------------------------" +echo "registry.db disponible en el contenedor como: $DEST_PATH" +echo "" +echo "Ahora ejecuta main.py con:" +echo "" +echo " METABASE_ADMIN_PASSWORD= \\" +echo " REGISTRY_DB_PATH=${DEST_PATH} \\" +echo " python apps/metabase_registry/main.py" +echo "" +echo "O bien:" +echo "" +echo " python apps/metabase_registry/main.py \\" +echo " --admin-password \\" +echo " --registry-db-path ${DEST_PATH}" +echo "---------------------------------------------------------------------" diff --git a/bash/functions/shell/assert_command_exists.md b/bash/functions/shell/assert_command_exists.md new file mode 100644 index 00000000..46e1f958 --- /dev/null +++ b/bash/functions/shell/assert_command_exists.md @@ -0,0 +1,36 @@ +--- +name: assert_command_exists +kind: function +lang: bash +domain: shell +version: "1.0.0" +purity: impure +signature: "assert_command_exists(command_name: string) -> void" +description: "Verifica que un comando está disponible en el PATH. Sale con exit code 1 si no se encuentra, con mensaje a stderr." +tags: [assert, command, exists, validation, shell, bash, path] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/shell/assert_command_exists.sh" +--- + +## Ejemplo + +```bash +source functions/shell/assert_command_exists.sh + +assert_command_exists docker +assert_command_exists jq +``` + +## Notas + +Usa `command -v` (POSIX) con redirección `&>/dev/null` para suprimir output. No produce nada a stdout en caso de éxito. + +Output limpio: void en éxito, mensaje a stderr en fallo. diff --git a/bash/functions/shell/assert_command_exists.sh b/bash/functions/shell/assert_command_exists.sh new file mode 100644 index 00000000..fddc16c8 --- /dev/null +++ b/bash/functions/shell/assert_command_exists.sh @@ -0,0 +1,18 @@ +# assert_command_exists +# --------------------- +# Verifica que un comando está disponible en el PATH. +# No produce output a stdout. +# Sale con exit code 1 si el comando no se encuentra. +# +# USO (sourced): +# source assert_command_exists.sh +# assert_command_exists docker + +assert_command_exists() { + local command_name="$1" + + if ! command -v "$command_name" &>/dev/null; then + echo "assert_command_exists: comando no encontrado en PATH: $command_name" >&2 + return 1 + fi +} diff --git a/bash/functions/shell/assert_file_exists.md b/bash/functions/shell/assert_file_exists.md new file mode 100644 index 00000000..5c9a35a4 --- /dev/null +++ b/bash/functions/shell/assert_file_exists.md @@ -0,0 +1,38 @@ +--- +name: assert_file_exists +kind: function +lang: bash +domain: shell +version: "1.0.0" +purity: impure +signature: "assert_file_exists(file_path: string) -> string" +description: "Verifica que un archivo existe en el filesystem. Imprime su tamaño en bytes a stdout. Sale con exit code 1 si el archivo no existe." +tags: [assert, file, exists, validation, shell, bash] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/shell/assert_file_exists.sh" +--- + +## Ejemplo + +```bash +source functions/shell/assert_file_exists.sh + +size=$(assert_file_exists /home/lucas/fn_registry/registry.db) +echo "Tamaño: $size bytes" +``` + +## Notas + +La función se sourcea, no se ejecuta directamente. Usa `stat -c%s` para obtener el tamaño en bytes (compatible con GNU coreutils / Linux). + +Output limpio: solo el número de bytes a stdout. Los errores van a stderr. + +No usa `set -e` internamente — el caller controla el flujo con el exit code de retorno. diff --git a/bash/functions/shell/assert_file_exists.sh b/bash/functions/shell/assert_file_exists.sh new file mode 100644 index 00000000..05299b2d --- /dev/null +++ b/bash/functions/shell/assert_file_exists.sh @@ -0,0 +1,20 @@ +# assert_file_exists +# ------------------ +# Verifica que un archivo existe en el filesystem. +# Imprime su tamaño en bytes a stdout. +# Sale con exit code 1 si el archivo no existe. +# +# USO (sourced): +# source assert_file_exists.sh +# size=$(assert_file_exists /ruta/al/archivo) + +assert_file_exists() { + local file_path="$1" + + if [ ! -f "$file_path" ]; then + echo "assert_file_exists: archivo no encontrado: $file_path" >&2 + return 1 + fi + + stat -c%s "$file_path" +}