Communitygithub.com

FLOH Skills (Azure DevOps con pharos)

Skills portátiles para agentes IA. Proporciona capacidades de Azure DevOps a través de la CLI `pharos` para Claude Code, Codex, Gemini CLI, Cursor y otros clientes.

¿Qué es FLOH Skills (Azure DevOps con pharos)?

Este skill proporciona una interfaz completa de Azure DevOps a través de la herramienta de línea de comandos `pharos`, que se autentica mediante la variable de entorno `ADO_PAT`, eliminando la necesidad de un inicio de sesión en el navegador. Está diseñado para agentes de IA como Claude Code, Cursor, Codex y Gemini CLI, permitiéndoles realizar operaciones comunes de Azure DevOps directamente desde el shell. Incluye comandos para consultar elementos de trabajo (`pharos query`) con filtros como asignado a mí, sprint, tipo, estado y tags; crear, actualizar y eliminar elementos de trabajo; gestionar archivos adjuntos e imágenes en texto; trabajar con wikis; y gestionar enlaces entre elementos. Una característica clave es que `pharos query` devuelve elementos de trabajo 'hidratados' (con toda su información) en una sola llamada, evitando múltiples consultas por elemento. El skill también maneja conflictos de concurrencia mediante operaciones `test` en la revisión y evita cambios en valores ya establecidos para no incrementar innecesariamente la revisión del elemento. Además, incluye advertencias sobre cómo leer correctamente archivos adjuntos (por ejemplo, PDFs y presentaciones) y consejos para evitar errores comunes como confundir imágenes en línea con archivos adjuntos.

Compatible con✓Claude Code✓Codex CLI✓Cursor✓Gemini CLI
npx skills add ASNNetworks/floh-skills

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Vista previa

Del README del skill

FLOH Skills (Azure DevOps con pharos) preview
chart
diagram

Documentación

Trabajar con Azure DevOps mediante pharos

pharos es una CLI que te ofrece Azure DevOps desde el shell, autenticada mediante una variable de entorno en lugar de un inicio de sesión en el navegador.

Todo lo que puede hacer se enumera a continuación. No ejecutes pharos --help para averiguarlo, ya que eso cuesta varias llamadas y esta sección contiene la misma información.

whoami                         a quién pertenece ADO_PAT. @Me resuelve a esto
types                          qué --state y --type ACEPTARÁN, por tipo
query                          QUÉ elementos de trabajo — asignados a ti, en un sprint,
                               de un tipo, aún abiertos. Elementos hidratados, no ids.
task <id>                      un elemento de trabajo completo: campos, comentarios,
                               archivos adjuntos, relaciones CON títulos, y el
                               contenido + discusión de cada página wiki vinculada
                               --children  un EPIC y todo lo que contiene, en
                               UNA llamada. --depth <n> para más profundo (máx. 5)
                               --compact   aplana identidades, elimina claves del tablero
create <type> --title "…"      un elemento de trabajo. --parent va en la MISMA revisión
update <id>                    cambiar un campo: --state --priority --assignee
                               --title, o --field Nombre=valor para cualquier otra cosa
link <id> --parent <id>        relacionar dos elementos. También --child --related
unlink <id> --parent <id>      --predecessor --successor --duplicate
link <id> --wiki-page <ruta>   vincular una PÁGINA WIKI a un elemento de trabajo — el lado del elemento
wiki links <ruta>              qué elementos de trabajo enlazan una página (ADO no tiene API)
wiki link <ruta> --item <id>   el lado WIKI; --item repetible
wiki unlink <ruta> --item <id>
history <id>                   qué CAMBIÓ, campo por campo, quién y cuándo
iterations | areas             los sprints con sus fechas, y las áreas
links                          cada tipo de relación que tiene esta ORG, y qué
                               --flag lo alcanza (seis de ~dieciocho)
fields [--type T]              qué ACEPTARÁ un campo — valores permitidos
attach <id> <archivo>          poner un ARCHIVO en un elemento de trabajo (lista de Archivos adjuntos)
detach <id> <url-o-guid>       quitar uno. --yes. O --name <archivo>
download <id-elemento|guid|url>  leer uno de vuelta. Un ID DE ELEMENTO con --name <archivo>,
                               o --all --out <dir>. --out o nada se escribe
image <archivo>                subir una IMAGEN para usar dentro del texto. Imprime el
                               markdown para pegar. NO es lo mismo que attach
people [consulta]              quién puede ser @mencionado, con la forma @<guid>
delete <id> --yes              → Papelera de reciclaje (sin eliminación permanente, a propósito)
restore <id>                   recuperar uno
deleted                        qué hay en la Papelera de reciclaje, con nombres.
                               --top <n> — devuelve 50 por defecto e informa
                               el total real como "count"
wiki list | tree | read <ruta> | write <ruta> | delete <ruta>
wiki move <ruta> <nueva ruta>   mover una página; las subpáginas vienen con ella
wiki rename <ruta> <nuevo nombre>  la misma llamada, solo hoja. AMBAS necesitan --yes
wiki duplicate <ruta> [a]       un verbo que Azure DevOps no tiene. "<ruta> - Copia N"
wiki image <archivo>           una imagen para una PÁGINA o comentario de página. Diferente
                                punto final de `image`, y el nombre es único
wiki import <archivo...>       .md .txt .docx .pdf .rtf .html -> páginas. Un
                                .pptx se rechaza, a propósito — ver más abajo
                                --under <ruta> --as <nombre>
comment list | add | edit | delete   <destino> es un id de elemento de trabajo O una ruta wiki
comment react | unreact | reactors   like dislike heart hooray smile confused
hooks list | check | create | repoint | delete    enlaces de servicio para tiempo real.
                                `check` necesita --hub <url>; `list` muestra la URL
                                ya en uso
plan <archivo>                  un plan de implementación → un árbol de elementos de trabajo
setup                          org, proyecto, token → llavero + perfil de shell

Entrada de texto: --text / --file / --stdin. Global: --pretty para humanos, --yes para verbos destructivos, --dry-run para previsualizar.

ADO_ORG, ADO_PROJECT y ADO_PAT ya están en el entorno después de la configuración. No los verifiques antes de trabajar; uno faltante se anuncia a sí mismo como "kind": "config" al salir con código 2, que es la única vez que importa.

Encontrar el trabajo: pharos query

No recurras a curl y al punto final WIQL. Este skill solía darte una receta para exactamente eso, porque no había un verbo query. Ahora lo hay, y hace la parte que la receta no podía: WIQL devuelve solo ids, así que la receta te daba dieciséis números simples y una llamada por elemento para darles sentido.

pharos query --mine                       # asignados a ti, aún abiertos
pharos query --sprint "Sprint 1"          # --sprint current para @currentIteration
pharos query --type Epic --state Doing    # ambos repetibles
pharos query --assignee "[email protected]"
pharos query --tag api --all              # --all incluye trabajo terminado
pharos query --wiql "SELECT [System.Id] FROM WorkItems WHERE …"   # escape hatch

Las banderas se combinan con AND. La salida son elementos hidratados — id, tipo, título, estado, asignado a, iteración, tags, prioridad, cambiado — así que --mine es un solo comando, no una consulta seguida de una obtención por resultado. Usa pharos task <id> solo para los pocos en los que realmente vas a trabajar.

Lee el campo openness antes de informar un conteo. "12 abiertos" no significa nada hasta que sepas qué se consideró terminado, y Azure DevOps permite que una plantilla de proceso renombre cada estado. query lee las categorías de estado del propio proyecto y te dice qué estados trató como terminales; si no pudo leerlos, lo dice y recurre a adivinanzas, y esa es tu señal para pasar --state explícitamente. assignedTo nombra a quién resolvió realmente @Me — un token compartido o de servicio hace que "asignado a mí" signifique silenciosamente otra persona.

--all se combina con cualquier otro filtro, así que --mine --all es cómo sabes si nada está asignado a ti de nada abierto. Un pharos query --all simple es válido y devuelve todo el proyecto.

--sprint toma el nombre simple o el fieldPath y normaliza entre ellos — la regla de fieldPath trata System.IterationPath como un valor de campo. Pero un sprint nombrado coincide con UNDER (subiteraciones incluidas) mientras que --sprint current coincide con = (exacto), por lo que no son la misma consulta.

--wiql también acepta una consulta FROM WorkItemLinks … MODE (MustContain) y hidrata los ids de la misma manera, que es cómo encuentras cada elemento que lleva un tipo de relación dado en una sola llamada.

NO escribas [System.State] NOT IN GROUP 'Completed' si recurres a --wiql. Se analiza, devuelve 200 y coincide con todo — IN GROUP cubre solo categorías de tipo de elemento de trabajo, y un grupo desconocido se resuelve en el conjunto vacío sin error. Medido, 2026-08-05.

Cambiar un elemento de trabajo

pharos update 225 --state Doing              # mueve el estado cuando el trabajo se mueve
pharos update 225 --priority 1 --assignee "[email protected]"
pharos update 225 --field Microsoft.VSTS.Scheduling.RemainingWork=3
pharos attach 225 ./bestsellers.xlsx --comment "Los números"

update lee el elemento y aplica el cambio bajo una operación test en /rev, así que alguien que escribió entre tu lectura y tu escritura recibe un conflict en lugar de perder su edición. Establecer un valor que ya tiene no escribe nada y lo dice — una PATCH sin sentido aún incrementa System.Rev e invalida todas las demás revisiones almacenadas en caché del elemento. --dry-run muestra el antes → después y no escribe nada. No hay --yes: una edición de campo es una edición ordinaria y Azure DevOps mantiene cada revisión.

attach sube los bytes y luego los vincula como una relación AttachedFile — dos llamadas, un comando. Los archivos adjuntos son inmutables: adjuntar el mismo archivo dos veces hace dos de ellos, y no hay reemplazo ni versionado. detach quita uno; necesita --yes, encuentra el archivo adjunto por identidad en lugar de por posición, y deja los bytes en Azure DevOps para que volver a adjuntar la url lo recupere. Nombra el archivo con --name <archivo> o pasa el GUID — y como con download, dos archivos que comparten un nombre RECHAZAN en lugar de eliminar el que se ordene primero.

create pone --parent en la misma revisión que los campos, por lo que un hijo nunca es brevemente huérfano, y toma los mismos nombres de bandera que update.

unlink rechaza en lugar de adivinar, y vale la pena saberlo antes de verlo. Azure DevOps elimina una relación por su POSICIÓN en el arreglo, por lo que un índice de una lectura desactualizada corta un enlace diferente y la solicitud aún tiene éxito. unlink encuentra la relación por identidad y la elimina bajo una operación test en la revisión que leyó. Si la relación no está ahí, obtienes código de salida 3 — eso significa que no pasó nada, no que la llamada falló.

delete mueve a la Papelera de reciclaje y necesita --yes; el rechazo cita el título primero. restore <id> lo recupera y no necesita bandera.

No hay eliminación permanente aquí, deliberadamente. Es el único verbo irreversible que Azure DevOps tiene, y --yes es una bandera que has aprendido a pasar. La interfaz web es dueña de ello. Si alguien realmente necesita purgar, envíalo allí en lugar de buscar una bandera.

Imágenes en texto y archivos adjuntos

Estas son dos cosas diferentes y elegir la incorrecta es el error que vale la pena evitar.

pharos attach 225 ./bestsellers.xlsx    # un ARCHIVO, en la lista de Archivos adjuntos
pharos image ./screenshot.png           # una IMAGEN, para poner dentro del texto

Un archivo adjunto crea una relación AttachedFile y aparece en la lista de Archivos adjuntos del elemento de trabajo. Una imagen en línea no crea ninguna relación — medido en #333, dos capturas de pantalla pegadas en una descripción dieron relations: 0 y attachments: []. Es un ![](…) en el texto del campo y nada más, por lo que la lista de Archivos adjuntos está bien al no mostrar nada para ello.

Así que image imprime una línea de markdown y deja la escritura a tu criterio:

pharos image ./chart.png --pretty
# ![chart](https://dev.azure.com/…/wit/attachments/<guid>?fileName=chart.png)

pharos update 225 --field System.Description="$(cat <<'EOF'
Los ingresos aumentaron. Mira el gráfico:

![chart](https://dev.azure.com/…/wit/attachments/<guid>?fileName=chart.png)
EOF
)"

Una imagen de wiki es un punto final diferente y necesita wiki image. Una wiki es un repositorio git, por lo que un archivo adjunto allí es un ARCHIVO y su NOMBRE es su identidad: sube una segunda image.png y aterriza sobre la primera, y cada página que apunta a /.attachments/image.png cambia silenciosamente de imagen. wiki image hace que el nombre sea único antes de enviarlo e imprime un enlace relativo al repositorio:

pharos wiki image ./diagram.png --pretty
# ![diagram](/.attachments/diagram-1786046773042.png)

Usa esa ruta, no una url absoluta — una página que enlaza a una url wit/attachments se renderiza para cualquiera que tenga una sesión y se rompe para todos los demás. El mismo markdown funciona tanto en una página como en un comentario de página.

Obtener un archivo adjunto de un elemento de trabajo

pharos task <id> lista lo que está adjunto, con el GUID de cada archivo — en el JSON y en --pretty. Luego tómalo en una sola llamada, por nombre:

pharos download 41 --name Skills.pptx --out ./Skills.pptx
pharos download 41 --all --out ./attachments      # todos los archivos adjuntos del elemento
pharos download <guid-or-url> --out ./file.bin    # cuando ya tienes uno

--out es lo que escribe. Sin él obtienes el tamaño y ningún archivo — los bytes sin procesar en stdout corromperían el JSON que imprime cada otro verbo.

Un pharos download 41 simple es un error de uso que lleva la lista — nombre, GUID y tamaño — por lo que elegir el archivo correcto nunca cuesta una segunda llamada. Dos archivos adjuntos con el mismo nombre rechazan en lugar de adivinar: los archivos adjuntos son inmutables, por lo que adjuntar un archivo dos veces hace dos de ellos y el nombre no es una identidad.

detach toma el mismo --name <archivo>, por lo que el verbo al que recurres después de leer task --pretty acepta lo que te mostró:

pharos detach 41 --name Skills.pptx --yes      # --dry-run lo previsualiza primero

Leer lo que hay dentro es TU trabajo, con TUS herramientas

download te da bytes en el disco y se detiene ahí. Convertir un .docx, .pptx, .xlsx o .pdf en algo que puedas leer es trabajo de tu entorno.

No uses pharos-convert para ello. Es el convertidor de wiki import, compartido con la aplicación macOS para que ambos produzcan la misma página a partir del mismo archivo — su salida está formada para convertirse en una página wiki, y recurrir a él aquí acopla lo que lees a la ruta de importación de la aplicación. Tampoco puede leer .pptx en absoluto.

Dos formas en que esto falla silenciosamente, ambas medidas en un elemento de trabajo real:

  • Verificaste la herramienta en el lugar equivocado. which markitdown contra el PATH del sistema y import docx contra el python3 del sistema ambos devuelven vacío en una máquina donde esa herramienta está instalada — en un venv por skill. Un agente que ejecuta esas dos comprobaciones concluye "no hay nada aquí" y elude herramientas que estaban allí todo el tiempo. Mira dónde tu agente guarda sus herramientas antes de concluir que están ausentes.
  • El texto no vacío no es prueba de que leíste el documento. Un .pptx real extrajo siete fragmentos, alrededor de 90 caracteres; toda la especificación estaba en dos PNGs incrustados. Cualquier lector solo de texto devuelve algo para esa presentación y parece que funcionó. Para una presentación de diapositivas o un PDF escaneado, la carga útil suele ser las imágenes — extráelas y míralas realmente.

Primero pregúntate si lees PDFs de forma nativa. Si es así, esto es un solo comando.

Muchos agentes — Claude Code entre ellos — leen un PDF visualmente, página por página, como una persona lo ve. Eso cubre un escaneado sin texto en absoluto, sin paso de OCR. Si ese es tu caso, todo el problema se reduce a una conversión:

soffice --headless --convert-to pdf f.docx --outdir ./out    # .pptx, .xlsx también
# …

Skills relacionados