Agent Skills: Teaching Your AI Coding Assistant to Write Better Angular Code
If you've used AI coding assistants like Claude Code, Cursor, or GitHub Copilot, you've probably noticed something frustrating: they often generate outdated code. Ask for an Angular component, and you might get constructor-based dependency injection instead of the modern inject() function, or *ngIf instead of the new @if control flow. This happens because these AI models were trained on code from a specific point in time, and frameworks like Angular evolve rapidly.
Agent Skills solve this problem. They're simple instruction files that teach AI assistants how to write code the way you want it written.
What exactly is an Agent Skill?
Think of an Agent Skill like a recipe card you hand to a chef. The chef (the AI) already knows how to cook, but the recipe card tells them exactly how you want your dish prepared—which ingredients to use, which to avoid, and what the final result should look like.
In technical terms, a skill is just a markdown file named SKILL.md that contains instructions for the AI. When you ask the AI to do something that matches a skill's purpose, it reads those instructions and follows them.
Here's the simplest possible skill:
---
name: my-angular-skill
description: "Use when creating Angular components or services."
---
# Angular Rules
Always use the `inject()` function for dependency injection.
Never use constructor injection.
That's it. When the AI recognizes you're working on Angular code, it reads this file and follows the rules.
Why do Angular developers need skills?
Angular has changed dramatically in recent versions. The patterns that were correct in Angular 14 are now deprecated in Angular 18 and beyond. Without guidance, AI assistants will generate a mix of old and new patterns because their training data contains years of Angular code written in different styles.
Here's what typically happens without a skill:
You ask: "Create a user service that fetches data from an API"
AI generates (outdated pattern):
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {} // ❌ Old pattern
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
}
What you actually want (modern pattern):
@Injectable({
providedIn: 'root'
})
export class UserService {
private http = inject(HttpClient); // ✅ Modern pattern
getUsers(): Observable<User[]> {
return this.http.get<User[]>('/api/users');
}
}
The difference seems small, but multiply this across an entire codebase and you end up with inconsistent code that mixes patterns from different Angular eras.
Creating your first Angular skill
Let's build a practical skill that enforces modern Angular patterns. Create a folder called .claude/skills/angular-modern/ in your project root, then add a SKILL.md file:
---
name: angular-modern-patterns
description: Generate Angular 18+ code using signals, standalone components,
and inject(). Use when creating components, services, directives,
or refactoring existing Angular code.
---
# Modern Angular Development Patterns
When writing Angular code, follow these patterns consistently.
## Dependency Injection
Use the `inject()` function instead of constructor injection:
typescript
// ✅ Correct - use this pattern
export class UserComponent {
private userService = inject(UserService);
private router = inject(Router);
}
// ❌ Wrong - never generate this
export class UserComponent {
constructor(
private userService: UserService,
private router: Router
) {}
}
## Components
All components must be standalone. In Angular v20+, omit `standalone: true`
since it's the default:
typescript
// ✅ Correct for Angular 20+
@Component({
selector: 'app-user-card',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule],
template: ...
})
export class UserCardComponent {
// Use signal inputs instead of @Input decorator
user = input.required();
// Use output function instead of @Output decorator
userSelected = output();
}
## Control Flow
Use the new built-in control flow syntax instead of structural directives:
typescript
// ✅ Correct - new control flow
@if (user()) {
} @else {
No user found
}
@for (item of items(); track item.id) {
}
// ❌ Wrong - old directives
## Signals for State
Prefer signals over plain properties for reactive state:
typescript
export class CounterComponent {
// ✅ Correct - signals for reactive state
count = signal(0);
doubleCount = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
}
Save this file, and now every time you ask the AI to generate Angular code, it will follow these patterns.
## How skills actually work under the hood
When you start a conversation with Claude Code (or another skill-enabled AI), here's what happens:
1. **Metadata loading:** The AI reads just the `name` and `description` from each skill file. This costs very few tokens—roughly 50-100 per skill.
2. **Pattern matching:** When you make a request, the AI checks if any skill descriptions match what you're asking for. "Create an Angular component" matches our skill's description about creating components.
3. **Full loading:** Only when there's a match does the AI read the complete skill instructions. This keeps conversations efficient—you're not paying for unused context.
4. **Code generation:** The AI follows the skill's instructions when generating its response.
This "progressive disclosure" design means you can have dozens of skills installed without slowing anything down. The AI only loads what it needs.
## Organizing skills for a real Angular project
For a production Angular application, you'll likely want several skills working together. Here's a practical folder structure:
your-angular-project/
├── .claude/
│ └── skills/
│ ├── angular-modern/
│ │ └── SKILL.md # Core patterns we created above
│ ├── component-generator/
│ │ ├── SKILL.md # Component scaffolding workflow
│ │ └── templates/
│ │ └── component.ts # Template file for reference
│ ├── ngrx-patterns/
│ │ └── SKILL.md # State management conventions
│ └── testing/
│ └── SKILL.md # Jest and TestBed patterns
├── CLAUDE.md # Project-level context (always loaded)
└── src/
└── ...
The `CLAUDE.md` file at your project root is special—it loads automatically for every conversation and should contain high-level project context:
markdown
Project: Customer Portal
Tech Stack
- Angular 20
- NgRx Signals for state management
- Angular Material with custom theme
- Jest for testing
Conventions
- All components use OnPush change detection
- Feature modules organized by domain (users, orders, products)
- API calls go through services, never directly in components
Commands
-
npm run test- Run unit tests -
npm run e2e- Run Playwright tests -
npm run lint- Check code style
## A complete example: Component generation workflow
Let's create a more advanced skill that handles a common workflow—generating a complete Angular feature with a component, service, and tests.
Create `.claude/skills/feature-generator/SKILL.md`:
markdown
name: angular-feature-generator
description: Generate complete Angular features including component, service,
and tests. Use when asked to create a new feature, page, or
module for the application.
Angular Feature Generation
When asked to create a new feature, generate these files in order:
1. Service (data layer first)
// feature-name.service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class FeatureNameService {
private http = inject(HttpClient);
// Methods return Observables for flexibility
// Components convert to signals as needed
}
2. Component (presentation layer)
// feature-name.component.ts
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-feature-name',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [], // Add required imports
template: ``
})
export class FeatureNameComponent {
private featureService = inject(FeatureNameService);
// Convert service observables to signals for template
// data = toSignal(this.featureService.getData());
}
3. Test file
// feature-name.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClientTesting } from '@angular/common/http/testing';
describe('FeatureNameComponent', () => {
let component: FeatureNameComponent;
let fixture: ComponentFixture<FeatureNameComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FeatureNameComponent],
providers: [provideHttpClientTesting()]
}).compileComponents();
fixture = TestBed.createComponent(FeatureNameComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
File organization
Place generated files in the appropriate feature folder:
src/app/features/feature-name/
├── feature-name.component.ts
├── feature-name.component.spec.ts
├── feature-name.service.ts
└── feature-name.service.spec.ts
Now when you ask "Create a user profile feature," the AI knows exactly what files to generate, in what order, and following which patterns.
Skills versus other AI extension methods
You might wonder how skills compare to other ways of extending AI assistants, like MCP servers or function calling. They serve different purposes and work well together:
Skills teach the AI how to approach tasks. They provide patterns, conventions, and domain knowledge. Skills are static instruction files that get loaded into the conversation.
MCP (Model Context Protocol) connects the AI to external systems. An MCP server might give the AI access to your Figma designs, your GitHub repository, or a live database. MCP provides dynamic, real-time data.
Function calling lets the AI execute specific actions, like running a calculation or calling an API endpoint.
For Angular development, a practical setup might combine all three:
- Skill: Teaches the AI your component patterns and coding standards
- MCP server: Connects to Figma so the AI can see your designs while generating components
-
Function: Runs
ng generatecommands to actually create the files
Getting started today
Here's a practical path to start using skills in your Angular projects:
Step 1: Create the .claude/skills/ folder in your project root.
Step 2: Add a basic Angular patterns skill using the example from this post.
Step 3: Create a CLAUDE.md file documenting your project's tech stack and conventions.
Step 4: Test it by asking the AI to generate a component and verify it follows your patterns.
Step 5: Iterate. When the AI generates something wrong, add a rule to your skill to prevent it next time.
The Angular team has also published official AI configuration that you can generate with ng new in Angular CLI v20.2+. This gives you a solid starting point that you can customize for your team's specific conventions.
Conclusion
Agent Skills transform AI coding assistants from generic code generators into specialized development partners that understand your project's conventions. For Angular developers dealing with rapidly evolving framework patterns, skills ensure the AI generates modern, consistent code instead of a mixture of deprecated approaches.
The investment is minimal—a few markdown files—but the payoff is significant. Instead of constantly correcting AI-generated code, you teach the AI once and it follows your patterns consistently. As your conventions evolve, you update the skill files and every future interaction reflects those changes.
Start small with a single skill covering your most important patterns, and expand from there as you identify more opportunities for guidance.
Top comments (1)
This resonates a lot. The outdated patterns problem is exactly what makes AI-generated code feel unreliable in fast-moving frameworks like Angular.
I really like the recipe card framing for Agent Skills — it explains why this works without getting abstract. The progressive loading idea (metadata first, full skill only on match) also clears up a lot of confusion around token usage.
Curious how you’ve seen teams adopt this in practice: do most start with one strict modern patterns skill, or do they usually need to break it up early into multiple smaller skills?