chore: auto-commit (799 archivos)

- .claude/CLAUDE.md
- .claude/commands/subagentes.md
- .claude/rules/INDEX.md
- .mcp.json
- bash/functions/cybersecurity/analyze_dns.md
- bash/functions/cybersecurity/audit_http_headers.md
- bash/functions/cybersecurity/audit_ssh_config.md
- bash/functions/cybersecurity/check_firewall.md
- bash/functions/cybersecurity/detect_suspicious_users.md
- bash/functions/cybersecurity/encrypt_file.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 00:28:20 +02:00
parent 20f72edb5a
commit 47fac22230
805 changed files with 5515 additions and 810 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "analyze_dns(domain: string, mode: string) -> void"
description: "Análisis DNS completo de un dominio: registros A/AAAA/MX/NS/TXT/CNAME/SOA, consulta whois y verificación contra listas negras DNSBL (spamhaus, spamcop, sorbs, barracuda)."
tags: [bash, cybersecurity, dns, network, whois, dnsbl, reconnaissance]
tags: [bash, cybersecurity, dns, network, whois, dnsbl, reconnaissance, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "audit_http_headers(url: string) -> void"
description: "Audita las cabeceras HTTP de seguridad de una URL: verifica la presencia de HSTS (con validación de max-age mínimo de 6 meses), Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy y cabeceras CORS. También detecta cabeceras que exponen información del servidor."
tags: [bash, cybersecurity, web, http, headers, security, hsts, csp, hardening]
tags: [bash, cybersecurity, web, http, headers, security, hsts, csp, hardening, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "audit_ssh_config(config_path: string) -> void"
description: "Audita la configuración de sshd_config evaluando parámetros de seguridad críticos (PermitRootLogin, PasswordAuthentication, Port, MaxAuthTries, X11Forwarding, AllowUsers). También revisa intentos de login fallidos en los logs y lista las claves autorizadas del usuario actual."
tags: [bash, cybersecurity, ssh, audit, security, hardening, linux]
tags: [bash, cybersecurity, ssh, audit, security, hardening, linux, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "check_firewall() -> void"
description: "Detecta el firewall activo del sistema (ufw, firewalld o iptables) y muestra su estado, reglas activas y puertos en escucha para cruzar con las reglas. Si no se detecta ningún firewall, emite una advertencia de exposición."
tags: [bash, cybersecurity, firewall, ufw, iptables, network, hardening, linux]
tags: [bash, cybersecurity, firewall, ufw, iptables, network, hardening, linux, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "detect_suspicious_users() -> void"
description: "Revisa el sistema en busca de indicadores de compromiso en cuentas de usuario: UIDs 0 extras (además de root), usuarios con shell de login válida, homes en rutas inusuales, miembros de grupos privilegiados (sudo, docker, wheel, adm, etc.) y sesiones activas."
tags: [bash, cybersecurity, users, audit, linux, privilege-escalation, hardening]
tags: [bash, cybersecurity, users, audit, linux, privilege-escalation, hardening, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "encrypt_file(mode: string, file: string) -> void"
description: "Cifra o descifra un archivo usando AES-256-CBC con PBKDF2 (310.000 iteraciones) via openssl. La contraseña se lee de la variable de entorno ENCRYPT_PASSWORD o se solicita interactivamente. El archivo cifrado se guarda con extensión .enc; al descifrar se recupera el nombre original."
tags: [bash, cybersecurity, encryption, aes256, openssl, crypto, pbkdf2]
tags: [bash, cybersecurity, encryption, aes256, openssl, crypto, pbkdf2, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "enumerate_subdomains(domain: string, output_file: string) -> void"
description: "Enumera subdominios de un dominio objetivo usando un diccionario integrado de ~100 subdominios comunes (www, mail, api, dev, admin, vpn, etc.). Detecta tanto registros A (IP directa) como CNAME. Muestra progreso cada 20 subdominios y opcionalmente guarda los resultados en un archivo."
tags: [bash, cybersecurity, dns, subdomain, enumeration, reconnaissance, osint]
tags: [bash, cybersecurity, dns, subdomain, enumeration, reconnaissance, osint, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "generate_password(mode: string, length: int, count: int) -> void"
description: "Genera contraseñas seguras en cuatro modos: full (alfanumérico + símbolos, excluye caracteres ambiguos), alpha (solo alfanumérico), passphrase (palabras aleatorias unidas con guión) y pin (numérico). Calcula y muestra la entropía en bits para cada modo."
tags: [bash, cybersecurity, password, generator, entropy, security, urandom]
tags: [bash, cybersecurity, password, generator, entropy, security, urandom, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "geolocate_ip(target: string) -> void"
description: "Geolocaliza una dirección IP o dominio usando la API pública de ip-api.com. Muestra país, región, ciudad, coordenadas, ISP, ASN y detecta VPN, Proxy o infraestructura de hosting."
tags: [bash, cybersecurity, network, geoip, ip, osint, reconnaissance]
tags: [bash, cybersecurity, network, geoip, ip, osint, reconnaissance, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "inspect_ssl_cert(host: string) -> void"
description: "Inspecciona el certificado SSL/TLS de un host: muestra sujeto, emisor, fechas de validez, días hasta expiración, SANs (Subject Alternative Names), cadena de confianza completa y detecta soporte de versiones inseguras TLS 1.0/1.1."
tags: [bash, cybersecurity, ssl, tls, certificate, web, openssl, security]
tags: [bash, cybersecurity, ssl, tls, certificate, web, openssl, security, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "list_active_connections(mode: string) -> void"
description: "Muestra conexiones de red activas del sistema usando ss: puertos en escucha, conexiones establecidas y detección de conexiones hacia IPs externas (excluye RFC1918, loopback y link-local)."
tags: [bash, cybersecurity, network, connections, monitoring, ss, ports]
tags: [bash, cybersecurity, network, connections, monitoring, ss, ports, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "verify_file_hash(file: string, algorithm: string, expected_hash: string) -> void"
description: "Calcula el hash criptográfico de un archivo con el algoritmo especificado (md5, sha1, sha256, sha512) y opcionalmente lo compara con un hash esperado para verificar integridad. Retorna exit code 1 si los hashes no coinciden."
tags: [bash, cybersecurity, hash, integrity, checksum, md5, sha256, sha512]
tags: [bash, cybersecurity, hash, integrity, checksum, md5, sha256, sha512, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "analyze_disk_space([target_dir: string], [mode: string]) -> void"
description: "Analiza el uso de espacio en disco. Modos: partitions (df con filtros), top-dirs (du top 10), top-files (find top 20), inodes (df -i), all (todos). Emite advertencias si el uso supera el 90%."
tags: [bash, disk, space, analysis, filesystem]
tags: [bash, disk, space, analysis, filesystem, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_app_clear([--serial <S>], package: string) -> void"
description: "Wipe app data + cache via pm clear. App keeps installed but factory-state. Multi-emulator via --serial."
tags: [android, adb, app, clear, reset]
tags: [android, adb, app, clear, reset, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device/emulator serial. Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_app_info([--serial <S>], package, [--json]) -> stdout"
description: "Inspect installed app: version, target SDK, activities via dumpsys package."
tags: [android, adb, app, info, dumpsys]
tags: [android, adb, app, info, dumpsys, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional ADB serial to target a specific device/emulator. Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_app_kill([--serial <S>], package: string) -> void"
description: "Force-stop running app via am force-stop. Multi-emulator via --serial."
tags: [android, adb, app, kill, force-stop]
tags: [android, adb, app, kill, force-stop, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device/emulator serial. Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_app_launch([--serial <S>], package: string, [activity: string]) -> void"
description: "Launch app activity via am start. Multi-emulator via --serial."
tags: [android, adb, app, launch, activity]
tags: [android, adb, app, launch, activity, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target serial. Default: first device"
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_app_uninstall([--serial <S>] package [--keep-data]) -> void"
description: "Uninstall app via adb uninstall. Optionally keep data with --keep-data."
tags: [android, adb, app, uninstall]
tags: [android, adb, app, uninstall, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device/emulator serial. Auto-detects first connected device if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_emu_battery([--serial <S>], level: int, [--charging <true|false>]) -> void"
description: "Simulate battery state on emulator (level + charging). Emulator-only."
tags: [android, emulator, battery, power]
tags: [android, emulator, battery, power, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional emulator serial (e.g. emulator-5554). Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_emu_geo_fix([--serial <S>], longitude: string, latitude: string, [altitude: string]) -> void"
description: "Fake GPS location on Android emulator via emu geo fix. Emulator-only (not physical devices)."
tags: [android, emulator, geo, gps, location]
tags: [android, emulator, geo, gps, location, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional emulator serial. Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_emu_rotate([--serial <S>] [portrait|landscape|0|90|180|270])"
description: "Rotate emulator screen. Empty=toggle, or fixed orientation. Locks autorotate."
tags: [android, emulator, rotation, orientation]
tags: [android, emulator, rotation, orientation, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_input_keyevent([--serial <S>] key: string)"
description: "Send key event via adb shell input keyevent. Accepts aliases (BACK, HOME, POWER, ENTER, MENU, RECENT_APPS, VOLUME_UP, VOLUME_DOWN), raw numeric codes, or explicit KEYCODE_* names."
tags: [android, adb, input, keyevent, ui-test]
tags: [android, adb, input, keyevent, ui-test, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device/emulator serial. If omitted, adb_pick_serial resolves the single connected device."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_input_swipe([--serial <S>], x1: int, y1: int, x2: int, y2: int, [duration_ms: int])"
description: "Send swipe gesture between two points with duration."
tags: [android, adb, input, swipe, gesture, ui-test]
tags: [android, adb, input, swipe, gesture, ui-test, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_input_tap([--serial <S>], x: int, y: int) -> void"
description: "Send tap gesture at screen coordinates via adb shell input tap."
tags: [android, adb, input, tap, ui-test, gesture]
tags: [android, adb, input, tap, ui-test, gesture, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device serial. Auto-detected if omitted."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_input_text([--serial <S>], text: string) -> void"
description: "Type text in focused field via adb shell input text. Spaces handled."
tags: [android, adb, input, text, ui-test]
tags: [android, adb, input, text, ui-test, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_pull [--serial <S>] remote_path local_path"
description: "Pull file/dir from Android device to WSL via adb pull."
tags: [android, adb, pull, file, transfer]
tags: [android, adb, pull, file, transfer, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_push([--serial <S>], local_path: string, remote_path: string) -> void"
description: "Push file/dir from WSL to Android device via adb push."
tags: [android, adb, push, file, transfer]
tags: [android, adb, push, file, transfer, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device/emulator serial. Auto-detected if omitted."
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_screen_record([--serial <S>] [--duration <s>] [--bit-rate <bps>] [--size <WxH>] output_path: string) -> void"
description: "Record screen video via adb screenrecord, pulls to local path."
tags: [android, adb, screen, record, video]
tags: [android, adb, screen, record, video, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_screenshot([--serial <S>], output_path: string) -> void"
description: "Capture screen as PNG via adb exec-out screencap -p."
tags: [android, adb, screenshot, screen, capture]
tags: [android, adb, screenshot, screen, capture, pendiente-usar]
uses_functions: [adb_wsl_bash_infra]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "android_shell([--serial <S>], cmd ...args)"
description: "Execute arbitrary shell command on Android device. Multi-emulator via --serial."
tags: [android, adb, shell, exec]
tags: [android, adb, shell, exec, pendiente-usar]
params:
- name: "--serial <S>"
desc: "Optional target device serial. Omit to auto-pick (single device) or use ADB_SERIAL env."
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "append_diary_entry(titulo: string, cuerpo: string) -> string"
description: "Añade una entrada al diario del dia en ${DIARY_DIR:-docs/diary}/YYYY-MM-DD.md. Crea el archivo con cabecera si no existe. Nunca reescribe contenido previo. Si cuerpo es vacio escribe solo el header de la seccion."
tags: [diary, markdown, append, idempotent, infra]
tags: [diary, markdown, append, idempotent, infra, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "build_cpp_linux(target?: string) -> void"
description: "Compila las funciones y apps C++ del registry para Linux nativo usando cmake"
tags: [cpp, build, cmake, linux, imgui]
tags: [cpp, build, cmake, linux, imgui, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "0.1.0"
purity: impure
signature: "build_wasm_cpp_app(app_name: string, [--no-budget-check]) -> void"
description: "Compila una app C++ del registry (cpp/apps/<name>) a WASM via emscripten. Sale build/wasm/<name>/<name>.{html,js,wasm,wasm.gz}. Falla si gzip > 2 MB."
tags: [wasm, emscripten, cpp, build, gamedev]
tags: [wasm, emscripten, cpp, build, gamedev, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "cuda_toolkit_check() -> void"
description: "Detecta componentes CUDA instalados en el sistema y emite pares key=value a stdout: nvcc (version o missing), nvidia_smi (present/missing), driver_version, cuda_libs (path o missing) y overall (ok|partial|missing). Exit code 0 siempre — funcion informativa, no fatal."
tags: [cuda, nvidia, gpu, hardware, probe, infra, toolkit]
tags: [cuda, nvidia, gpu, hardware, probe, infra, toolkit, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "detect_wsl([--check]) -> void"
description: "Detecta si el sistema es WSL (Windows Subsystem for Linux). Con --check retorna solo exit code (0=WSL, 1=no WSL) sin output. Sin argumentos imprime versión WSL, usuario Windows, distribución, hostname, unidades montadas y ruta Windows del directorio actual."
tags: [bash, wsl, windows, detect, integration]
tags: [bash, wsl, windows, detect, integration, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -3,7 +3,7 @@ name: e2e_run_cpp_windows
lang: bash
domain: infra
description: "Cross-compila una app C++ del registry para Windows con mingw-w64, deploy al Desktop\\apps de Windows (matando instancia previa con taskkill.exe), lanza el .exe nativamente desde WSL y devuelve stdout + exit code. Pensado para tests headless tipo altsnap_jitter_test."
tags: [windows, e2e, cross-compile, test, mingw]
tags: [windows, e2e, cross-compile, test, mingw, pendiente-usar]
purity: impure
kind: function
signature: "e2e_run_cpp_windows(target string, --no-build, --no-deploy) int"
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "frontend_doctor(project_dir: string) -> diagnostics_stdout"
description: "Diagnostica la salud de un proyecto frontend Mantine. Verifica Node, React, Mantine, PostCSS, TypeScript, vite.config y detecta residuos de shadcn/@base-ui. Imprime tabla de checks con exit code 0/1."
tags: [frontend, mantine, doctor, diagnostics, health, validation]
tags: [frontend, mantine, doctor, diagnostics, health, validation, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: 1.0.0
purity: impure
signature: "git_hook_audit_app_drift <repo_dir>"
description: "Pre-commit guard: bloquea commit si los archivos staged tocan una app cuyo app.md tiene drift de uses_functions. Permite ediciones a app.md (correcciones)."
tags: [git, hook, precommit, registry-first, audit]
tags: [git, hook, precommit, registry-first, audit, pendiente-usar]
uses_functions:
- audit_uses_functions_go_infra
uses_types: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "gitea_create_webhook(owner: string, repo: string, target_url: string, secret?: string) -> json"
description: "Crea un webhook de push en un repositorio Gitea. El webhook notifica a target_url en cada push."
tags: [gitea, webhook, push, deploy, ci, infra]
tags: [gitea, webhook, push, deploy, ci, infra, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "gitea_list_repos(owner: string) -> string"
description: "Lista repositorios de un owner en Gitea. Intenta listar como org primero; si falla, lista como usuario. Imprime una línea por repo en formato name<TAB>html_url<TAB>description."
tags: [gitea, git, repo, list, org, user, api, infra]
tags: [gitea, git, repo, list, org, user, api, infra, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "gradle_clean(project_dir: string) -> int"
description: "Limpia build artifacts de un proyecto Android (gradle clean + rm .gradle + rm build)."
tags: [android, gradle, clean, build]
tags: [android, gradle, clean, build, pendiente-usar]
params:
- name: project_dir
desc: "Raiz del proyecto Gradle"
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_cpp_deps() -> void"
description: "Verifica e instala las dependencias de sistema necesarias para compilar C++ con ImGui (cmake, g++, glfw, mesa)"
tags: [cpp, dependencies, setup, cmake, imgui]
tags: [cpp, dependencies, setup, cmake, imgui, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_mantine(project_dir: string) -> void"
description: "Instala Mantine UI con todas sus dependencias (@mantine/core, hooks, charts, notifications, form) y PostCSS en un proyecto frontend. Detecta package manager por lockfile. Genera postcss.config.cjs si no existe. Idempotente."
tags: [mantine, frontend, install, react, ui, postcss]
tags: [mantine, frontend, install, react, ui, postcss, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_nodejs([version: string]) -> void"
description: "Instala Node.js en Linux usando nvm. Instala nvm v0.39.7 si no está presente. Instala la versión de Node indicada, la activa con 'nvm use' y la configura como default. Idempotente si nvm ya está instalado."
tags: [bash, install, nodejs, nvm]
tags: [bash, install, nodejs, nvm, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_nordvpn() -> void"
description: "Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2). Configura repositorio oficial, instala paquete y habilita servicio nordvpnd. Idempotente."
tags: [vpn, nordvpn, install, infra, wsl2]
tags: [vpn, nordvpn, install, infra, wsl2, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_pnpm() -> void"
description: "Instala pnpm globalmente usando npm (npm install -g pnpm). Verifica que npm esté disponible. Idempotente: si pnpm ya está instalado, informa y termina sin hacer nada."
tags: [bash, install, pnpm, node]
tags: [bash, install, pnpm, node, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_python312() -> void"
description: "Instala Python 3.12 detectando la distribución Linux automáticamente. Ubuntu/Debian/Mint usan deadsnakes PPA; Fedora/RHEL usan dnf; Arch/Manjaro usan pacman. Instala también python3.12-venv, python3.12-dev y verifica pip. Idempotente."
tags: [bash, install, python, python312]
tags: [bash, install, python, python312, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_uv() -> void"
description: "Instala uv, el gestor de paquetes Python ultra-rápido escrito en Rust, usando el instalador oficial de astral.sh. Configura PATH en ~/.bashrc y ~/.zshrc. Idempotente: si uv ya está instalado, informa y termina."
tags: [bash, install, uv, python]
tags: [bash, install, uv, python, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_volta() -> void"
description: "Instala Volta, el gestor de versiones de Node.js, usando el instalador oficial de get.volta.sh. Configura VOLTA_HOME y PATH en ~/.bashrc y ~/.zshrc. Idempotente: si Volta ya está instalado, informa y termina."
tags: [bash, install, volta, node]
tags: [bash, install, volta, node, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_delete(entry: string)"
description: "Elimina una entry del KeePassXC database via keepassxc-cli rm. La entry pasa a la papelera dentro del .kdbx (no se borra fisicamente)."
tags: [keepass, keepassxc, secret, credential, delete]
tags: [keepass, keepassxc, secret, credential, delete, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_generate(entry: string, length?: int, username?: string, url?: string) -> string"
description: "Genera un password aleatorio (lower+upper+digits+special), lo almacena en una entry nueva y lo imprime a stdout. Length default 24."
tags: [keepass, keepassxc, secret, credential, generate, random]
tags: [keepass, keepassxc, secret, credential, generate, random, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_get(entry: string, attr?: string) -> string"
description: "Lee un atributo (Password por defecto) de una entry del KeePassXC database via keepassxc-cli. Resuelve master password desde pass (meta/keepassxc-master) o env KEEPASS_PASSWORD."
tags: [keepass, keepassxc, secret, credential, get]
tags: [keepass, keepassxc, secret, credential, get, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_list(prefix?: string) -> json"
description: "Lista paths de entries del KeePassXC database como array JSON. Filtra opcionalmente por prefijo de grupo. Internamente usa keepass_dump y proyecta solo los paths."
tags: [keepass, keepassxc, list]
tags: [keepass, keepassxc, list, pendiente-usar]
uses_functions:
- keepass_dump_bash_infra
uses_types: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_search(term: string) -> json"
description: "Busca entries en el KeePassXC database por substring. Devuelve array JSON de paths que matchean (title/username/url/notes)."
tags: [keepass, keepassxc, search, query]
tags: [keepass, keepassxc, search, query, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "keepass_set(entry: string, password: string, username?: string, url?: string)"
description: "Crea o sobreescribe una entry en el KeePassXC database. Auto-detecta si existe (edit) o no (add). Soporta username y url opcionales."
tags: [keepass, keepassxc, secret, credential, set, write]
tags: [keepass, keepassxc, secret, credential, set, write, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "list_listening_ports([mode: string]) -> void"
description: "Lista puertos activos del sistema usando ss (preferido) o netstat como fallback. Modos: all (LISTEN), tcp, udp, established (conexiones activas), stats (resumen + interfaces). Imprime salida tabulada a stdout."
tags: [bash, ports, network, listening, monitoring]
tags: [bash, ports, network, listening, monitoring, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_connect(country?: string, city?: string) -> json"
description: "Conecta a NordVPN por pais, ciudad o servidor especifico. Sin argumentos conecta al mejor servidor disponible. Devuelve JSON con resultado."
tags: [vpn, nordvpn, connect, infra, network]
tags: [vpn, nordvpn, connect, infra, network, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_disconnect() -> json"
description: "Desconecta de NordVPN. Idempotente — si no hay conexion activa retorna ok. Devuelve JSON con resultado."
tags: [vpn, nordvpn, disconnect, infra, network]
tags: [vpn, nordvpn, disconnect, infra, network, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_get_ip() -> json"
description: "Obtiene IP publica actual con fallback entre multiples servicios. Indica si la conexion VPN esta activa y el servidor usado."
tags: [vpn, nordvpn, ip, infra, network, verification]
tags: [vpn, nordvpn, ip, infra, network, verification, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_list_cities(country: string) -> json"
description: "Lista ciudades disponibles de un pais en NordVPN como array JSON ordenado."
tags: [vpn, nordvpn, cities, infra, network]
tags: [vpn, nordvpn, cities, infra, network, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_list_countries() -> json"
description: "Lista paises disponibles en NordVPN como array JSON ordenado alfabeticamente."
tags: [vpn, nordvpn, countries, infra, network]
tags: [vpn, nordvpn, countries, infra, network, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_set_protocol(protocol: string) -> json"
description: "Cambia el protocolo de NordVPN entre NordLynx (WireGuard) y OpenVPN. NordLynx recomendado por velocidad."
tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra]
tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "nordvpn_status() -> json"
description: "Obtiene estado actual de NordVPN como JSON estructurado. Incluye servidor, IP, pais, protocolo y estado de conexion."
tags: [vpn, nordvpn, status, infra, network]
tags: [vpn, nordvpn, status, infra, network, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "pass_delete(entry: string) -> void"
description: "Elimina un secreto del password store (pass)."
tags: [pass, secret, credential, delete]
tags: [pass, secret, credential, delete, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "pass_generate(entry: string, [length: int]) -> string"
description: "Genera un password aleatorio, lo almacena en el password store e imprime el valor generado."
tags: [pass, secret, credential, generate, random]
tags: [pass, secret, credential, generate, random, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "pass_list([prefix: string]) -> json"
description: "Lista entradas del password store como JSON array. Filtra opcionalmente por prefijo."
tags: [pass, secret, credential, list]
tags: [pass, secret, credential, list, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "pass_sync() -> json"
description: "Sincroniza el password store con el repositorio git remoto (pull + push)."
tags: [pass, secret, sync, git]
tags: [pass, secret, sync, git, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "port_kill(port: int, signal: string) -> void"
description: "Mata los procesos que escuchan en un puerto TCP dado. Idempotente: si no hay proceso en el puerto retorna exit 0. Hace un segundo intento con SIGKILL si el primer intento con signal no libera el puerto."
tags: ["port", "kill", "process", "tcp"]
tags: [port, kill, process, tcp, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "pre_commit_hook_install(repo_dir: string, [--force]) -> void"
description: "Instala un hook pre-commit en .git/hooks/pre-commit de un repo dado. El hook invoca scan_secrets_in_dirty para abortar el commit si detecta secrets en archivos staged. Idempotente: si el hook ya esta instalado (marca fn_registry-pre-commit-v1) no lo sobreescribe a menos que se pase --force."
tags: ["git", "hook", "precommit", "secrets"]
tags: [git, hook, precommit, secrets, pendiente-usar]
uses_functions: ["scan_secrets_in_dirty_bash_cybersecurity"]
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "systemd_local_restart(name: string) -> json"
description: "Reinicia un servicio systemd local con systemctl restart. Útil tras actualizar el binario o cambiar el unit. Requiere sudo."
tags: [systemd, service, local, infra, restart]
tags: [systemd, service, local, infra, restart, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "systemd_local_uninstall(name: string) -> json"
description: "Detiene, deshabilita y elimina el unit file de un servicio systemd local. Idempotente: no falla si el servicio ya está parado o el unit no existe. Requiere sudo."
tags: [systemd, service, local, infra, uninstall, cleanup]
tags: [systemd, service, local, infra, uninstall, cleanup, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "tail_journal(unit: string, lines: int=100, follow: bool=false, since: string=\"\", priority: string=\"info\") -> void"
description: "Wrapper sobre journalctl con formato consistente. Tail logs de una unidad systemd con coloreado, filtro por prioridad y seguimiento opcional."
tags: ["journal", "systemd", "logs"]
tags: [journal, systemd, logs, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "tbd_branch_create(mode: string, ...args: string) -> void"
description: "Crea una rama TBD (trunk-based development) desde master/main actualizado. Soporta modos 'issue <NNNN> <slug>' y 'quick <slug>'. Autodetecta la rama base (master/main), verifica working tree limpio, hace pull --rebase y crea la rama. Valida formato de numero de issue (4 digitos) y slug (kebab-case ASCII)."
tags: [git, tbd, branch, trunk-based-development, workflow]
tags: [git, tbd, branch, trunk-based-development, workflow, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "tbd_branch_finish([merge_title: string]) -> void"
description: "Integra una rama TBD (issue/* o quick/*) a master/main con merge --no-ff, publica el merge al remote y elimina la rama local. Autodetecta la rama base (master/main), verifica working tree limpio y construye el titulo del merge commit. NO ejecuta tests — esa responsabilidad es del caller. Exit 2 si hay conflicto de merge (deja al usuario resolver)."
tags: [git, tbd, merge, trunk-based-development, workflow]
tags: [git, tbd, merge, trunk-based-development, workflow, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+55
View File
@@ -0,0 +1,55 @@
---
name: telemetry_prelude
lang: bash
domain: infra
version: 0.1.0
purity: impure
kind: function
description: "Prelude bash que envuelve cada funcion del registry definida en el shell con un wrapper que mide duration y registra cada llamada en call_monitor.operations.db. Activable con FN_TELEMETRY=1. Issue 0085c."
tags: [telemetry, monitoring, registry, bash-wrapper, pendiente-usar]
signature: "source telemetry_prelude.sh"
error_type: "error_go_core"
returns_optional: false
params:
- name: FN_TELEMETRY (env)
desc: "Si vale '1', el prelude auto-envuelve cada funcion bash conocida del registry. Si no, return 0 inmediato sin hacer nada."
- name: FN_REGISTRY_ROOT (env)
desc: "Override de la raiz. Si no se setea, se descubre walking up desde cwd buscando registry.db."
- name: CLAUDE_SESSION_ID (env)
desc: "ID de sesion Claude Code persistido en cada fila de calls."
output: "Sin output. Side effect: cada funcion del registry sourceada queda reemplazada por un wrapper con telemetria. Idempotente."
uses_functions: []
uses_types: []
imports:
- sqlite3 (CLI)
- date (coreutils)
- find (coreutils)
example: |
# Auto-wrap
export FN_TELEMETRY=1
source bash/functions/infra/android_screenshot.sh # define android_screenshot()
source bash/functions/infra/telemetry_prelude.sh # envuelve android_screenshot
android_screenshot /tmp/out.png # registrado en calls como android_screenshot_bash_infra
file_path: "bash/functions/infra/telemetry_prelude.sh"
tested: false
notes: |
Mecanismo: `declare -f <name>` extrae el cuerpo de la funcion. Se renombra
a `_fn_t_orig_<name>` via eval. La funcion original queda reemplazada por
un wrapper que mide `date +%s%3N` antes/despues, ejecuta `_fn_t_orig_<name>`,
captura exit code, y llama `_fn_t_log function_id duration_ms success error_class`.
function_id heuristic: `{name}_bash_{domain}` donde `name`=basename del .sh
y `domain`=basename del directorio padre. Coincide con convencion del registry.
Fail-safe: si la BD no existe, sqlite3 falta, o INSERT falla, el wrapper
ignora silenciosamente y retorna el exit code del original. NUNCA aborta
ni modifica el comportamiento de la funcion envuelta.
Idempotente: marca cada wrapper con `_FN_T_WRAPPED_<name>=1` y no
re-envuelve. Sourcear el prelude N veces es seguro.
Limitacion: el wrapper requiere que las funciones del registry ya esten
sourceadas antes de cargarse este prelude. Si la app sourcea una funcion
DESPUES del prelude, esa funcion NO queda envuelta automaticamente — hay
que llamar `_fn_t_autowrap` manualmente o usar `_fn_t_wrap <name> <id>`.
---
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
# telemetry_prelude — Wrapper de telemetria para funciones bash del registry.
# Issue 0085c-bash.
#
# Uso 1 (manual): source bash/functions/<dom>/<fn>.sh primero, despues source
# este archivo, y todas las funciones bash del registry
# quedaran envueltas con logging a call_monitor.operations.db.
#
# Uso 2 (auto): exportar FN_TELEMETRY=1 antes de sourcear; este prelude
# detecta cada funcion definida que coincida con una funcion
# del registry (bash/functions/<dom>/<name>.sh) y la
# redefine como wrapper.
#
# Reglas:
# - Idempotente: marca cada wrapper con flag _FN_T_WRAPPED_<name>=1; no
# re-envuelve si ya esta envuelta.
# - No-op silencioso si BD no existe o INSERT falla. NUNCA aborta el caller.
# - Solo guarda function_id, duration_ms, success, error_class. NUNCA args.
if [ "${FN_TELEMETRY:-0}" != "1" ]; then
return 0 2>/dev/null || exit 0
fi
# ---- Resolve registry root ----
_fn_t_root() {
if [ -n "${FN_REGISTRY_ROOT:-}" ] && [ -f "$FN_REGISTRY_ROOT/registry.db" ]; then
printf '%s' "$FN_REGISTRY_ROOT"
return 0
fi
local d="${PWD}"
while [ "$d" != "/" ]; do
if [ -f "$d/registry.db" ]; then
printf '%s' "$d"
return 0
fi
d=$(dirname "$d")
done
return 1
}
# ---- Resolve operations.db ----
_FN_T_DB=""
_fn_t_resolve_db() {
if [ -n "$_FN_T_DB" ] && [ -f "$_FN_T_DB" ]; then return 0; fi
local root
root=$(_fn_t_root) || return 1
_FN_T_DB="$root/projects/fn_monitoring/apps/call_monitor/operations.db"
if [ ! -f "$_FN_T_DB" ]; then
_FN_T_DB=""
return 1
fi
return 0
}
# ---- Log call ----
_fn_t_log() {
local fn_id="$1" duration_ms="$2" success="$3" error_class="${4:-}"
_fn_t_resolve_db || return 0
command -v sqlite3 >/dev/null 2>&1 || return 0
local sid="${CLAUDE_SESSION_ID:-}"
local ts
ts=$(date -u +%s)
sid="${sid//\'/\'\'}"
fn_id="${fn_id//\'/\'\'}"
error_class="${error_class//\'/\'\'}"
sqlite3 "$_FN_T_DB" "INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts) VALUES ('$sid','$fn_id','bash_wrapper','',$duration_ms,$success,'$error_class','',$ts);" 2>/dev/null || true
}
# ---- Wrap a single function by name ----
_fn_t_wrap() {
local orig="$1" fn_id="$2"
# already wrapped?
local guard
guard="_FN_T_WRAPPED_${orig}"
if [ "${!guard:-0}" = "1" ]; then return 0; fi
# must exist as a function
declare -F "$orig" >/dev/null 2>&1 || return 1
# capture original body, rename to _fn_t_orig_<name>
local body
body=$(declare -f "$orig") || return 1
# body starts with "<orig> ()"; prepend prefix to rename
local renamed="_fn_t_orig_${body}"
eval "$renamed"
# define wrapper with the original name
eval "
${orig}() {
local _t0_ms _t1_ms _rc _dur
_t0_ms=\$(date +%s%3N)
_fn_t_orig_${orig} \"\$@\"
_rc=\$?
_t1_ms=\$(date +%s%3N)
_dur=\$((_t1_ms - _t0_ms))
if [ \$_rc -eq 0 ]; then
_fn_t_log \"${fn_id}\" \$_dur 1 \"\"
else
_fn_t_log \"${fn_id}\" \$_dur 0 \"exit_\$_rc\"
fi
return \$_rc
}
"
eval "$guard=1"
}
# ---- Auto-wrap: walk bash/functions and wrap every function currently defined ----
_fn_t_autowrap() {
local root
root=$(_fn_t_root) || return 1
local f domain name fn_id
while IFS= read -r f; do
domain=$(basename "$(dirname "$f")")
name=$(basename "$f" .sh)
fn_id="${name}_bash_${domain}"
if declare -F "$name" >/dev/null 2>&1; then
_fn_t_wrap "$name" "$fn_id"
fi
done < <(find "$root/bash/functions" -type f -name '*.sh' 2>/dev/null)
}
# ---- Run autowrap on source ----
_fn_t_autowrap
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "wait_for_http <url> [timeout_seconds] [interval_seconds]"
description: "Hace polling a una URL HTTP/HTTPS hasta recibir respuesta 2xx o agotar el timeout. Util en deploys, post-restart de servicios y smoke tests."
tags: [http, wait, poll, health, deploy, smoke-test]
tags: [http, wait, poll, health, deploy, smoke-test, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "wait_for_port(host: string, port: int, timeout_seconds: int, interval_seconds: int) -> int"
description: "Hace polling TCP a host:puerto hasta que acepte conexiones o agote el timeout. Util para esperar a que un servicio (DB, API, container) este listo antes de ejecutar pasos siguientes."
tags: [tcp, wait, poll, port]
tags: [tcp, wait, poll, port, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "compile_cpp_app(app_name?: string) -> void"
description: "Pipeline que resuelve la app C++ desde el nombre o CWD, la cross-compila para Windows con mingw-w64, y despliega el .exe al escritorio de Windows. Composicion de resolve_cpp_app_dir + build_cpp_windows + deploy_cpp_exe_to_windows."
tags: [cpp, compile, windows, mingw, cross-compile, deploy, pipeline]
tags: [cpp, compile, windows, mingw, cross-compile, deploy, pipeline, pendiente-usar]
uses_functions:
- resolve_cpp_app_dir_bash_infra
- build_cpp_windows_bash_infra
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "full_git_pull() -> stdout: tabla resumen"
description: "Pull automatico de fn_registry + todos los sub-repos locales + submodules + fn sync. Descubre repos locales, stashea dirty trees antes de pullear, hace pull --ff-only, actualiza submodulos del repo principal, pulla ~/.password-store, regenera registry.db con fn index y ejecuta fn sync."
tags: [git, pull, sync, registry, pipeline]
tags: [git, pull, sync, registry, pipeline, pendiente-usar]
uses_functions:
- discover_git_repos_bash_infra
- git_pull_with_stash_bash_infra
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "full_git_push(commit_message?: string) -> stdout: tabla resumen"
description: "Push automatico de fn_registry + todos los sub-repos + fn sync. Descubre repos, escanea secrets (aborta si detecta), auto-inicializa apps/analyses sin .git via ensure_repo_synced, auto-commitea dirty trees, pushea solo repos adelantados, pushea ~/.password-store sin commitear, y ejecuta fn sync."
tags: [git, push, sync, registry, pipeline]
tags: [git, push, sync, registry, pipeline, pendiente-usar]
uses_functions:
- discover_git_repos_bash_infra
- scan_secrets_in_dirty_bash_cybersecurity
@@ -0,0 +1,111 @@
---
name: generate_capability_doc
kind: pipeline
lang: bash
domain: pipelines
version: "1.0.0"
purity: impure
signature: "generate_capability_doc(group: string, --registry: string?, --out: string?) -> void"
description: "Regenera la tabla de funciones de una pagina capability en docs/capabilities/<group>.md consultando registry.db. Preserva bloques curated (Ejemplo canonico, Fronteras, Prerequisitos, Notas). Si el archivo no existe lo crea con plantilla minima."
tags: ["capability-groups", "docs", "doctor", "generator", "pipeline"]
uses_functions:
- audit_capability_groups_go_infra
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
- name: group
desc: "slug del capability group (ej. notebook, metabase). Coincide con el tag canonico en frontmatter de funciones."
- name: --registry
desc: "path opcional a registry.db o a su directorio padre (default: walk-up desde cwd hasta encontrar registry.db)."
- name: --out
desc: "path opcional del archivo de salida (default: <root>/docs/capabilities/<group>.md)."
output: "path del archivo actualizado o creado + count de funciones a stdout. Exit 0 ok, exit 1 si error."
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/pipelines/generate_capability_doc.sh"
---
## Ejemplo
```bash
# Regenerar tabla de notebook (ya existe, preserva Ejemplo canonico / Fronteras)
./bash/functions/pipelines/generate_capability_doc.sh notebook
# → /home/lucas/fn_registry/docs/capabilities/notebook.md updated (5 functions)
# Crear pagina nueva para un grupo sin pagina todavia
./bash/functions/pipelines/generate_capability_doc.sh metabase
# → /home/lucas/fn_registry/docs/capabilities/metabase.md created (12 functions)
# Especificar registry y destino custom
./bash/functions/pipelines/generate_capability_doc.sh android \
--registry /ruta/alternativa/registry.db \
--out /tmp/android_cap.md
# → /tmp/android_cap.md created (8 functions)
# Grupo sin funciones todavia (avisa pero no falla)
./bash/functions/pipelines/generate_capability_doc.sh nuevo_grupo
# WARN: El grupo 'nuevo_grupo' no tiene funciones con ese tag en registry.db.
# → /home/lucas/fn_registry/docs/capabilities/nuevo_grupo.md created (0 functions)
```
## Comportamiento detallado
### Resolucion de root
Walk-up desde `cwd` buscando `registry.db`. Si se pasa `--registry`:
- Si es un archivo: toma el directorio padre.
- Si es un directorio: lo usa directamente.
### Tabla generada (SQL)
```sql
SELECT f.id, f.signature, f.description
FROM functions f, json_each(f.tags) j
WHERE j.value = '<group>'
ORDER BY f.id;
```
JOIN custom via `json_each` — usa `sqlite3` directo (excepcion autorizada para JOINs no expuestos por MCP).
### Formato de tabla
```markdown
| ID | Firma | Que hace |
|---|---|---|
| `<id>` | `<signature>` | <description> |
```
Los `|` dentro de signatures y descriptions se escapan como `\|`.
### Preservacion de bloques curated
Cuando el archivo ya existe:
- El bloque entre `## Funciones` y la siguiente `## ` se reemplaza.
- Todo lo anterior y posterior (Ejemplo canonico, Fronteras, Prerequisitos, Notas, etc.) se mantiene intacto.
- Implementado con `awk`: copia hasta `## Funciones`, imprime nueva tabla, salta contenido viejo hasta encontrar `^## `, reanuda copia.
### Archivo nuevo
Si `docs/capabilities/<group>.md` no existe, se crea con plantilla minima:
- Titulo `# Capability: <group>`.
- Placeholder de descripcion (editable a mano).
- Tabla generada.
- Secciones vacias: `## Ejemplo canonico`, `## Fronteras`.
## Codigos de salida
| Codigo | Significado |
|--------|-------------|
| 0 | Exito |
| 1 | Error: grupo no especificado, registry.db no encontrado, fallo SQL, awk vacio |
## Notas
- `uses_functions: []` — depende de `sqlite3` y `awk` del sistema, no de funciones del registry.
- El tag del grupo debe ser plano (ej. `notebook`, `metabase`), no `tag:notebook`.
- Enganchado en `docs/capabilities/INDEX.md` como el mecanismo de auto-generacion referenciado bajo `fn doctor capabilities --update`.
- Seguro para re-ejecucion: idempotente si el registry no cambia.
@@ -0,0 +1,231 @@
#!/usr/bin/env bash
# generate_capability_doc — regenera la tabla "Funciones" de una pagina capability
# Preserva bloques curated (Ejemplo canonico, Fronteras, Prerequisitos, Notas, etc.)
# Usage: generate_capability_doc <group> [--registry <path>] [--out <path>]
set -euo pipefail
# --------------------------------------------------------------------------
# Helpers
# --------------------------------------------------------------------------
die() { echo "ERROR: $*" >&2; exit 1; }
warn() { echo "WARN: $*" >&2; }
# --------------------------------------------------------------------------
# Parse args
# --------------------------------------------------------------------------
GROUP=""
REGISTRY_PATH=""
OUT_PATH=""
while [[ $# -gt 0 ]]; do
case "$1" in
--registry)
shift
REGISTRY_PATH="${1:-}"
[[ -z "$REGISTRY_PATH" ]] && die "--registry requiere un valor"
shift
;;
--out)
shift
OUT_PATH="${1:-}"
[[ -z "$OUT_PATH" ]] && die "--out requiere un valor"
shift
;;
-*)
die "Opcion desconocida: $1"
;;
*)
[[ -n "$GROUP" ]] && die "Solo se acepta un <group>. Ya se especifico: '$GROUP'"
GROUP="$1"
shift
;;
esac
done
[[ -z "$GROUP" ]] && die "Uso: generate_capability_doc <group> [--registry <path>] [--out <path>]"
# --------------------------------------------------------------------------
# Resolver registry root
# --------------------------------------------------------------------------
find_registry_root() {
local dir
dir="$(pwd)"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/registry.db" ]]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
}
if [[ -n "$REGISTRY_PATH" ]]; then
# Si se pasa un path que termina en registry.db, tomar el directorio
if [[ -f "$REGISTRY_PATH" ]]; then
REGISTRY_ROOT="$(dirname "$(realpath "$REGISTRY_PATH")")"
elif [[ -d "$REGISTRY_PATH" ]]; then
REGISTRY_ROOT="$(realpath "$REGISTRY_PATH")"
else
die "registry no encontrado en: $REGISTRY_PATH"
fi
else
REGISTRY_ROOT="$(find_registry_root)" || die "No se encontro registry.db. Ejecutar desde dentro del registry o pasar --registry."
fi
DB="$REGISTRY_ROOT/registry.db"
[[ -f "$DB" ]] || die "registry.db no encontrado en: $DB"
# --------------------------------------------------------------------------
# Resolver output path
# --------------------------------------------------------------------------
if [[ -z "$OUT_PATH" ]]; then
OUT_PATH="$REGISTRY_ROOT/docs/capabilities/${GROUP}.md"
fi
# --------------------------------------------------------------------------
# Consultar funciones del grupo
# --------------------------------------------------------------------------
# JOIN custom entre functions y json_each(tags) — excepcion autorizada para sqlite3 directo.
# Usamos U+001F (ASCII Unit Separator) como separador de campos para evitar conflictos
# con el caracter | que aparece en signatures Python (ej. "list[int] | None").
SEP=$'\x1f'
ROWS="$(sqlite3 -separator "$SEP" "$DB" \
"SELECT f.id, f.signature, f.description
FROM functions f, json_each(f.tags) j
WHERE j.value = '${GROUP}'
ORDER BY f.id;" 2>/dev/null)" || die "Error al consultar registry.db"
# Escapar | en un valor para que no rompa la tabla Markdown
escape_pipe() {
printf '%s' "$1" | sed 's/|/\\|/g'
}
# Construir tabla Markdown
TABLE_HEADER="| ID | Firma | Que hace |
|---|---|---|"
TABLE_ROWS=""
FUNC_COUNT=0
if [[ -n "$ROWS" ]]; then
while IFS="$SEP" read -r id signature description; do
# Limpiar espacios extra
id="${id// /}"
signature="$(printf '%s' "$signature" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
description="$(printf '%s' "$description" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
# Escapar pipes internos en firma y descripcion
sig_esc="$(escape_pipe "$signature")"
desc_esc="$(escape_pipe "$description")"
TABLE_ROWS="${TABLE_ROWS}| \`${id}\` | \`${sig_esc}\` | ${desc_esc} |
"
FUNC_COUNT=$((FUNC_COUNT + 1))
done <<< "$ROWS"
fi
if [[ $FUNC_COUNT -eq 0 ]]; then
warn "El grupo '${GROUP}' no tiene funciones con ese tag en registry.db."
TABLE_CONTENT="_No hay funciones con tag ${GROUP}._"
else
TABLE_CONTENT="${TABLE_HEADER}
${TABLE_ROWS}"
fi
# --------------------------------------------------------------------------
# Crear o actualizar el archivo
# --------------------------------------------------------------------------
mkdir -p "$(dirname "$OUT_PATH")"
if [[ ! -f "$OUT_PATH" ]]; then
# --- Archivo nuevo: plantilla minima ---
cat > "$OUT_PATH" <<TEMPLATE
# Capability: ${GROUP}
_(Descripcion del grupo — editar a mano)_
## Funciones
${TABLE_CONTENT}
## Ejemplo canonico
_(Anadir 1-2 bloques de codigo end-to-end)_
## Fronteras
_(Que NO cubre este grupo)_
TEMPLATE
echo "${OUT_PATH} created (${FUNC_COUNT} functions)"
else
# --- Archivo existente: reemplazar SOLO el bloque "## Funciones" ---
# Estrategia awk:
# - Copiar todo hasta (e incluyendo) "## Funciones"
# - Imprimir linea en blanco + nueva tabla + linea en blanco
# - Saltar lineas hasta encontrar la proxima seccion "^## "
# - Reanudar copia desde esa seccion en adelante
# Escribir tabla en archivo temporal para pasarla a awk sin problemas de escaping
TMP_TABLE="$(mktemp)"
trap 'rm -f "$TMP_TABLE"' EXIT
printf '%s\n' "$TABLE_CONTENT" > "$TMP_TABLE"
TMP_OUT="$(mktemp)"
trap 'rm -f "$TMP_TABLE" "$TMP_OUT"' EXIT
awk -v table_file="$TMP_TABLE" '
BEGIN {
in_functions = 0
done = 0
# Leer tabla nueva en una variable
table_content = ""
while ((getline line < table_file) > 0) {
table_content = table_content line "\n"
}
close(table_file)
}
# Detectar inicio de la seccion Funciones
/^## Funciones$/ && !done {
print $0
printf "\n"
printf "%s", table_content
in_functions = 1
next
}
# Mientras estamos en el bloque Funciones, saltar lineas hasta proxima seccion
in_functions && /^## / {
in_functions = 0
done = 1
printf "\n"
print $0
next
}
in_functions {
# Saltar contenido viejo del bloque Funciones
next
}
# Fuera del bloque: copiar tal cual
{ print $0 }
END {
# Si no habia seccion siguiente (Funciones era la ultima), cerrar bien
if (in_functions) {
printf "\n"
}
}
' "$OUT_PATH" > "$TMP_OUT"
# Verificar que el archivo resultante no este vacio
if [[ ! -s "$TMP_OUT" ]]; then
rm -f "$TMP_OUT"
die "Error: awk produjo un archivo vacio. El archivo original no fue modificado."
fi
mv "$TMP_OUT" "$OUT_PATH"
echo "${OUT_PATH} updated (${FUNC_COUNT} functions)"
fi
exit 0
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "install_systemd_service --name <N> --exec <PATH> [opts] -> json"
description: "Pipeline que registra una app como servicio systemd del sistema: genera el unit, lo instala en /etc/systemd/system/, hace daemon-reload, enable, start y devuelve status. Requiere sudo sin password para systemctl y escritura en /etc/systemd/system/."
tags: [systemd, service, local, infra, pipeline, install]
tags: [systemd, service, local, infra, pipeline, install, pendiente-usar]
uses_functions:
- systemd_local_install_unit_bash_infra
- systemd_local_enable_bash_infra
@@ -0,0 +1,85 @@
---
name: propose_capability_groups
kind: pipeline
lang: bash
domain: pipelines
version: "1.0.0"
purity: impure
signature: "propose_capability_groups([--min-count N], [--max-domains M], [--json], [--apply tag]) -> report"
description: "Analiza tags candidatos a capability group (issue 0086). Filtra via blocklist + cap de dominios. Lista candidatos o promociona con --apply (llama generate_capability_doc + actualiza INDEX.md)."
tags: ["capability-groups", "doctor", "audit", "pipeline"]
uses_functions:
- generate_capability_doc_bash_pipelines
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
- name: --min-count
desc: "minimo de funciones con el tag para considerarlo candidato (default 3)"
- name: --max-domains
desc: "maximo de dominios distintos entre funciones del tag (default 3, filtra tags genericos que cruzan muchos dominios)"
- name: --json
desc: "salida JSON estructurada en vez de texto (util para agentes)"
- name: --apply
desc: "promociona el tag dado a capability group: genera doc con generate_capability_doc + actualiza docs/capabilities/INDEX.md (idempotente)"
output: "tabla de candidatos a stdout (tag, count, domains, already_group, samples), o efectos en disco si --apply (nuevo .md + fila en INDEX.md)"
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/pipelines/propose_capability_groups.sh"
---
## Ejemplo
```bash
# Listar candidatos con al menos 10 funciones
bash bash/functions/pipelines/propose_capability_groups.sh --min-count 10
# Listar con salida JSON (para agentes)
bash bash/functions/pipelines/propose_capability_groups.sh --min-count 5 --json
# Promocionar un tag a capability group
bash bash/functions/pipelines/propose_capability_groups.sh --apply metabase
# Via fn run
./fn run propose_capability_groups --min-count 10
```
## Logica
### Modo listar (sin --apply)
1. Resuelve la raiz del registry (walk-up hasta `registry.db`).
2. Filtra tags via **blocklist** hardcodeada (idiomas, dominios, CRUD generico, verbos super-genericos, primitivas, estados).
3. Query SQL con `json_each(f.tags)` + `GROUP BY` + `HAVING cnt >= N AND domains <= M`.
4. Marca cada candidato con `already_group: yes/no` parseando los links `[tag](tag.md)` de `docs/capabilities/INDEX.md`.
5. Imprime tabla formateada con maximo 3 IDs de muestra por candidato.
6. Resumen: total candidatos, ya-grupos, nuevos.
### Modo --apply
1. Valida que el tag no esta en blocklist y pasa el filtro de count/domains.
2. Llama a `bash/functions/pipelines/generate_capability_doc.sh <tag>`.
3. Inserta fila en `docs/capabilities/INDEX.md` (idempotente — no duplica si ya existe).
4. Imprime checklist de edicion manual para el usuario.
## Blocklist
Tags en la blocklist nunca son candidatos (caso exacto):
- **Idioma**: `go py bash ps ts python cpp`
- **Dominio**: `core infra finance datascience cybersecurity shell tui pipelines browser`
- **Kind/purity**: `function pipeline component pure impure`
- **CRUD generico**: `add create delete list update get set remove insert`
- **Verbo super-generico**: `compose convert combine append empty exists check find format parse render`
- **Estructural**: `generic helper utility wrapper test`
- **Primitivas**: `string number int float array slice map dict value key`
- **Estados**: `pending-usar pendiente-usar`
## Notas
- Usa `sqlite3` directo para el JOIN custom `functions + json_each(tags)` (autorizado por la regla registry_calls.md — JOINs custom no expuestos por el MCP).
- La insercion en INDEX.md usa `python3` embebido para manejar la posicion relativa a la cabecera de tabla de forma portable.
- El flag `--max-domains` es la heuristica clave: un tag como `add` aparece en 8 dominios → filtrado; `metabase` aparece en 2 → candidato valido.
@@ -0,0 +1,346 @@
#!/usr/bin/env bash
# propose_capability_groups — analiza tags candidatos a capability group (issue 0086)
# Filtra via blocklist + cap de dominios. Lista candidatos o promociona con --apply.
set -euo pipefail
# ---------------------------------------------------------------------------
# Blocklist: tags genericos que nunca son capability groups
# ---------------------------------------------------------------------------
BLOCKLIST=(
# idioma
go py bash ps ts python cpp
# dominio
core infra finance datascience cybersecurity shell tui pipelines browser
# kind / purity
function pipeline component pure impure
# CRUD generico
add create delete list update get set remove insert
# verbo super-generico
compose convert combine append empty exists check find format parse render
# estructural
generic helper utility wrapper test
# primitivas
string number int float array slice map dict value key
# estados
pending-usar pendiente-usar
)
# ---------------------------------------------------------------------------
# Resolver raiz del registry (walk-up hasta registry.db)
# ---------------------------------------------------------------------------
find_registry_root() {
local dir
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/registry.db" ]]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
echo "ERROR: registry.db no encontrado en ningún directorio padre" >&2
return 1
}
# ---------------------------------------------------------------------------
# Defaults
# ---------------------------------------------------------------------------
MIN_COUNT=3
MAX_DOMAINS=4
JSON_MODE=false
APPLY_TAG=""
# ---------------------------------------------------------------------------
# Parse args
# ---------------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--min-count)
MIN_COUNT="$2"
shift 2
;;
--max-domains)
MAX_DOMAINS="$2"
shift 2
;;
--json)
JSON_MODE=true
shift
;;
--apply)
APPLY_TAG="$2"
shift 2
;;
--help|-h)
echo "Uso: propose_capability_groups [--min-count N] [--max-domains M] [--json] [--apply <tag>]"
echo ""
echo " --min-count N Minimo de funciones con el tag (default: 3)"
echo " --max-domains M Maximo de dominios distintos (default: 3)"
echo " --json Salida JSON"
echo " --apply <tag> Promociona el tag a capability group"
exit 0
;;
*)
echo "ERROR: argumento desconocido: $1" >&2
exit 1
;;
esac
done
REGISTRY_ROOT="$(find_registry_root)"
DB="$REGISTRY_ROOT/registry.db"
INDEX_MD="$REGISTRY_ROOT/docs/capabilities/INDEX.md"
# ---------------------------------------------------------------------------
# Construir lista de tags bloqueados como CSV quoted para SQL IN (...)
# ---------------------------------------------------------------------------
build_blocklist_sql() {
local csv=""
for tag in "${BLOCKLIST[@]}"; do
csv="${csv}'${tag}',"
done
# quitar coma final
echo "${csv%,}"
}
# ---------------------------------------------------------------------------
# Parsear tags ya en INDEX.md: extraer slugs de [tag](tag.md)
# ---------------------------------------------------------------------------
get_existing_groups() {
if [[ ! -f "$INDEX_MD" ]]; then
return
fi
grep -oP '\[([^\]]+)\]\(\1\.md\)' "$INDEX_MD" | grep -oP '\[([^\]]+)\]' | tr -d '[]' || true
}
# ---------------------------------------------------------------------------
# Comprobar si un tag esta en blocklist
# ---------------------------------------------------------------------------
in_blocklist() {
local tag="$1"
for blocked in "${BLOCKLIST[@]}"; do
if [[ "$blocked" == "$tag" ]]; then
return 0
fi
done
return 1
}
# ---------------------------------------------------------------------------
# MODO --apply: promocionar un tag a capability group
# ---------------------------------------------------------------------------
apply_tag() {
local tag="$1"
# Validar: no en blocklist
if in_blocklist "$tag"; then
echo "ERROR: '$tag' está en la blocklist de tags genericos. No se puede promocionar." >&2
exit 1
fi
# Validar: count >= min y domains <= max
local blocklist_sql
blocklist_sql="$(build_blocklist_sql)"
local row
row="$(sqlite3 "$DB" "
SELECT COUNT(*) AS cnt, COUNT(DISTINCT f.domain) AS domains
FROM functions f, json_each(f.tags) j
WHERE j.value = '${tag}'
GROUP BY j.value;
" 2>/dev/null || true)"
if [[ -z "$row" ]]; then
echo "ERROR: tag '$tag' no encontrado en el registry o no tiene funciones." >&2
exit 1
fi
local cnt domains
cnt="$(echo "$row" | cut -d'|' -f1)"
domains="$(echo "$row" | cut -d'|' -f2)"
if [[ "$cnt" -lt "$MIN_COUNT" ]]; then
echo "ERROR: tag '$tag' tiene $cnt funciones, minimo requerido es $MIN_COUNT." >&2
exit 1
fi
if [[ "$domains" -gt "$MAX_DOMAINS" ]]; then
echo "ERROR: tag '$tag' aparece en $domains dominios distintos (maximo $MAX_DOMAINS). Probablemente es generico." >&2
exit 1
fi
echo "Promocionando tag '$tag' a capability group..."
echo " funciones: $cnt dominios: $domains"
echo ""
# Paso 1: llamar a generate_capability_doc
local gen_script="$REGISTRY_ROOT/bash/functions/pipelines/generate_capability_doc.sh"
if [[ ! -f "$gen_script" ]]; then
echo "ERROR: no se encontro generate_capability_doc.sh en $gen_script" >&2
exit 1
fi
echo "=> Generando docs/capabilities/${tag}.md ..."
bash "$gen_script" "$tag"
echo " OK"
# Paso 2: anadir fila a INDEX.md (idempotente)
if [[ ! -f "$INDEX_MD" ]]; then
echo "ERROR: no se encontro INDEX.md en $INDEX_MD" >&2
exit 1
fi
local row_pattern
row_pattern="| \[${tag}\](${tag}.md)"
if grep -qF "$row_pattern" "$INDEX_MD"; then
echo "=> Fila para '$tag' ya existe en INDEX.md — sin cambios."
else
echo "=> Anadiendo fila a INDEX.md ..."
# Insertar despues de la linea de cabecera |---|---|---| de la tabla "Grupos vigentes"
# Buscamos la linea del separador de cabecera de tabla que va despues de "## Grupos vigentes"
local new_row="| [${tag}](${tag}.md) | ${cnt} | _(editar — promovido automaticamente)_ |"
# Usar Python para insertar la linea de forma portable (awk no maneja bien insercion relativa)
python3 - "$INDEX_MD" "$new_row" <<'PYEOF'
import sys
index_path = sys.argv[1]
new_row = sys.argv[2]
with open(index_path, "r") as f:
lines = f.readlines()
# Encontrar el bloque "Grupos vigentes" y luego la linea separadora |---|---|---|
in_section = False
insert_after = -1
for i, line in enumerate(lines):
if "## Grupos vigentes" in line:
in_section = True
if in_section and line.strip().startswith("|---|"):
insert_after = i
break
if insert_after == -1:
print("ERROR: no se encontro la tabla 'Grupos vigentes' en INDEX.md", file=sys.stderr)
sys.exit(1)
lines.insert(insert_after + 1, new_row + "\n")
with open(index_path, "w") as f:
f.writelines(lines)
print(f" Fila insertada en posicion {insert_after + 1}")
PYEOF
echo " OK"
fi
echo ""
echo "HECHO. Pasos manuales pendientes:"
echo " 1. Editar docs/capabilities/${tag}.md:"
echo " - Anadir parrafo de descripcion del grupo."
echo " - Completar seccion 'Ejemplo canonico' con codigo real."
echo " - Completar seccion 'Fronteras' (que NO hace el grupo)."
echo " - Anadir 'Notas' si aplica."
echo " 2. Actualizar la frase descripcion en docs/capabilities/INDEX.md"
echo " (reemplazar el placeholder con descripcion real)."
}
# ---------------------------------------------------------------------------
# MODO LISTAR: analizar candidatos
# ---------------------------------------------------------------------------
list_candidates() {
local blocklist_sql
blocklist_sql="$(build_blocklist_sql)"
# Query: tags con suficientes funciones y no demasiados dominios
local query
query="
SELECT
j.value AS tag,
COUNT(*) AS cnt,
COUNT(DISTINCT f.domain) AS domains,
GROUP_CONCAT(DISTINCT f.domain) AS domain_list,
GROUP_CONCAT(f.id) AS function_ids
FROM functions f, json_each(f.tags) j
WHERE j.value NOT IN (${blocklist_sql})
GROUP BY j.value
HAVING cnt >= ${MIN_COUNT} AND domains <= ${MAX_DOMAINS}
ORDER BY cnt DESC;
"
local raw_results
raw_results="$(sqlite3 "$DB" "$query" 2>/dev/null || true)"
if [[ -z "$raw_results" ]]; then
echo "No se encontraron candidatos con min-count=${MIN_COUNT} y max-domains=${MAX_DOMAINS}."
exit 0
fi
# Obtener grupos ya existentes
local existing_groups
existing_groups="$(get_existing_groups)"
if $JSON_MODE; then
# Salida JSON
echo "["
local first=true
while IFS='|' read -r tag cnt domains domain_list function_ids; do
[[ -z "$tag" ]] && continue
local already_group="false"
if echo "$existing_groups" | grep -qxF "$tag" 2>/dev/null; then
already_group="true"
fi
# Tomar hasta 3 samples
local samples
samples="$(echo "$function_ids" | tr ',' '\n' | head -3 | tr '\n' ',' | sed 's/,$//')"
if $first; then
first=false
else
echo ","
fi
printf ' {"tag":"%s","count":%s,"domains":%s,"domain_list":"%s","already_group":%s,"samples":[%s]}' \
"$tag" "$cnt" "$domains" "$domain_list" "$already_group" \
"$(echo "$samples" | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/')"
done <<< "$raw_results"
echo ""
echo "]"
return
fi
# Salida texto humano
local total=0
local already=0
local nuevos=0
# Acumular lineas para contar primero
local lines_output=()
while IFS='|' read -r tag cnt domains domain_list function_ids; do
[[ -z "$tag" ]] && continue
local already_group="no"
if echo "$existing_groups" | grep -qxF "$tag" 2>/dev/null; then
already_group="yes"
((already++)) || true
else
((nuevos++)) || true
fi
((total++)) || true
# Tomar hasta 3 samples
local samples
samples="$(echo "$function_ids" | tr ',' '\n' | head -3 | paste -sd ',' -)"
lines_output+=("$(printf "%-22s %-6s %-25s %-13s %s" "$tag" "$cnt" "$domain_list" "$already_group" "$samples")")
done <<< "$raw_results"
printf "%-22s %-6s %-25s %-13s %s\n" "TAG" "COUNT" "DOMAINS" "ALREADY_GROUP" "SAMPLES"
printf "%-22s %-6s %-25s %-13s %s\n" "----------------------" "------" "-------------------------" "-------------" "-------"
for line in "${lines_output[@]}"; do
echo "$line"
done
echo ""
echo "${total} candidatos. ${already} ya son grupo. ${nuevos} son nuevos."
echo "Promociona con: bash bash/functions/pipelines/propose_capability_groups.sh --apply <tag>"
}
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
if [[ -n "$APPLY_TAG" ]]; then
apply_tag "$APPLY_TAG"
else
list_candidates
fi
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "check_command(cmd: string, [error_code: string], [description: string]) -> void; check_commands(cmd...: string) -> void; check_directory(dir: string, [msg: string]) -> void; check_file(file: string, [msg: string]) -> void"
description: "Verifica existencia de comandos, directorios y archivos con output formateado. Complementa assert_command_exists con mensajes de error detallados y logging."
tags: [bash, check, dependency, command, exists, validation]
tags: [bash, check, dependency, command, exists, validation, pendiente-usar]
uses_functions: [bash_log_bash_shell]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "bash_confirm(prompt: string, [default: string]) -> exit_code"
description: "Dialogo interactivo de confirmacion y/n con valor por defecto configurable. Soporta respuestas yes/y/si."
tags: [bash, confirm, prompt, interactive, dialog]
tags: [bash, confirm, prompt, interactive, dialog, pendiente-usar]
uses_functions: [bash_colors_bash_shell]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "handle_error(error_code: string, error_description: string, [solution: string]) -> exit_code"
description: "Muestra un box de error formateado con contexto del fallo: script, linea, funcion, directorio y usuario. Registra en log."
tags: [bash, error, handler, box, formatted, context]
tags: [bash, error, handler, box, formatted, context, pendiente-usar]
uses_functions: [bash_colors_bash_shell, bash_log_bash_shell]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "safe_run(cmd: string, [error_code: string], [error_desc: string]) -> void; setup_error_trap() -> void; error_trap_handler(exit_code: int, line_number: int) -> void"
description: "Ejecuta comandos con manejo de errores integrado. Incluye trap handler que captura fallos con numero de linea y codigo de salida."
tags: [bash, safe, run, error, trap, handler]
tags: [bash, safe, run, error, trap, handler, pendiente-usar]
uses_functions: [bash_log_bash_shell]
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "convert_text_case(mode: string, file: string, [output_file: string]) -> void"
description: "Convierte el contenido de un archivo de texto. Modos: upper (todo mayúsculas), lower (todo minúsculas), lf (normaliza saltos de línea a LF eliminando \\r), crlf (normaliza saltos a CRLF). Sin output_file imprime a stdout."
tags: [bash, text, convert, case, encoding]
tags: [bash, text, convert, case, encoding, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "git_clean_branches([base_branch: string], [--remote], [--force]) -> void"
description: "Elimina ramas locales ya mergeadas en la rama base (autodetecta main/master). Con --remote también borra las ramas en todos los remotes. Con --force omite la confirmación interactiva."
tags: [bash, git, branches, clean, merge]
tags: [bash, git, branches, clean, merge, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "git_log_visual([--count N: int], [--author X: string], [--since D: string], [--all]) -> void"
description: "Muestra el historial de commits Git con grafo visual, colores y formato legible (hash, fecha, autor, mensaje, ramas). Soporta filtros por cantidad, autor, fecha y modo todas-las-ramas. Pagina con less."
tags: [bash, git, log, history, graph]
tags: [bash, git, log, history, graph, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "git_push_all_remotes([--message msg: string]) -> void"
description: "Hace commit de todos los cambios pendientes (git add -A + git commit) y pushea la rama actual a todos los remotes configurados. Si no hay cambios solo pushea. Requiere --message si hay cambios sin commitear."
tags: [bash, git, push, remote, all]
tags: [bash, git, push, remote, all, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "git_repo_status() -> void"
description: "Muestra el estado completo de un repositorio Git: rama actual, upstream (ahead/behind), cambios pendientes, stash, remotes y últimos 8 commits. Sale con exit code 1 si el directorio actual no es un repo Git."
tags: [bash, git, status, repo, branch]
tags: [bash, git, status, repo, branch, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
+1 -1
View File
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "run_steps(yaml_file: string, [--strict]) -> string"
description: "Ejecuta pasos de un YAML generico donde cada step tiene action=command. Lee el YAML con yq, ejecuta cada paso secuencialmente con timeout configurable, captura exit code y output, respeta continue_on_error, y al final reporta JSON estandar a stdout via report_execution_json. Sale con exit code 0 (success), 1 (failure) o 2 (partial). Con --strict mapea partial a failure."
tags: [execution, yaml, runner, standard, pipeline]
tags: [execution, yaml, runner, standard, pipeline, pendiente-usar]
uses_functions: [exit_with_status_bash_shell, report_execution_json_bash_shell]
uses_types: []
returns: []