chore: auto-commit (1 archivos)

- tools/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 00:16:47 +02:00
parent 236a3eddc7
commit 8641b49bee
4 changed files with 1880 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Clasificador de notas sueltas de un vault Obsidian por titulo, via ask_llm (claude-direct).
Uso:
classify_notes.py [Vault] [--apply]
Vault por defecto: NotasDeObsidian.
Sin --apply: genera el plan (classify_plan_<vault>.json) y muestra distribucion, sin mover.
Con --apply: mueve cada nota suelta a su carpeta (in-situ; Obsidian resuelve links por nombre).
Taxonomia: estudio, tech, hacking, personal, finanzas, proyectos, otros.
"""
import sys, os, json, re, shutil
from collections import Counter
sys.path.insert(0, "/home/enmanuel/fn_registry/python/functions")
from core.ask_llm import ask_llm
from obsidian import list_obsidian_notes
OBS = "/home/enmanuel/Obsidian"
CATS = ["estudio", "tech", "hacking", "personal", "finanzas", "proyectos", "otros"]
BATCH = 50
def root_notes(vault):
vp = f"{OBS}/{vault}"
out = []
for n in list_obsidian_notes(vp):
rel = os.path.relpath(n, vp)
if "/" in rel or "/.git/" in n or "/dist/" in n:
continue
out.append(os.path.basename(n)[:-3])
return sorted(set(out))
def classify_batch(titles):
listado = "\n".join(f"{i}. {t}" for i, t in enumerate(titles))
prompt = (
"Clasifica cada nota de Obsidian (vault personal de un dev espanol) en UNA categoria.\n"
f"Categorias validas: {', '.join(CATS)}.\n"
"Guia: estudio=apuntes de cursos/teoria (data science, matematicas, bases de datos, frameworks). "
"tech=dev practico (comandos, docker, linux, git, apis, servidores, herramientas). "
"hacking=seguridad ofensiva real (bug bounty, exploits, recon, osint tecnico, web hacking). "
"personal=vida personal (citas, salud, tramites, viajes, familia). "
"finanzas=banca personal, cripto, trading. proyectos=proyectos propios de software/negocio. "
"otros=no encaja.\n"
"Devuelve SOLO un objeto JSON {indice: categoria}, indices 0-based, sin texto extra.\n\n"
+ listado
)
raw = ask_llm(prompt, model="claude-haiku-4-5-20251001", echo=False)
m = re.search(r'\{.*\}', raw, re.S)
if not m:
return {}
try:
return json.loads(m.group(0))
except Exception:
return {}
def main():
args = [a for a in sys.argv[1:] if not a.startswith("--")]
vault = args[0] if args else "NotasDeObsidian"
apply = "--apply" in sys.argv
vp = f"{OBS}/{vault}"
plan_path = f"/home/enmanuel/fn_registry/projects/obsidian/tools/classify_plan_{vault}.json"
titles = root_notes(vault)
print(f"{vault}: {len(titles)} notas sueltas en raiz")
plan = {}
for b in range(0, len(titles), BATCH):
chunk = titles[b:b + BATCH]
res = classify_batch(chunk)
for i, t in enumerate(chunk):
cat = str(res.get(str(i), res.get(i, "otros"))).strip().lower()
plan[t] = cat if cat in CATS else "otros"
json.dump(plan, open(plan_path, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
c = Counter(plan.values())
print("distribucion:", {k: c[k] for k in CATS if c[k]})
for cat in CATS:
ej = [t for t, cc in plan.items() if cc == cat][:4]
if ej:
print(f" [{cat}] " + " | ".join(ej))
if apply:
moved = 0
for t, cat in plan.items():
src = f"{vp}/{t}.md"
if not os.path.exists(src):
continue
dd = f"{vp}/{cat}"; os.makedirs(dd, exist_ok=True)
dst = f"{dd}/{t}.md"
if os.path.exists(dst):
continue
shutil.move(src, dst); moved += 1
print(f"\nmovidas: {moved}")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+185
View File
@@ -0,0 +1,185 @@
{
"01 - Bug Bounty Basics": "hacking",
"01 - Tendencias y Estacionalidad": "finanzas",
"02 - Open Redirect": "hacking",
"03 - HTTP Parameter Pollution": "hacking",
"2025-05-10": "otros",
"2025-06-11": "otros",
"78": "otros",
"Almacenar Contraseñas de Github": "tech",
"Apuntes rapidos automatizacion prefect": "estudio",
"Argumentos para chromium en docker desktop": "tech",
"Arreglar codigo usando diffs": "tech",
"Asesinato sql": "hacking",
"Bases de datos publicas para verificar los identificadores": "tech",
"Bibliotecas de simulaciones para python": "estudio",
"CURSO - Time Series": "estudio",
"Camara raspberry Arducam IMX135": "tech",
"Cambiar menu contextual": "tech",
"Comando para conectarte desde ssh": "tech",
"Comandos de micro": "tech",
"Comenzar proyecto con Julia": "estudio",
"Como hacerlo segun chatgpt": "otros",
"Como instalar Node.js en windows": "tech",
"Como se consigue el texto despues de procesarlo": "tech",
"Como se ve el texto con latex desde chatgpt": "tech",
"Como usar confy UI": "tech",
"Compilacion usando tarjeta grafica de llama.cpp": "tech",
"Conectarse a server kumatora": "tech",
"Conseguir codigo latex usando una IA": "tech",
"Conseguuir codigo latex usando una IA": "tech",
"Continuar sesion codex": "tech",
"Coolify": "tech",
"Cosas que arreglar en contenedor docker": "tech",
"Crear imagenes con IA": "tech",
"Curso DDLR TV Hacking": "estudio",
"Curso avanzado de Simulacion": "estudio",
"Curso basico para hacer en un momento": "estudio",
"Curso sobre simulaciones con python": "estudio",
"Custom LLMs": "tech",
"Dar de alta demanda de madrid": "personal",
"Data Science para invertir en bolsa": "finanzas",
"DataStack": "tech",
"Database de grafos": "estudio",
"Descargar cualquier version de chromium para scraping": "tech",
"Documentacion tixl": "tech",
"Drawing 2024-08-28 21.45.22.excalidraw": "otros",
"Drawing 2024-10-12 16.52.25.excalidraw": "otros",
"Drawing 2024-10-19 23.03.02.excalidraw": "otros",
"Drawing 2025-06-11 22.05.14.excalidraw": "otros",
"Drawing 2025-09-07 01.56.43.excalidraw": "otros",
"Drawing 2026-01-01 04.44.45.excalidraw": "otros",
"Drawing 2026-01-01 04.44.45.excalidraw.restored": "otros",
"Drawing 2026-04-09 00.31.22.excalidraw": "otros",
"Editor de texto": "tech",
"Ejemplos de datascience en bolsa": "estudio",
"Ejempos de tipos de simulaciones": "estudio",
"Embeddings y Búsqueda Semántica": "estudio",
"Enlaces para descargar apks de Android": "tech",
"Estudios": "estudio",
"Exchangers criptomonedas": "finanzas",
"Extraer assets de cities skylines": "proyectos",
"FITZ-Studio": "proyectos",
"Fallos de generacion de notebook completo": "tech",
"FavMusiic": "personal",
"Formulas de series": "estudio",
"Foro peliculas shinchan": "personal",
"Foros": "personal",
"Foros con databases": "tech",
"FrontendStack": "proyectos",
"Fuentes Generacion Imagenes": "tech",
"Funciones de matrix synapsis element": "tech",
"Generacion Alternativa Obsidian con bbdd y Embeddings": "tech",
"Google Dorking to found vulns": "hacking",
"Grupos comerciales": "finanzas",
"Guardar y mover formulas matematicas": "estudio",
"HAcer bots de telegram": "proyectos",
"Herramientas open source": "tech",
"Herramientas para Neo4j": "tech",
"Identificadores de Libros Academicos": "estudio",
"Iniciar Dagster": "tech",
"Instalar Julia": "tech",
"Instalar Office 2025": "tech",
"Ip Homer gui": "tech",
"Java open source": "tech",
"LLM a tener en cuenta": "estudio",
"LeaksCSV": "hacking",
"Libro All of statistics diagrama": "estudio",
"Libro Real world Bug hunting": "hacking",
"Matematicas": "estudio",
"Mcp Inspector": "tech",
"Media Aritmetica": "estudio",
"Mejoras fuzzygraph": "proyectos",
"Metodos de acceso a datos usando LLMs": "estudio",
"Metricas de correlacion": "estudio",
"Metricas para kmeans": "estudio",
"Milkdown editor de markdown": "tech",
"Minecraft mods": "personal",
"Modelos de machineLearning": "estudio",
"MonteMiniCam": "proyectos",
"MyToolbox": "proyectos",
"NVM": "tech",
"Notas desde android": "personal",
"Notas para procesamiento de audio": "tech",
"Ocr sencillo para windows": "tech",
"Orden implementacion": "proyectos",
"Osint Rapido": "hacking",
"Paginas ciencia de datos": "estudio",
"Paginas embeddings": "estudio",
"Paginas para investigar": "hacking",
"Pandas para knime": "estudio",
"Plasmic para el diseño frontend": "tech",
"Preparacion de carpeta para aplicaciones con react": "tech",
"Presencia en Red": "personal",
"Prioridades": "personal",
"Probabilidad": "estudio",
"Prompt Marimo para llm": "tech",
"Prompt para marimo": "tech",
"Prompt para trabajar con Mantine": "tech",
"Prompt para tutoriales de librerias": "tech",
"Prompts para modelos": "tech",
"Proveedores de contexto": "tech",
"Proyecto Miguel Facturas": "proyectos",
"Prueba de comentarios": "tech",
"Prueba formula en latex": "tech",
"Prueba texto markdown": "tech",
"PruebaDibujoAndroid": "personal",
"Pruebas de extraccion": "tech",
"Quants": "estudio",
"Qué es una simulación": "estudio",
"Redes de criptomonedas": "finanzas",
"Reino de Nicoya - Wikipedia, la enciclopedia libre": "personal",
"Sensores de windows con WMI": "tech",
"Servidor minecraft docker": "tech",
"Servidor ssh para Windows": "tech",
"Simulaciones - 01": "estudio",
"Sitios desde donde descargar datasets": "estudio",
"SocialCasino Programacion": "proyectos",
"Sprites para simulacion": "tech",
"TAREAS DATABROKER": "personal",
"Tareas Gente": "personal",
"Tareas Mias": "personal",
"Texto control de espacios y saltos de linea": "tech",
"Tutorial Instalación de Docker": "tech",
"Tutorial Servidores Coolify": "tech",
"Tutorial Sympy": "estudio",
"Tutorial de React": "estudio",
"Unidad de Control (Control Unit - CU)": "estudio",
"Untitled": "otros",
"Untitled 1": "otros",
"Untitled 10": "otros",
"Untitled 11": "otros",
"Untitled 12": "otros",
"Untitled 13": "otros",
"Untitled 2": "otros",
"Untitled 3": "otros",
"Untitled 4": "otros",
"Untitled 5": "otros",
"Untitled 6": "otros",
"Untitled 7": "otros",
"Untitled 8": "otros",
"Untitled 9": "otros",
"Ver environment de conda por defecto en la terminal de visual studio": "tech",
"Ver severance online": "personal",
"Verano Joven 2024": "personal",
"Vscode-navegador": "tech",
"cURL": "tech",
"comandos para conectarse a ssh": "tech",
"comandos raspberry pi": "tech",
"como arreglar mi cuerpo by-ashlly": "personal",
"como crear un metodo de estudio en mi tablet": "personal",
"como crear un metodo de estudio en mi tablet.sync-conflict-20240912-102912-XYN43SQ": "personal",
"como crear un metodo de estudio en mi tablet.sync-conflict-20240912-102933-XYN43SQ": "personal",
"correos temporales nuevo": "personal",
"darkweblinks": "hacking",
"dataherrero": "tech",
"diseño ui": "tech",
"endpoints": "tech",
"ffdjklafjl": "otros",
"formulas random": "otros",
"instalar ollama en wsl": "tech",
"livekit": "tech",
"popelisss": "otros",
"prompt rob0ts": "tech",
"redes neuronales python": "estudio"
}
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Organiza AurgiObsidian in-situ por reglas (los titulos tienen prefijos consistentes).
Mover notas dentro del mismo vault es seguro: Obsidian resuelve wikilinks/embeds por nombre,
no por ruta. Idempotente. Con --apply mueve; sin flag solo muestra el plan.
"""
import sys, os, re, shutil, glob
from collections import Counter
V = "/home/enmanuel/Obsidian/AurgiObsidian"
FECHA = re.compile(r'^\d{4}-\d{2}-\d{2}')
def cat(t):
tl = t.lower()
if FECHA.match(t):
return "bitacora"
if "excalidraw" in tl or t.startswith("Drawing") or t.startswith("2334Drawing"):
return "diagramas"
if t.startswith(("Carga DDS", "Carga Stg", "STG.", "ETL", "INITIAL_", "Carga Movimientos")) \
or t.startswith(("Llenar", "llenar_", "insert_", "update ")):
return "etl"
if t.startswith("Navision."):
return "navision"
if "reunion" in tl:
return "reuniones"
if t.startswith("GENAI") or "mioti" in tl or "prompt engineering" in tl or "curso palantir" in tl:
return "cursos"
if any(k in tl for k in ["bigquery", "big query", "looker", "metabase", "kpi", "dashboard",
"cuadro de mando", "cubo", "informe", "reporte", "visualiz", "tabla de hecho",
"modelos de datos", "segmentacion", "clustering"]):
return "bi"
if any(k in tl for k in ["sql", "query", "querys", "tabla", "tablas", "conexion", "conexiones",
"transformacion", "transformaciones", "origen", "origenes", "tpv", "vista",
"datos", "bbdd", "base de datos"]):
return "datos"
return "otros"
def main():
apply = "--apply" in sys.argv
notes = [p for p in glob.glob(f"{V}/**/*.md", recursive=True)
if "/.obsidian/" not in p and "/.git/" not in p]
plan = []
for p in notes:
rel = os.path.relpath(p, V)
if "/" in rel: # ya en subcarpeta — no remover (excepto Compartir en Drive lo dejamos)
continue
plan.append((p, cat(os.path.basename(p)[:-3])))
dist = Counter(c for _, c in plan)
print(f"AurgiObsidian: {len(plan)} notas sueltas en raiz")
for c, n in dist.most_common():
print(f" {c}: {n}")
if not apply:
print("\n(dry-run; usa --apply para mover)")
return
moved = 0
for p, c in plan:
dd = f"{V}/{c}"; os.makedirs(dd, exist_ok=True)
dst = f"{dd}/{os.path.basename(p)}"
if os.path.exists(dst):
continue
shutil.move(p, dst); moved += 1
print(f"\nmovidas: {moved}")
if __name__ == "__main__":
main()