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:
@@ -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()
|
||||
Reference in New Issue
Block a user