--- name: propose_capability_groups kind: pipeline lang: bash domain: pipelines version: "1.0.0" purity: impure signature: "propose_capability_groups([--min-count N], [--max-domains M], [--json], [--apply tag]) -> report" description: "Analiza tags candidatos a capability group (issue 0086). Filtra via blocklist + cap de dominios. Lista candidatos o promociona con --apply (llama generate_capability_doc + actualiza INDEX.md)." tags: ["capability-groups", "doctor", "audit", "pipeline"] uses_functions: - generate_capability_doc_bash_pipelines uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [] params: - name: --min-count desc: "minimo de funciones con el tag para considerarlo candidato (default 3)" - name: --max-domains desc: "maximo de dominios distintos entre funciones del tag (default 3, filtra tags genericos que cruzan muchos dominios)" - name: --json desc: "salida JSON estructurada en vez de texto (util para agentes)" - name: --apply desc: "promociona el tag dado a capability group: genera doc con generate_capability_doc + actualiza docs/capabilities/INDEX.md (idempotente)" output: "tabla de candidatos a stdout (tag, count, domains, already_group, samples), o efectos en disco si --apply (nuevo .md + fila en INDEX.md)" tested: false tests: [] test_file_path: "" file_path: "bash/functions/pipelines/propose_capability_groups.sh" --- ## Ejemplo ```bash # Listar candidatos con al menos 10 funciones bash bash/functions/pipelines/propose_capability_groups.sh --min-count 10 # Listar con salida JSON (para agentes) bash bash/functions/pipelines/propose_capability_groups.sh --min-count 5 --json # Promocionar un tag a capability group bash bash/functions/pipelines/propose_capability_groups.sh --apply metabase # Via fn run ./fn run propose_capability_groups --min-count 10 ``` ## Logica ### Modo listar (sin --apply) 1. Resuelve la raiz del registry (walk-up hasta `registry.db`). 2. Filtra tags via **blocklist** hardcodeada (idiomas, dominios, CRUD generico, verbos super-genericos, primitivas, estados). 3. Query SQL con `json_each(f.tags)` + `GROUP BY` + `HAVING cnt >= N AND domains <= M`. 4. Marca cada candidato con `already_group: yes/no` parseando los links `[tag](tag.md)` de `docs/capabilities/INDEX.md`. 5. Imprime tabla formateada con maximo 3 IDs de muestra por candidato. 6. Resumen: total candidatos, ya-grupos, nuevos. ### Modo --apply 1. Valida que el tag no esta en blocklist y pasa el filtro de count/domains. 2. Llama a `bash/functions/pipelines/generate_capability_doc.sh `. 3. Inserta fila en `docs/capabilities/INDEX.md` (idempotente — no duplica si ya existe). 4. Imprime checklist de edicion manual para el usuario. ## Blocklist Tags en la blocklist nunca son candidatos (caso exacto): - **Idioma**: `go py bash ps ts python cpp` - **Dominio**: `core infra finance datascience cybersecurity shell tui pipelines browser` - **Kind/purity**: `function pipeline component pure impure` - **CRUD generico**: `add create delete list update get set remove insert` - **Verbo super-generico**: `compose convert combine append empty exists check find format parse render` - **Estructural**: `generic helper utility wrapper test` - **Primitivas**: `string number int float array slice map dict value key` - **Estados**: `pending-usar pendiente-usar` ## Notas - Usa `sqlite3` directo para el JOIN custom `functions + json_each(tags)` (autorizado por la regla registry_calls.md — JOINs custom no expuestos por el MCP). - La insercion en INDEX.md usa `python3` embebido para manejar la posicion relativa a la cabecera de tabla de forma portable. - El flag `--max-domains` es la heuristica clave: un tag como `add` aparece en 8 dominios → filtrado; `metabase` aparece en 2 → candidato valido.