From ng
Generate Angular artifacts (components, services, pipes, directives, guards, interceptors, routes, stores). Use automatically when the user asks to create any Angular code, scaffold a feature, or add a new file to an Angular project. Mirrors Angular CLI conventions.
npx claudepluginhub mayeedwin/angular-plugin --plugin ngThis skill uses the workspace's default tool permissions.
**Primary reference**: https://angular.dev/llms.txt — always consult Angular documentation for current API.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Primary reference: https://angular.dev/llms.txt — always consult Angular documentation for current API.
app.config.ts (standalone app) vs app.module.ts (module-based app)tsconfig.json to find path aliases (@shared/*, @core/*, etc.)package.json for Angular version and installed packages (NgRx, signals API availability)jest.config.js) or Karma (karma.conf.js) is the test runnerng generate)Angular 20+: Component files use
{name}.ts(no.component.suffix) with matching{name}.html,{name}.scss,{name}.spec.ts. All other artifacts retain their type suffix (.service.ts,.pipe.ts, etc.).
| Angular CLI | Plugin equivalent | Output |
|---|---|---|
ng g c {name} | /ng:generate component {name} | {name}.ts + {name}.html + {name}.scss + {name}.spec.ts |
ng g s {name} | /ng:generate service {name} | {name}.service.ts + {name}.service.spec.ts |
ng g p {name} | /ng:generate pipe {name} | {name}.pipe.ts + {name}.pipe.spec.ts |
ng g d {name} | /ng:generate directive {name} | {name}.directive.ts + {name}.directive.spec.ts |
ng g g {name} | /ng:generate guard {name} | {name}.guard.ts + {name}.guard.spec.ts |
ng g interceptor {name} | /ng:generate interceptor {name} | {name}.interceptor.ts + {name}.interceptor.spec.ts |
ng g r {name} | /ng:generate routes {name} | {name}.routes.ts |
| — | /ng:generate store {name} | NgRx feature store (actions/reducer/selectors/effects/index) |
src/app/shared/{type}s/{artifact-name}/src/app/pages/{feature}/{type}s/{artifact-name}/src/app/core/services/src/app/core/guards/ or src/app/core/interceptors/src/app/pages/{feature}/Always create an index.ts barrel in the containing directory if one does not already exist.
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
@Component({
selector: 'app-{name}',
standalone: true,
imports: [],
templateUrl: './{name}.component.html',
styleUrl: './{name}.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class {Name}Component {
// Use inject() — never constructor injection
// private readonly service = inject({Name}Service);
}
HTML template — use new Angular control flow:
<!-- @if / @for / @switch — never *ngIf / *ngFor -->
providedIn: 'root' default)import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class {Name}Service {
private readonly http = inject(HttpClient);
}
Use feature-level providers array in routes config when service must be scoped to a lazy route.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: '{name}', standalone: true, pure: true })
export class {Name}Pipe implements PipeTransform {
transform(value: unknown, ...args: unknown[]): unknown {
return value;
}
}
import { Directive, inject } from '@angular/core';
@Directive({ selector: '[app{Name}]', standalone: true })
export class {Name}Directive {
// private readonly el = inject(ElementRef);
}
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
export const {name}Guard: CanActivateFn = (route, state) => {
const router = inject(Router);
// return true | false | UrlTree
return true;
};
import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
export const {name}Interceptor: HttpInterceptorFn = (req, next) => {
return next(req);
};
import { Routes } from '@angular/router';
import { {Name}Component } from './{name}.component';
export const {NAME}_ROUTES: Routes = [
{
path: '',
component: {Name}Component,
},
];
actions.ts:
import { createAction, props } from '@ngrx/store';
export const load{Name} = createAction('[{Name}] Load');
export const load{Name}Success = createAction('[{Name}] Load Success', props<{ data: {Name}[] }>());
export const load{Name}Failure = createAction('[{Name}] Load Failure', props<{ error: string }>());
reducer.ts:
import { createReducer, on } from '@ngrx/store';
import * as {Name}Actions from './actions';
export interface {Name}State {
data: {Name}[];
loading: boolean;
error: string | null;
}
const initialState: {Name}State = { data: [], loading: false, error: null };
export const {name}Reducer = createReducer(
initialState,
on({Name}Actions.load{Name}, state => ({ ...state, loading: true, error: null })),
on({Name}Actions.load{Name}Success, (state, { data }) => ({ ...state, loading: false, data })),
on({Name}Actions.load{Name}Failure, (state, { error }) => ({ ...state, loading: false, error })),
);
selectors.ts:
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { {Name}State } from './reducer';
export const select{Name}State = createFeatureSelector<{Name}State>('{name}');
export const select{Name}Data = createSelector(select{Name}State, s => s.data);
export const select{Name}Loading = createSelector(select{Name}State, s => s.loading);
export const select{Name}Error = createSelector(select{Name}State, s => s.error);
effects.ts:
import { inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, of, switchMap } from 'rxjs';
import * as {Name}Actions from './actions';
import { {Name}Service } from '../services/{name}.service';
export const load{Name}Effect = createEffect(
(actions$ = inject(Actions), service = inject({Name}Service)) =>
actions$.pipe(
ofType({Name}Actions.load{Name}),
switchMap(() =>
service.getAll().pipe(
map(data => {Name}Actions.load{Name}Success({ data })),
catchError(err => of({Name}Actions.load{Name}Failure({ error: err.message }))),
),
),
),
{ functional: true },
);
Always use takeUntilDestroyed() — never ngOnDestroy + Subject pattern:
private readonly destroyRef = inject(DestroyRef);
ngOnInit() {
this.service.data$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => { /* ... */ });
}
After creating any artifact in a directory, ensure index.ts exports it:
export * from './{name}.component';
export * from './{name}.service';
// etc.
any — use unknown and narrow, or define an interface@Input() and @Output() must be typedreadonly for injected dependencies and signal() values that should not be reassigned