Agent Skills : transformez votre IA en développeur Angular senior
Les agents IA n’ont jamais été aussi puissants. Mais sur Angular… ils sont restés bloqués sur la version 16.
Alors que nous sommes passés à l’ère de l’Angular moderne avec les signaux (signal(), input()), les nouveaux contrôles de flux (@if, @for) et les composants autonomes, les LLMs continuent de générer du code d’un autre temps : @Input, *ngFor et NgModule.
Jusqu’ici, nous ne pouvions donc pas profiter pleinement de la puissance de l’IA.
Mais la donne vient de changer. L’équipe Angular vient de publier ses skills officiels pour faire passer votre agent IA d’un développeur Angular junior à un senior. Ces skills s’appuient sur le standard ouvert Agent Skills, initié par Anthropic et supporté par les principaux agents IA comme GitHub Copilot, Claude Code et Gemini CLI.
Qu’est-ce que le standard Agent Skills ?
Un Agent Skill est un dossier contenant un fichier SKILL.md qui inclut des métadonnées (un frontmatter YAML qui doit inclure au minimum name et description) et des instructions expliquant à un agent comment accomplir une tâche spécifique.
Un skill peut également contenir :
- du code exécutable dans un dossier
scripts/ - de la documentation de référence en Markdown dans un dossier
references/ - des ressources statiques (templates, images, schémas de données) dans un dossier
assets/
Contrairement au format AGENTS.md (un fichier de contexte global chargé systématiquement et entièrement à chaque session), les skills sont chargés de manière différée et progressive.
Lorsqu’un agent commence une tâche, il charge uniquement les métadonnées du skill pour déterminer dans quel contexte et à quel moment il doit lire le reste du fichier SKILL.md.
Cela permet aux agents de disposer de toutes les informations nécessaires sans saturer leur fenêtre de contexte ni gaspiller des tokens.
Comment fonctionnent les “Agent Skills” ?
Le chargement progressif des Agent Skills se déroule en trois phases — découverte, activation, exécution — comme illustré dans le diagramme suivant :
- Phase 1 — Découverte : Au démarrage, l’agent charge uniquement le nom et la description de chaque skill.
- Décision : Pour chaque tâche reçue, l’agent évalue si elle correspond à la description d’un skill. Si ce n’est pas le cas, le skill reste inactif. Sinon, on passe à l’activation.
- Phase 2 — Activation : L’agent charge dans son contexte les instructions détaillées contenues dans le fichier
SKILL.md. - Phase 3 — Exécution : L’agent suit les instructions, charge des fichiers supplémentaires référencés dans le skill si besoin, et exécute du code si nécessaire.
Comment installer les skills Angular ?
Les skills Angular sont disponibles dans le dépôt GitHub angular/skills. Mais pour les installer dans votre projet, il suffit de lancer la commande suivante :
npx skills add angular/skills
Une fois cette commande lancée, vous pourrez choisir les différents skills que vous souhaitez installer parmi les deux disponibles :
angular-developerpour enseigner aux agents comment développer en Angular.angular-new-apppour enseigner aux agents à générer de nouveaux projets Angular.
Cela aura pour effet de télécharger et de placer les skills sélectionnés dans le dossier .agents/skills. C’est dans ce dossier que les agents compatibles — comme Claude Code, GitHub Copilot ou Gemini CLI — iront chercher les skills disponibles.
Le chemin exact peut varier selon l’agent, mais .agents/skills est le chemin conventionnel adopté par la plupart des implémentations.
NOTA BENE : Il est important de noter que ce projet a d’abord vu le jour au sein de la communauté, notamment via le dépôt analogjs/angular-skills maintenu par Brandon Roberts. Face au succès de l’initiative et à l’importance du standard Agent Skills, l’équipe Angular a décidé de reprendre le flambeau en proposant un dépôt officiel angular/skills.
Avant / Après : ce que ça change vraiment
J’ai généré une nouvelle application Angular et demandé à un agent IA (modèle Gemini 3 Flash) de développer une fonctionnalité pour afficher une liste de plats.
Sans les skills
L’agent sans skills a généré du vieux code Angular :
- Décorateurs
@Input(),@Output(). - Directives structurelles
*ngIfet*ngFor. - Des
.subscribe()à profusion - Lifecycle hook
ngOnInit
Voici un exemple de ce qui a été généré. Le code complet peut être trouvé dans la branche without-skills du dépôt ahasall/angular-skills-demo.
import { Component, Input, Output, EventEmitter } from '@angular/core';import { CommonModule } from '@angular/common';import { Dish } from '../../models/dish.model';import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({ selector: 'app-dish-card', standalone: true, imports: [CommonModule], templateUrl: './dish-card.html', styleUrl: './dish-card.css', animations: [ trigger('heartAnimation', [ transition(':increment', [ style({ transform: 'scale(0.8)' }), animate('150ms ease-out', style({ transform: 'scale(1.3)' })), animate('150ms ease-in', style({ transform: 'scale(1)' })) ]), transition(':decrement', [ style({ transform: 'scale(1.2)' }), animate('150ms ease-in-out', style({ transform: 'scale(1)' })) ]) ]) ]})export class DishCardComponent { @Input({ required: true }) dish!: Dish; @Output() likeToggle = new EventEmitter<number>();
onLike() { this.likeToggle.emit(this.dish.id); }
getStatusClass() { switch (this.dish.status) { case 'Available': return 'bg-green-100 text-green-700'; case 'Out of Stock': return 'bg-orange-100 text-orange-700'; case 'Unavailable': return 'bg-red-100 text-red-700'; default: return 'bg-gray-100 text-gray-700'; } }}
<div class="bg-white rounded-xl shadow-md overflow-hidden transition-all duration-300 hover:shadow-lg flex flex-col h-full border border-gray-100"> <div class="relative h-48 overflow-hidden group"> <img [src]="dish.image || 'https://images.unsplash.com/photo-1495195129352-aec325b55b65?w=500&q=80'" [alt]="dish.name" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"> <button (click)="$event.stopPropagation(); onLike()" class="absolute top-3 right-3 p-2 rounded-full bg-white/80 backdrop-blur-sm shadow-sm transition-all duration-300 hover:bg-white active:scale-90" [class.text-red-500]="dish.liked" [class.text-gray-400]="!dish.liked" [@heartAnimation]="dish.liked ? 1 : 0"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24" stroke="none"> <path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3c1.54 0 2.93.651 3.912 1.693C12.58 3.651 13.97 3 15.512 3c2.974 0 5.438 2.322 5.438 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001z" /> </svg> </button> </div>
<div class="p-4 flex flex-col flex-grow"> <div class="flex justify-between items-start mb-2 gap-2"> <h3 class="text-lg font-semibold text-gray-900 truncate">{{ dish.name }}</h3> <span class="text-lg font-bold text-indigo-600 shrink-0">{{ dish.price | currency:'EUR' }}</span> </div>
<div class="mt-auto pt-3 flex items-center justify-between"> <span [class]="getStatusClass()" class="px-2.5 py-0.5 rounded-full text-xs font-medium"> {{ dish.status }} </span>
<button (click)="onLike()" class="text-sm font-medium transition-colors duration-200" [class.text-red-500]="dish.liked" [class.text-gray-500]="!dish.liked"> {{ dish.liked ? 'Liked' : 'Like' }} </button> </div> </div></div>
Avec les skills
L’agent avec skills a généré du code Angular moderne :
- Inputs basés sur les signaux
input() - Signal-like
output() - Nouveaux contrôles de flux avec
@ifet@for resourcepour la récupération et la gestion de l’état des données
Voici un exemple de ce qui a été généré. Le code complet peut être trouvé dans la branche with-skills du dépôt ahasall/angular-skills-demo.
import { Component, input, output, computed } from '@angular/core';import { Dish } from '../dish.model';import { CommonModule } from '@angular/common';import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
@Component({ selector: 'app-dish-card', standalone: true, imports: [CommonModule], templateUrl: './dish-card.html', animations: [ trigger('heartBeat', [ transition('unliked => liked', [ animate( '300ms ease-in', keyframes([ style({ transform: 'scale(1)', offset: 0 }), style({ transform: 'scale(1.3)', offset: 0.5 }), style({ transform: 'scale(1)', offset: 1.0 }), ]), ), ]), ]), ],})export class DishCardComponent { dish = input.required<Dish>(); toggleLike = output<string>();
statusClasses = computed(() => { switch (this.dish().status) { case 'Available': return 'bg-emerald-100 text-emerald-700'; case 'Out of Stock': return 'bg-orange-100 text-orange-700'; case 'Unavailable': return 'bg-red-100 text-red-700'; default: return 'bg-gray-100 text-gray-700'; } });}
<div class="bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 flex flex-col h-full border border-gray-100"> <div class="relative h-48 w-full bg-gray-200"> @if (dish().image) { <img [src]="dish().image" [alt]="dish().name" class="h-full w-full object-cover" /> } @else { <div class="h-full w-full flex items-center justify-center text-gray-400"> <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor" > <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> </div> } <button (click)="toggleLike.emit(dish().id)" class="absolute top-3 right-3 p-2 rounded-full bg-white/80 backdrop-blur-sm shadow-sm hover:bg-white transition-colors group" [@heartBeat]="dish().liked ? 'liked' : 'unliked'" > <svg xmlns="http://www.w3.org/2000/svg" [class.fill-red-500]="dish().liked" [class.stroke-red-500]="dish().liked" [class.stroke-gray-400]="!dish().liked" [class.fill-none]="!dish().liked" class="h-6 w-6 transition-colors" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" > <path stroke-linecap="round" stroke-linejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> </svg> </button> </div>
<div class="p-5 flex flex-col flex-grow"> <div class="flex justify-between items-start mb-2"> <h3 class="text-lg font-bold text-gray-800 line-clamp-1">{{ dish().name }}</h3> <span class="text-emerald-600 font-bold">{{ dish().price | currency:'EUR' }}</span> </div>
<div class="mt-auto flex items-center justify-between"> <span class="px-3 py-1 rounded-full text-xs font-medium" [ngClass]="statusClasses()"> {{ dish().status }} </span> </div> </div></div>
Nota Bene : gardons les pieds sur terre. L’IA reste un assistant, pas le pilote. Même avec ces skills, le code généré — ici avec un modèle léger comme Gemini 3 Flash — n’est pas parfait : CommonModule toujours présent, ngClass au lieu de class. Il y a toujours du travail de relecture.
Conclusion
Les agents IA sont devenus des outils du quotidien pour les développeurs. Mais un agent qui ne connaît pas l’Angular moderne est un agent qu’on ne peut pas vraiment faire confiance pour écrire du code production-ready. Les skills Angular changent ça. En une commande, votre agent passe de générateur de code d’un autre temps à vrai partenaire de développement : il connaît les signaux, le nouveau contrôle de flux, les composants autonomes — tout ce qui fait l’Angular d’aujourd’hui. Le meilleur dans tout ça ? Les skills sont de simples fichiers Markdown, versionnables et portables. Vous pouvez les adapter à votre guise ou même créer les vôtres.
Vous aimez ce blog ?
Suivez-moi sur Twitter pour plus de contenu !
Rejoignez la newsletter pour du contenu de grande qualité dans votre boite mail
