Communitygithub.com

ASNNetworks/floh-skills

Compétences d'agent par FLOH. Capacités SKILL.md portables pour Claude Code, Codex, Gemini CLI, Cursor et d'autres clients d'agents.

Qu'est-ce que floh-skills ?

Cette compétence d'agent fournit une interface en ligne de commande (`pharos`) pour interagir avec Azure DevOps directement depuis un shell, sans nécessiter de connexion via navigateur. Elle permet de créer, lire, mettre à jour, supprimer, lier et délier des éléments de travail, de gérer les pièces jointes, les commentaires, l'historique des modifications, les itérations et les zones, ainsi que de manipuler les pages wiki. La fonctionnalité `pharos query` remplace avantageusement les appels WIQL bruts en renvoyant des éléments de travail hydratés (avec tous leurs champs) en une seule commande, évitant ainsi des appels individuels coûteux. La compétence prend également en charge la création de plans d'implémentation sous forme d'arbres d'éléments de travail, la gestion des hooks de service et l'upload d'images avec génération de code Markdown. Les identifiants (organisation, projet et token PAT) sont stockés dans l'environnement après la configuration initiale, et la compétence fonctionne sur tous les clients d'agents prenant en charge les fichiers `SKILL.md`, comme Claude Code, Codex, Gemini CLI et Cursor.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Aperçu

Depuis le README du skill

floh-skills preview
graphique
diagramme

Documentation

Travailler avec Azure DevOps via pharos

pharos est une CLI qui vous donne accès à Azure DevOps depuis un terminal, authentifiée par une variable d'environnement plutôt que par une connexion navigateur.

Tout ce qu'elle peut faire est listé ci-dessous. N'exécutez pas pharos --help pour le découvrir — cela coûte plusieurs appels et cette section contient les mêmes informations.

whoami                         à qui appartient ADO_PAT. @Me résout cette personne
types                          ce que --state et --type ACCEPTENT, par type
query                          QUELS éléments de travail — qui vous sont assignés, dans un sprint,
                               d'un type, encore ouverts. Éléments hydratés, pas des identifiants.
task <id>                      un élément de travail complet : champs, commentaires,
                               pièces jointes, relations AVEC titres, et le
                               contenu + discussion de chaque page wiki liée
                               --children  un EPIC et tout ce qu'il contient, en
                               UN SEUL appel. --depth <n> pour plus profond (max 5)
                               --compact   aplatir les identités, supprimer les clés de tableau
create <type> --title "…"      un élément de travail. --parent va dans le MÊME correctif
update <id>                    modifier un champ : --state --priority --assignee
                               --title, ou --field Name=valeur pour autre chose
link <id> --parent <id>        relier deux éléments. Aussi --child --related
unlink <id> --parent <id>      --predecessor --successor --duplicate
link <id> --wiki-page <path>   lier une PAGE WIKI à un élément de travail — le côté élément
wiki links <path>              quels éléments de travail lient une page (ADO n'a pas d'API)
wiki link <path> --item <id>   le côté WIKI ; --item répétable
wiki unlink <path> --item <id>
history <id>                   ce qui a CHANGÉ, champ par champ, qui et quand
iterations | areas             les sprints avec leurs dates, et les zones
links                          chaque type de relation que cette ORGA possède, et quel
                               --flag y accède (six sur ~dix-huit)
fields [--type T]              ce qu'un champ ACCEPTE — valeurs autorisées
attach <id> <file>             mettre un FICHIER sur un élément de travail (liste Pièces jointes)
detach <id> <url-ou-guid>      en retirer un. --yes. Ou --name <fichier>
download <id-élément|guid|url> lire un fichier. Un ID D'ÉLÉMENT DE TRAVAIL avec --name <fichier>,
                               ou --all --out <rép>. --out ou rien, c'est écrit
image <file>                   télécharger une IMAGE pour utilisation dans le texte. Imprime le
                               markdown à coller. PAS la même chose que attach
people [query]                 qui peut être @mentionné, avec le format @<guid>
delete <id> --yes              → Corbeille (pas de suppression définitive, intentionnellement)
restore <id>                   ramener un élément
deleted                        ce qu'il y a dans la Corbeille, avec les noms.
                               --top <n> — elle renvoie 50 par défaut et signale
                               le total réel comme "count"
wiki list | tree | read <path> | write <path> | delete <path>
wiki move <path> <new path>    déplacer une page ; les sous-pages suivent
wiki rename <path> <new name>  le même appel, feuille seulement. LES DEUX ont besoin de --yes
wiki duplicate <path> [to]     un verbe que Azure DevOps n'a pas. "<path> - Copie N"
wiki image <file>              une image pour une PAGE ou un commentaire de page. Point de terminaison
                               différent de `image`, et le nom est rendu unique
wiki import <file...>          .md .txt .docx .pdf .rtf .html -> pages. Un
                               fichier .pptx est refusé, intentionnellement — voir ci-dessous
                               --under <path> --as <name>
comment list | add | edit | delete   <cible> est un id d'élément de travail OU un chemin wiki
comment react | unreact | reactors   like dislike heart hooray smile confused
hooks list | check | create | repoint | delete    webhooks en temps réel.
                               `check` nécessite --hub <url> ; `list` montre l'URL
                               déjà utilisée
plan <file>                    un plan d'implémentation → une arborescence d'éléments de travail
setup                          org, projet, token → trousseau + profil shell

Saisie de texte : --text / --file / --stdin. Global : --pretty pour un humain, --yes pour les verbes destructifs, --dry-run pour prévisualiser.

ADO_ORG, ADO_PROJECT et ADO_PAT sont déjà dans l'environnement après la configuration. Ne les vérifiez pas avant de travailler ; une absence se signale comme "kind": "config" avec code de sortie 2, c'est le seul moment où cela compte.

Trouver le travail : pharos query

N'utilisez pas curl et le point de terminaison WIQL. Cette compétence vous donnait auparavant une recette pour exactement cela, car il n'y avait pas de verbe de requête. Il y en a un maintenant, et il fait ce que la recette ne pouvait pas faire : WIQL renvoie uniquement des identifiants, donc la recette vous donnait seize numéros bruts et un appel par élément pour leur donner un sens.

pharos query --mine                       # assigné à vous, toujours ouvert
pharos query --sprint "Sprint 1"          # --sprint current pour @currentIteration
pharos query --type Epic --state Doing    # tous deux répétables
pharos query --assignee "[email protected]"
pharos query --tag api --all              # --all inclut le travail terminé
pharos query --wiql "SELECT [System.Id] FROM WorkItems WHERE …"   # échappatoire

Les drapeaux se combinent (ET). La sortie est des éléments hydratés — id, type, titre, état, assigné, itération, tags, priorité, modifié — donc --mine est une seule commande, pas une requête suivie d'une récupération par résultat. Utilisez pharos task <id> uniquement pour ceux sur lesquels vous allez réellement travailler.

Lisez le champ openness avant de signaler un décompte. "12 ouverts" n'a pas de sens tant que vous ne savez pas ce qui a été compté comme terminé, et Azure DevOps permet à un modèle de processus de renommer chaque état. query lit les catégories d'état du projet et vous indique quels états il a traités comme terminaux ; s'il n'a pas pu les lire, il le dit et retombe sur une estimation, et c'est le signal pour passer --state explicitement. assignedTo nomme qui @Me a réellement résolu — un jeton partagé ou de service signifie que "assigné à moi" peut silencieusement désigner quelqu'un d'autre.

--all se combine avec tous les autres filtres, donc --mine --all est la façon de distinguer rien ne vous est assigné de rien n'est ouvert. Un simple pharos query --all est valide et renvoie l'ensemble du projet.

--sprint accepte soit le nom simple soit le fieldPath et normalise entre eux — la règle fieldPath concerne System.IterationPath en tant que valeur de champ. Mais un sprint nommé correspond avec UNDER (sous-itérations incluses) tandis que --sprint current correspond avec = (exact), donc ce ne sont pas les mêmes requêtes.

--wiql accepte également une requête FROM WorkItemLinks … MODE (MustContain) et hydrate les identifiants de la même manière, ce qui vous permet de trouver chaque élément portant un type de relation donné en un seul appel.

N'écrivez PAS [System.State] NOT IN GROUP 'Completed' si vous utilisez --wiql. Elle analyse, renvoie 200 et correspond à tout — IN GROUP ne couvre que les catégories de TYPE d'élément de travail, et un groupe inconnu se résout en ensemble vide sans erreur. Mesuré, le 05/08/2026.

Modifier un élément de travail

pharos update 225 --state Doing              # changer l'état quand le travail avance
pharos update 225 --priority 1 --assignee "[email protected]"
pharos update 225 --field Microsoft.VSTS.Scheduling.RemainingWork=3
pharos attach 225 ./bestsellers.xlsx --comment "Les chiffres"

update lit l'élément et applique la modification sous une opération test sur /rev, donc si quelqu'un a écrit entre votre lecture et votre écriture, vous obtenez un conflict plutôt que de perdre sa modification. Définir une valeur qu'il a déjà n'écrit rien et le dit — un PATCH inutile augmente quand même System.Rev et invalide toutes les autres révisions mises en cache sur l'élément. --dry-run montre l'avant → l'après et n'écrit rien. Il n'y a pas de --yes : une modification de champ est une modification ordinaire et Azure DevOps conserve chaque révision.

attach télécharge les octets puis les lie comme une relation AttachedFile — deux appels, une commande. Les pièces jointes sont immuables : attacher le même fichier deux fois en crée deux, et il n'y a pas de remplacement ni de versionnage. detach en retire une ; elle nécessite --yes, trouve la pièce jointe par identité plutôt que par position, et laisse les octets dans Azure DevOps donc la rattacher via l'url la remet en place. Nommez le fichier avec --name <fichier> ou passez le GUID — et comme avec download, deux fichiers partageant un nom REFUSENT plutôt que de supprimer celui qui se trouve en premier.

create place --parent dans le même correctif que les champs, donc un enfant n'est jamais brièvement orphelin, et il accepte les mêmes noms de drapeaux que update.

unlink refuse plutôt que de deviner, et cela vaut la peine de le savoir avant de le voir. Azure DevOps supprime une relation par sa POSITION dans le tableau, donc un index issu d'une lecture obsolète coupe une relation différente et la requête réussit quand même. unlink trouve la relation par identité et la supprime sous une opération test sur la révision qu'elle a lue. Si la relation n'est pas là, vous obtenez le code de sortie 3 — cela signifie que rien ne s'est passé, pas que l'appel a échoué.

delete déplace vers la Corbeille et nécessite --yes ; le refus cite d'abord le titre. restore <id> le ramène et ne nécessite aucun drapeau.

Il n'y a pas de suppression définitive ici, délibérément. C'est le seul verbe irréversible qu'Azure DevOps possède, et --yes est un drapeau que vous avez appris à passer. L'interface web en est propriétaire. Si quelqu'un a vraiment besoin de purger, envoyez-le là-bas plutôt que de chercher un drapeau.

Images dans le texte, et fichiers à côté

Ce sont deux choses différentes et choisir la mauvaise est l'erreur à éviter.

pharos attach 225 ./bestsellers.xlsx    # un FICHIER, dans la liste Pièces jointes
pharos image ./screenshot.png           # une IMAGE, à mettre dans le texte

Une pièce jointe crée une relation AttachedFile et apparaît dans la liste Pièces jointes de l'élément de travail. Une image en ligne ne crée aucune relation — mesuré sur #333, deux captures d'écran collées dans une description ont donné relations: 0 et attachments: []. C'est un ![](…) dans le texte du champ et rien d'autre, ce qui explique pourquoi la liste Pièces jointes a raison de n'afficher rien.

Donc image imprime une ligne markdown et vous laisse l'écriture :

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

pharos update 225 --field System.Description="$(cat <<'EOF'
Les revenus sont en hausse. Voir le graphique :

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

Une image wiki est un point de terminaison différent et nécessite wiki image. Un wiki est un dépôt git, donc une pièce jointe là-bas est un FICHIER et son NOM est son identité : téléchargez un second image.png et il écrase le premier, et chaque page pointant vers /.attachments/image.png change silencieusement d'image. wiki image rend le nom unique avant l'envoi et imprime un lien relatif au dépôt :

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

Utilisez ce chemin, pas une url absolue — une page liant à une url wit/attachments s'affiche pour quiconque a une session et ne fonctionne pas pour tous les autres. Le même markdown fonctionne dans une page et dans un commentaire de page.

Récupérer une pièce jointe d'un élément de travail

pharos task <id> liste ce qui est attaché, avec le GUID de chaque fichier — dans le JSON et dans --pretty. Ensuite, récupérez-le en un seul appel, par nom :

pharos download 41 --name Skills.pptx --out ./Skills.pptx
pharos download 41 --all --out ./attachments      # toutes les pièces jointes de l'élément
pharos download <guid-or-url> --out ./file.bin    # quand vous en avez déjà un

--out est ce qui écrit. Sans lui, vous obtenez la taille et aucun fichier — les octets bruts sur la sortie standard corrompraient le JSON que chaque autre verbe imprime.

Un simple pharos download 41 est une erreur d'utilisation portant la liste — nom, GUID et taille — donc choisir le bon fichier ne coûte jamais un second appel. Deux pièces jointes avec le même nom refusent plutôt que de deviner : les pièces jointes sont immuables, donc attacher un fichier deux fois en crée deux et le nom n'est pas une identité.

detach accepte le même --name <fichier>, donc le verbe que vous utilisez après avoir lu task --pretty accepte ce qu'il vous a montré :

pharos detach 41 --name Skills.pptx --yes      # --dry-run le prévisualise d'abord

Lire ce qu'il contient est VOTRE travail, avec VOS outils

download vous donne des octets sur le disque et s'arrête là. Transformer un .docx, .pptx, .xlsx ou .pdf en quelque chose de lisible est le travail de votre environnement.

N'utilisez pas pharos-convert pour cela. C'est le convertisseur de wiki import, partagé avec l'application macOS pour que les deux produisent la même page à partir du même fichier — sa sortie est conçue pour devenir une page wiki, et l'utiliser ici couple ce que vous lisez au chemin d'import de l'application. Il ne peut pas non plus lire les fichiers .pptx.

Deux façons dont cela échoue silencieusement, toutes deux mesurées sur un vrai élément de travail :

  • Vous avez cherché l'outil au mauvais endroit. which markitdown sur le PATH système et import docx sur le python3 système retournent tous deux vides sur une machine où ces outils sont installés — dans un venv par compétence. Un agent qui exécute ces deux vérifications conclut "rien ici" et contourne des outils qui étaient là depuis le début. Regardez où votre agent conserve ses outils avant de conclure qu'ils sont absents.
  • Un texte non vide n'est pas une preuve que vous avez lu le document. Un vrai fichier .pptx s'est extrait en sept fragments, environ 90 caractères ; l'intégralité de la spécification se trouvait dans deux PNG intégrés. Tout lecteur de texte seul renvoie quelque chose pour ce fichier et a l'air d'avoir fonctionné. Pour un diaporama, ou un PDF numérisé, le contenu est généralement constitué des images — extrayez-les et regardez-les réellement.

Demandez-vous d'abord si vous lisez les PDF nativement. Si c'est le cas, c'est une commande.

De nombreux agents — Claude Code parmi eux — lisent un PDF visuellement, page par page, comme une personne le regarde. Cela couvre un scan sans aucun texte, avec aucune étape d'OCR. Si c'est votre cas, tout le problème se réduit à une seule conversion :

soffice --headless --convert-to pdf f.docx --outdir ./out    # .pptx, .xlsx aussi
#

…

[truncé pour la traduction]

Skills associés