From 7fd2436bc3f3948005088de20fc1b219819478c8 Mon Sep 17 00:00:00 2001 From: fn-registry agent Date: Thu, 14 May 2026 02:06:41 +0200 Subject: [PATCH] chore: sync from fn-registry agent --- app.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 app.md diff --git a/app.md b/app.md new file mode 100644 index 0000000..0a6a823 --- /dev/null +++ b/app.md @@ -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