chore: sync from fn-registry agent

This commit is contained in:
fn-registry agent
2026-05-14 02:06:41 +02:00
commit 7fd2436bc3
+103
View File
@@ -0,0 +1,103 @@
---
name: fn_match
lang: go
domain: infra
description: "Fuzzy matcher entre un comando shell o snippet heredoc y funciones del registry. Devuelve las top-N funciones mas probables que cubren ese patron, con score normalizado. Consumido por el hook PreToolUse para sugerir funciones del registry en vez de codigo inline."
tags: [discovery, hooks, match, fuzzy, registry]
uses_functions: []
uses_types: []
framework: ""
entry_point: "cmd/fn/match.go"
dir_path: "apps/fn_match"
repo_url: ""
params:
- name: command
desc: "Comando shell o snippet heredoc a analizar. Puede pasarse como argumento posicional o por stdin."
- name: top
desc: "Numero maximo de resultados a devolver (default 3)."
- name: format
desc: "Formato de salida: json (default) o text."
- name: min-score
desc: "Umbral minimo de score normalizado 0..1 (default 0.3). Resultados por debajo se descartan."
output: "JSON o texto con las top-N funciones del registry ordenadas por score descendente. Score 1.0 = mejor match; resto son relativos al top."
---
## Ejemplo
```bash
# Arg posicional — JSON por defecto
./fn match "taskkill.exe /IM registry_dashboard.exe /F"
# Texto legible
./fn match --format text --top 5 "rsync -avz src/ user@host:/opt/app"
# Stdin pipe (util en hooks)
echo "curl -sf https://api.example.com/health | jq .status" | ./fn match
# Subir umbral para matches muy seguros solamente
./fn match --min-score 0.7 "encrypt file sha256"
```
Salida JSON:
```json
{
"query": "taskkill.exe /IM registry_dashboard.exe /F",
"top": [
{
"id": "deploy_cpp_exe_to_windows_bash_infra",
"score": 1,
"signature": "deploy_cpp_exe_to_windows(app_name: string, app_dir: string) -> void",
"snippet": "Copia el .exe de Windows... Mata el proceso si esta corriendo (taskkill.exe pre-autorizado)..."
}
],
"high_confidence": false
}
```
## Cuando usarla
**Hook PreToolUse (uso principal):** El hook intercepta comandos Bash antes de ejecutarse. Si el comando supera el umbral de score, el hook inyecta en el contexto:
```
USE: deploy_cpp_exe_to_windows_bash_infra [score=1.000] instead of inline
```
**Debug manual:** Para entender por que Claude escribe cierto patron inline repetidamente, correr `fn match` con ese patron y ver si el registry ya cubre el caso.
**Auditoria de gaps:** Si `fn match` devuelve `top: []` para un patron que aparece >3 veces en sesiones, ese patron es candidato a nueva funcion del registry.
## Pipeline interno
1. **Tokenize**: split por no-alfanumericos + eliminacion de flags (`-v`, `/F`), paths absolutos (conserva basename sin extension), numeros puros, tokens < 3 chars, y stopwords del dominio (`registry`, `function`, `app`, `file`, `get`, `set`, ...).
2. **FTS5 query**: `tok1 OR tok2 OR ...` sobre `functions_fts` con `bm25()`. Resultado: hasta 50 candidatos con rank BM25.
3. **Fetch metadata**: segundo query `SELECT ... FROM functions WHERE id IN (...)` para obtener name, signature, description, tags.
4. **Re-score aditivo**: cada token suma su mejor boost de campo (`name=+3.0`, `tags=+2.0`, `signature=+1.5`, `description=+1.0`). No multiplicativo — evita que tokens genericos saturen todos los resultados a 1.0.
5. **Language penalty**: si query tiene markers Python (`def`, `import`, `class`) y el hit es bash → x0.5. Si tiene markers bash (`curl`, `rsync`, `taskkill`, `exe`) y hit es py → x0.5.
6. **Normalizar**: el top score raw se convierte en 1.0; el resto es relativo.
7. **Filtrar y limitar**: `--min-score` descarta ruido; `--top N` limita salida.
8. **High confidence flag**: `top[0].score / top[1].score > 1.5``"high_confidence": true`.
## Gotchas
**Latencia:** 6-7ms en WSL2 con 1255 funciones indexadas. Sin daemon — proceso fresh por invocacion. Bien por debajo del objetivo de 50ms.
**FTS5 WAL:** La conexion se abre en modo lectura normal (no `mode=ro`) porque bm25() con WAL no checkpointed falla con "missing row from content table". El binario nunca escribe.
**FTS5 rebuild:** Si `fn match` devuelve `top: []` para queries que deberias matchear, el indice FTS5 puede estar desincronizado. Solucion:
```go
// Rebuild manual del indice:
INSERT INTO functions_fts(functions_fts, rank) VALUES('rebuild', NULL);
INSERT INTO types_fts(types_fts, rank) VALUES('rebuild', NULL);
INSERT INTO unit_tests_fts(unit_tests_fts, rank) VALUES('rebuild', NULL);
```
O simplemente `./fn index` — reindexar regenera el FTS5.
**Stopwords del dominio:** Tokens como `registry`, `function`, `app`, `file`, `get`, `set` se descartan del tokenizer porque matchean cientos de funciones igualmente y no aportan señal. Si un query solo tiene stopwords, `fn match` retorna error "no significant tokens".
**Scores normalizados:** El score 1.0 no significa "match perfecto" — es el mejor candidato relativo al batch. Un `high_confidence: false` con score 1.0 significa que el segundo candidato es similar. Para gates en hooks usar `high_confidence: true` como criterio.
**Implementacion:** `cmd/fn/match.go` en el modulo raiz `fn-registry`. Se compila junto al binario `fn` principal — no es un binario separado. Invocar como `./fn match ...`.
## Capability growth log
- v1.0.0 (2026-05-14): scoring multiplicativo inicial — todos los hits clampean a 1.0 cuando varios tokens matchean
- v1.1.0 (2026-05-14): scoring aditivo por token con normalizacion relativa + stopwords del dominio. FTS5 rebuild fix. Latencia: 6-7ms