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 d110aa40f9
commit cfdf515228
805 changed files with 5515 additions and 810 deletions
@@ -8,7 +8,7 @@ version: "1.0.0"
purity: impure
signature: "async def compute_centers_reachability_pipeline(origins, centers, isochrone_minutes, base_url, concurrency) -> dict"
description: "Calcula la accesibilidad de centros de servicio: matriz tiempo/distancia clientes→centros e isócronas por centro usando Valhalla."
tags: [pipeline, geo, footprint, valhalla, isochrone, matrix, reachability]
tags: [pipeline, geo, footprint, valhalla, isochrone, matrix, reachability, pendiente-usar]
uses_functions: ["valhalla_matrix_1_to_n_py_geo", "valhalla_isochrones_async_py_geo"]
uses_types: []
returns: []
@@ -8,7 +8,7 @@ version: "1.0.0"
purity: impure
signature: "def count_points_per_zone_pipeline(points: list[tuple[float, float]], zones: list[dict]) -> dict"
description: "Cuenta cuántos puntos (lon, lat) caen dentro de cada zona geográfica definida por GeoJSON."
tags: [pipeline, geo, footprint, geojson, spatial, count, zone]
tags: [pipeline, geo, footprint, geojson, spatial, count, zone, pendiente-usar]
uses_functions: ["load_geojson_polygons_py_geo", "polygon_bbox_py_geo", "point_in_polygons_bbox_py_geo"]
uses_types: []
returns: []
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "def extract_graph_from_text(text: str, entity_labels: list[str], relation_labels: list | dict, allowed: dict, model: Any, threshold: float = 0.3, max_chars_per_chunk: int = 1500, overlap_sentences: int = 2) -> dict"
description: "Pipeline E2E: texto -> grafo de entidades y relaciones. Orquesta chunking, extraccion con GLiNER2 por chunk, agregacion, filtrado tipado y resolucion de alias. Refactorizacion del playground del analisis gliner_glirel_tuning."
tags: [pipeline, graph, ner, relation-extraction, gliner2, nlp, e2e, knowledge-graph, datascience, python]
tags: [pipeline, graph, ner, relation-extraction, gliner2, nlp, e2e, knowledge-graph, datascience, python, pendiente-usar]
uses_functions:
- chunk_with_overlap_py_core
- extract_graph_gliner2_py_datascience
@@ -7,7 +7,7 @@ version: "1.0.0"
purity: impure
signature: "def extraction_pipeline(file_path: str, entity_presets: list[dict], relation_types: list[str], llm_chat_json: Callable[[list[dict]], dict], chunk_size: int = 500, chunk_overlap: int = 50, confidence_threshold: float = 0.5, dedup_threshold: float = 0.85, on_progress: Callable[[str, float], None] | None = None) -> ExtractionResult"
description: "Pipeline completa de extraccion de entidades y relaciones desde un documento. Orquesta extract_text_from_file -> preprocess_text -> split_text_into_chunks -> extract_entities_llm por chunk -> deduplicate_entities -> extract_relations_llm por chunk -> deduplicate_relations."
tags: [pipeline, extraction, entities, relations, llm, nlp, fuzzygraph, datascience]
tags: [pipeline, extraction, entities, relations, llm, nlp, fuzzygraph, datascience, pendiente-usar]
uses_functions:
- extract_text_from_file_py_core
- preprocess_text_py_core
@@ -8,7 +8,7 @@ version: "1.0.0"
purity: impure
signature: "async def generate_isochrones_by_zone_pipeline(zones, points, centers, base_url, concurrency) -> dict"
description: "Genera isócronas Valhalla por zona geográfica usando el p75 de tiempos de viaje de los puntos en cada zona."
tags: [pipeline, geo, footprint, valhalla, isochrone, zone, p75, geojson]
tags: [pipeline, geo, footprint, valhalla, isochrone, zone, p75, geojson, pendiente-usar]
uses_functions: ["load_geojson_polygons_py_geo", "polygon_bbox_py_geo", "point_in_polygons_bbox_py_geo", "summary_stats_py_datascience", "valhalla_isochrones_async_py_geo"]
uses_types: []
returns: []
@@ -8,7 +8,7 @@ version: "1.0.0"
purity: impure
signature: "def setup_geo_stack_docker_pipeline(compose_path: str, wait_seconds: int, verify: bool) -> dict"
description: "Levanta el geo stack Docker (Valhalla + PostGIS + Martin) via docker compose up -d y verifica que los tres servicios responden."
tags: [pipeline, geo, footprint, docker, valhalla, postgis, martin]
tags: [pipeline, geo, footprint, docker, valhalla, postgis, martin, pendiente-usar]
uses_functions: ["valhalla_route_py_geo", "docker_container_running_py_infra"]
uses_types: []
returns: []
@@ -0,0 +1,52 @@
---
name: tag_unused_pending
lang: py
domain: pipelines
version: 0.1.0
purity: impure
kind: pipeline
description: "Etiqueta cada funcion del registry sin consumidor con el tag `pendiente-usar` (issue 0062). Lee `fn doctor unused --json`, modifica el frontmatter YAML del .md de cada funcion, y deja la indicacion para correr `fn index`. Idempotente: tambien retira el tag de funciones que han vuelto a usarse."
tags: [registry, governance, deprecate, lifecycle, tag-management, pendiente-usar]
signature: "python tag_unused_pending.py"
error_type: "error_go_core"
returns_optional: false
params: []
output: "Stdout con summary {added, removed, already_tagged, missing_md}. Modifica .md de cada funcion afectada in-place. NUNCA borra archivos."
uses_functions:
- find_unused_functions_go_infra
uses_types: []
imports:
- json
- re
- sqlite3
- subprocess
- sys
- pathlib
example: |
./fn run tag_unused_pending
./fn index
# Despues: buscar las marcadas
# mcp__registry__fn_search 'tags:"pendiente-usar"'
file_path: "python/functions/pipelines/tag_unused_pending.py"
tested: false
notes: |
Pipeline impuro: muta archivos `.md` del registry.
Detecta dos formatos de `tags:` en frontmatter:
- Inline: `tags: [a, b, c]`
- Block: `tags:\n - a\n - b`
Si el campo no existe, anade una linea inline al final del frontmatter.
Idempotente: re-ejecutar no anade duplicados ni cambia archivos cuya
funcion no esta en la lista de unused. Si una funcion sale de la lista
(alguien la usa), el tag se retira automaticamente para mantener
coherencia.
Tras ejecutar SIEMPRE correr `./fn index` para que `registry.db.tags`
refleje los cambios. El indexer recalcula `content_hash` por funcion y
el `function_versions` snapshot la nueva version.
Filtro de busqueda: `mcp__registry__fn_search` con `tags:"pendiente-usar"`
o `sqlite3 registry.db "SELECT id FROM functions WHERE tags LIKE '%pendiente-usar%'"`.
---
@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""tag_unused_pending — etiqueta cada funcion del registry sin consumidor con
el tag `pendiente-usar`.
Issue 0062: politica explicita es marcar (no borrar) las funciones unused
para que sean visibles como "disponibles, esperando consumidor".
Flujo:
1. Invoca `./fn doctor unused --json` para obtener IDs.
2. Lee `registry.db.functions.file_path` para localizar el .md de cada uno.
3. Anade `pendiente-usar` al campo `tags` del frontmatter YAML. Idempotente.
4. Tras correr el pipeline ejecutar `./fn index` para que registry.db quede
coherente.
Re-ejecutable sin efectos secundarios. Para quitar el tag de una funcion
(porque vuelve a usarse): borrar manualmente la entrada del frontmatter y
re-indexar.
Limpieza del tag obsoleto: si una funcion ya no esta en la lista de unused
pero todavia tiene el tag, este script tambien lo retira (mantener
coherencia entre estado real y etiqueta).
"""
from __future__ import annotations
import json
import re
import sqlite3
import subprocess
import sys
from pathlib import Path
TAG = "pendiente-usar"
def find_root() -> Path:
here = Path(__file__).resolve()
for parent in (here, *here.parents):
if (parent / "registry.db").exists():
return parent
sys.exit("registry.db not found")
def get_unused_ids(root: Path) -> list[str]:
res = subprocess.run(
[str(root / "fn"), "doctor", "unused", "--json"],
capture_output=True, text=True, check=True, cwd=root,
)
data = json.loads(res.stdout) or []
return [e["ID"] for e in data]
def load_id_to_md(root: Path) -> dict[str, Path]:
conn = sqlite3.connect(str(root / "registry.db"))
rows = conn.execute("SELECT id, name, file_path FROM functions").fetchall()
conn.close()
out: dict[str, Path] = {}
for fid, name, fp in rows:
if not fp or not name:
continue
# Try same dir as the .py with <name>.md
py_path = root / fp
candidate = py_path.parent / f"{name}.md"
if candidate.exists():
out[fid] = candidate
continue
# Fallback: derive .md from .py path with same stem (for Go single-fn files)
derived = root / Path(fp).with_suffix(".md")
if derived.exists():
out[fid] = derived
return out
_FM_RE = re.compile(r"^---\n(.*?)\n---\n(.*)$", re.DOTALL)
_INLINE_RE = re.compile(r"^tags:\s*\[(.*?)\]\s*$", re.MULTILINE)
_BLOCK_RE = re.compile(r"^tags:\s*\n((?:\s+-\s+\S.*\n)+)", re.MULTILINE)
def modify_tag(text: str, tag: str, add: bool) -> tuple[str, bool]:
"""Return (new_text, changed). If add=True, ensure tag present. If add=False, remove it."""
m = _FM_RE.match(text)
if not m:
return text, False
fm, body = m.group(1), m.group(2)
inline_m = _INLINE_RE.search(fm)
if inline_m:
raw = inline_m.group(1).strip()
items = [x.strip().strip("'\"") for x in raw.split(",")] if raw else []
items = [x for x in items if x]
has = tag in items
if add and not has:
items.append(tag)
elif not add and has:
items = [x for x in items if x != tag]
else:
return text, False
new_line = "tags: [" + ", ".join(items) + "]"
new_fm = _INLINE_RE.sub(new_line, fm, count=1)
return f"---\n{new_fm}\n---\n{body}", True
block_m = _BLOCK_RE.search(fm)
if block_m:
items_block = block_m.group(1)
item_re = re.compile(rf"^(\s+-\s+){re.escape(tag)}\s*$", re.MULTILINE)
has = bool(item_re.search(items_block))
if add and not has:
indent_m = re.search(r"^(\s+-\s+)\S", items_block, re.MULTILINE)
prefix = indent_m.group(1)
new_block = items_block + f"{prefix}{tag}\n"
elif not add and has:
new_block = item_re.sub("", items_block)
# collapse double newlines
new_block = re.sub(r"\n{2,}", "\n", new_block)
if not new_block.strip():
# All items removed: replace whole block with inline []
new_fm = (fm[:block_m.start()] + "tags: []\n" + fm[block_m.end():]).rstrip()
return f"---\n{new_fm}\n---\n{body}", True
else:
return text, False
new_fm = fm[:block_m.start()] + "tags:\n" + new_block + fm[block_m.end():]
return f"---\n{new_fm}\n---\n{body}", True
# No tags line. Append inline tags: [tag] if adding; nothing to remove.
if add:
return f"---\n{fm}\ntags: [{tag}]\n---\n{body}", True
return text, False
def main() -> None:
root = find_root()
print(f"root: {root}", file=sys.stderr)
unused_ids = set(get_unused_ids(root))
print(f"unused (from fn doctor): {len(unused_ids)}", file=sys.stderr)
id_to_md = load_id_to_md(root)
print(f"functions with .md on disk: {len(id_to_md)}", file=sys.stderr)
added = 0
removed = 0
skipped_no_change = 0
missing_md = 0
for fid, md in id_to_md.items():
text = md.read_text(encoding="utf-8")
if fid in unused_ids:
new_text, changed = modify_tag(text, TAG, add=True)
if changed:
md.write_text(new_text, encoding="utf-8")
added += 1
else:
skipped_no_change += 1
else:
# not unused anymore — strip tag if present
new_text, changed = modify_tag(text, TAG, add=False)
if changed:
md.write_text(new_text, encoding="utf-8")
removed += 1
for fid in unused_ids - set(id_to_md):
missing_md += 1
print(
f"summary: added={added} removed={removed} "
f"already_tagged={skipped_no_change} missing_md={missing_md}",
)
print("run `./fn index` next to refresh registry.db.")
if __name__ == "__main__":
main()