Angular
ericrisco/rsc-harness
A skill your agent uses when building, refactoring, or debugging Angular (v20/21+): standalone components, signals, zoneless change detection, @if/@for/@defer control flow, inject() DI…
A skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…
$ npx skills add kid-sid/claude-spellbook --skill angular -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install kid-sid/claude-spellbook angular --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/angular .claude/skills/angular && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .claude/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/kid-sid/claude-spellbook/tree/main/skills/angularType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add kid-sid/claude-spellbook --skill angular -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install kid-sid/claude-spellbook angular --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/angular .agents/skills/angular && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .agents/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kid-sid/claude-spellbook --skill angular -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install kid-sid/claude-spellbook angular --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/angular .cursor/skills/angular && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .cursor/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/kid-sid/claude-spellbook.git --path skills/angular--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add kid-sid/claude-spellbook --skill angular -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install kid-sid/claude-spellbook angular --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/angular .gemini/skills/angular && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .gemini/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install kid-sid/claude-spellbook angularInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add kid-sid/claude-spellbook --skill angular -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/angular .github/skills/angular && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .github/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kid-sid/claude-spellbook --skill angular -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install kid-sid/claude-spellbook angular --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/angular .opencode/skills/angular && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "angular" agent skill from https://github.com/kid-sid/claude-spellbook/tree/main/skills/angular into .opencode/skills/angular/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "angular", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
angularA skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…
Angular is an agent skill from kid-sid/claude-spellbook. Use when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change detection, or writing TestBed component tests.
Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Development, covering Web performance, Anomaly detection and Refactoring. It works with Angular and RxJS. The repository describes itself as: A curated collection of skills, prompts, and workflows that extend Claude's capabilities — your personal grimoire for AI-powered development. The licence is MIT.
Read from SKILL.md and the folder at commit a7c2ac9. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript and html).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Angular loads about 5k tokens when it runs. Until then it costs about 58 tokens; SKILL.md has 587 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from kid-sid/claude-spellbook at commit a7c2ac9, republished under its MIT licence (© kid-sid). 587 words, ~4,978 tokens.
.claude/skills/angular/SKILL.md (or your agent's skills folder).Modern Angular (v17+) conventions using standalone components, signals, and typed forms.
OnPushsrc/
├── app/
│ ├── core/ # Singleton services, interceptors, guards (provided in root)
│ │ ├── services/
│ │ ├── interceptors/
│ │ └── guards/
│ ├── shared/ # Reusable standalone components, pipes, directives
│ │ ├── components/
│ │ ├── pipes/
│ │ └── directives/
│ ├── features/ # Feature modules — each is a route subtree
│ │ ├── users/
│ │ │ ├── user-list/
│ │ │ ├── user-detail/
│ │ │ └── users.routes.ts
│ │ └── orders/
│ ├── app.component.ts
│ ├── app.config.ts # bootstrapApplication providers
│ └── app.routes.tsNo NgModule required. Default for all new components.
import { Component, inject, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import { UserService } from '../core/services/user.service';
@Component({
selector: 'app-user-list',
standalone: true,
imports: [CommonModule, RouterLink], // import what you use
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ul>
@for (user of users(); track user.id) {
<li>
<a [routerLink]="['/users', user.id]">{{ user.name }}</a>
</li>
}
@empty { <li>No users found.</li> }
</ul>
`,
})
export class UserListComponent {
private userService = inject(UserService); // inject() instead of constructor DI
users = this.userService.users; // signal from service
}Prefer @if, @for, @switch over *ngIf, *ngFor:
@if (isLoading()) {
<app-spinner />
} @else if (error()) {
<app-error [message]="error()" />
} @else {
<app-data-table [rows]="data()" />
}
@for (item of items(); track item.id) {
<app-item [item]="item" />
} @empty {
<p>Nothing here.</p>
}
@switch (status()) {
@case ('active') { <span class="green">Active</span> }
@case ('inactive') { <span class="red">Inactive</span> }
@default { <span>Unknown</span> }
}Signals replace BehaviorSubject for synchronous local and service state.
import { signal, computed, effect } from '@angular/core';
// Writable signal
const count = signal(0);
count.set(5);
count.update(n => n + 1);
count(); // read — always call as a function
// Computed — automatically tracks dependencies
const doubled = computed(() => count() * 2);
// Effect — runs when dependencies change (use sparingly)
effect(() => {
console.log('count changed:', count());
});@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
// Private writable, public readonly
private _users = signal<User[]>([]);
readonly users = this._users.asReadonly();
readonly activeUsers = computed(() =>
this._users().filter(u => u.active)
);
load() {
this.http.get<User[]>('/api/users').subscribe(users => {
this._users.set(users);
});
}
add(user: User) {
this._users.update(list => [...list, user]);
}
}import { toSignal, toObservable } from '@angular/core/rxjs-interop';
// Convert Observable to Signal (use in components instead of async pipe)
readonly users = toSignal(this.http.get<User[]>('/api/users'), {
initialValue: [] as User[],
});
// Convert Signal to Observable (pass to RxJS operators)
const count$ = toObservable(this.count);
count$.pipe(debounceTime(300)).subscribe(...);// Provided in root (singleton for the whole app)
@Injectable({ providedIn: 'root' })
export class AuthService { ... }
// Provided in a specific component subtree (new instance per component)
@Component({
providers: [LocalCartService], // scoped to this component and its children
})
// inject() function — preferred over constructor injection
export class MyComponent {
private auth = inject(AuthService);
private router = inject(Router);
}
// Injection tokens for non-class values
export const API_URL = new InjectionToken<string>('API_URL');
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' },
provideHttpClient(withInterceptors([authInterceptor])),
provideRouter(routes),
],
};
// Usage
private apiUrl = inject(API_URL);@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = inject(API_URL);
getUsers(): Observable<User[]> {
return this.http.get<User[]>(`${this.apiUrl}/users`);
}
createUser(data: CreateUserDto): Observable<User> {
return this.http.post<User>(`${this.apiUrl}/users`, data);
}
updateUser(id: string, data: Partial<User>): Observable<User> {
return this.http.patch<User>(`${this.apiUrl}/users/${id}`, data);
}
deleteUser(id: string): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/users/${id}`);
}
}// core/interceptors/auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const token = auth.getToken();
if (!token) return next(req);
return next(req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
}));
};
// core/interceptors/error.interceptor.ts
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) inject(AuthService).logout();
if (error.status === 0) console.error('Network error');
return throwError(() => error);
}),
);
};
// app.config.ts
provideHttpClient(withInterceptors([authInterceptor, errorInterceptor]))// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
// Lazy-loaded feature route
{
path: 'users',
loadChildren: () => import('./features/users/users.routes').then(m => m.USERS_ROUTES),
},
// Protected route
{
path: 'admin',
canActivate: [authGuard],
loadChildren: () => import('./features/admin/admin.routes').then(m => m.ADMIN_ROUTES),
},
{ path: '**', component: NotFoundComponent },
];
// features/users/users.routes.ts
export const USERS_ROUTES: Routes = [
{ path: '', component: UserListComponent },
{ path: ':id', component: UserDetailComponent,
resolve: { user: userResolver } },
];// Functional guard (Angular 14+)
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLoggedIn()) return true;
return router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } });
};
// Functional resolver
export const userResolver: ResolveFn<User> = (route) => {
return inject(UserService).getUser(route.paramMap.get('id')!);
};
// Component reads resolved data
export class UserDetailComponent {
private route = inject(ActivatedRoute);
user = toSignal(this.route.data.pipe(map(d => d['user'] as User)));
}import { FormBuilder, Validators, AbstractControl } from '@angular/forms';
@Component({
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<input formControlName="email" />
@if (email.invalid && email.touched) {
<span>{{ emailError() }}</span>
}
<input type="password" formControlName="password" />
<button type="submit" [disabled]="form.invalid">Save</button>
</form>
`,
})
export class UserFormComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
get email() { return this.form.controls.email; }
emailError = computed(() => {
if (this.email.hasError('required')) return 'Email is required';
if (this.email.hasError('email')) return 'Invalid email format';
return '';
});
submit() {
if (this.form.invalid) return;
console.log(this.form.getRawValue());
}
}function noSpaces(control: AbstractControl) {
return control.value?.includes(' ')
? { noSpaces: 'Username cannot contain spaces' }
: null;
}
// Async validator (e.g. check username availability)
function uniqueUsername(userService: UserService): AsyncValidatorFn {
return (control) =>
userService.checkUsername(control.value).pipe(
map(taken => taken ? { usernameTaken: true } : null),
catchError(() => of(null)),
);
}@Component({
imports: [FormsModule],
template: `
<form #f="ngForm" (ngSubmit)="submit(f)">
<input name="name" ngModel required #nameField="ngModel" />
@if (nameField.invalid && nameField.touched) {
<span>Name is required</span>
}
<button type="submit" [disabled]="f.invalid">Save</button>
</form>
`,
})
export class SimpleFormComponent {
submit(form: NgForm) {
if (form.valid) console.log(form.value);
}
}Common operators used in Angular services and components:
// switchMap — cancel previous, use latest (search, route params)
this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(query => this.search(query)), // cancels in-flight on new keystroke
).subscribe(results => this._results.set(results));
// combineLatest — react when any input changes
combineLatest([this.userId$, this.filter$]).pipe(
switchMap(([id, filter]) => this.loadOrders(id, filter)),
).subscribe(orders => this._orders.set(orders));
// takeUntilDestroyed — auto-unsubscribe when component destroys (Angular 16+)
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
this.data$.pipe(takeUntilDestroyed()).subscribe(...);
// shareReplay — multicast HTTP call so multiple subscribers don't cause multiple requests
readonly users$ = this.http.get<User[]>('/api/users').pipe(
shareReplay(1),
);OnPush skips re-render unless: an @Input() reference changes, a signal read in the template changes, an Observable piped with async emits, or markForCheck() is called.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<p>{{ title }}</p>
<p>{{ count() }}</p> <!-- signal: auto-tracked -->
<p>{{ data$ | async }}</p> <!-- observable: tracked by async pipe -->
`,
})
export class MyComponent {
@Input() title = ''; // change detection on reference change
count = signal(0); // change detection on signal change
data$ = this.service.data$; // change detection on async pipe emit
}Always use OnPush for new components. Combine with signals or async pipe — avoid manual markForCheck().
// Custom pure pipe (re-runs only when input changes)
@Pipe({ name: 'truncate', standalone: true })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 100, trail = '…'): string {
return value.length > limit ? value.slice(0, limit) + trail : value;
}
}
// Usage in template
{{ longText | truncate:50 }}
{{ longText | truncate:50:'...' }}
// Impure pipe (re-runs on every change detection cycle) — use sparingly
@Pipe({ name: 'filter', standalone: true, pure: false })Built-in pipes: date, currency, number, percent, json, async, keyvalue, titlecase, uppercase, lowercase, slice.
// Attribute directive — adds behaviour to host element
@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
@Input('appHighlight') color = 'yellow';
private el = inject(ElementRef);
private renderer = inject(Renderer2);
@HostListener('mouseenter') onEnter() {
this.renderer.setStyle(this.el.nativeElement, 'background', this.color);
}
@HostListener('mouseleave') onLeave() {
this.renderer.removeStyle(this.el.nativeElement, 'background');
}
}
// Usage
<p appHighlight="lightblue">Hover me</p>Use NgRx when: multiple features share state, you need time-travel debugging, or complex side effects via Effects.
// state/user.actions.ts
import { createActionGroup, emptyProps, props } from '@ngrx/store';
export const UserActions = createActionGroup({
source: 'Users',
events: {
'Load Users': emptyProps(),
'Load Users Success': props<{ users: User[] }>(),
'Load Users Failure': props<{ error: string }>(),
},
});
// state/user.reducer.ts
const initialState: UserState = { users: [], loading: false, error: null };
export const userReducer = createReducer(
initialState,
on(UserActions.loadUsers, state => ({ ...state, loading: true })),
on(UserActions.loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),
on(UserActions.loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false })),
);
// state/user.effects.ts
export const loadUsersEffect = createEffect(
(actions$ = inject(Actions), userService = inject(UserService)) =>
actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(e => of(UserActions.loadUsersFailure({ error: e.message }))),
),
),
),
{ functional: true },
);
// Component
store.dispatch(UserActions.loadUsers());
users = toSignal(store.select(selectAllUsers));import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
describe('UserListComponent', () => {
let fixture: ComponentFixture<UserListComponent>;
let userService: jasmine.SpyObj<UserService>;
beforeEach(async () => {
userService = jasmine.createSpyObj('UserService', ['getUsers']);
userService.getUsers.and.returnValue(of([{ id: '1', name: 'Alice' }]));
await TestBed.configureTestingModule({
imports: [UserListComponent], // standalone: import directly
providers: [{ provide: UserService, useValue: userService }],
}).compileComponents();
fixture = TestBed.createComponent(UserListComponent);
fixture.detectChanges();
});
it('renders a user', () => {
const item = fixture.debugElement.query(By.css('li'));
expect(item.nativeElement.textContent).toContain('Alice');
});
it('calls service on init', () => {
expect(userService.getUsers).toHaveBeenCalled();
});
});describe('UserService', () => {
let service: UserService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService, provideHttpClientTesting()],
});
service = TestBed.inject(UserService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => httpMock.verify()); // ensure no unexpected requests
it('fetches users', () => {
service.getUsers().subscribe(users => {
expect(users.length).toBe(1);
expect(users[0].name).toBe('Alice');
});
const req = httpMock.expectOne('/api/users');
expect(req.request.method).toBe('GET');
req.flush([{ id: '1', name: 'Alice' }]);
});
});| Pitfall | Fix |
|---|---|
| Memory leaks from subscriptions | Use takeUntilDestroyed() or async pipe |
ExpressionChangedAfterItHasBeenCheckedError | Don't mutate state during change detection; use OnPush + signals |
Injecting in constructor when using inject() | Pick one — don't mix constructor params and inject() in the same class |
*ngFor without trackBy on large lists | Add track item.id (new syntax) or trackBy function |
| Impure pipe causing performance issues | Switch to pure pipe + signal/async pipe combination |
Importing BrowserModule in feature modules | Only import in app.config.ts; use CommonModule or standalone imports in features |
| Forgetting to unsubscribe from route params | Use toSignal(this.route.paramMap) or takeUntilDestroyed() |
ChangeDetectionStrategy.Default on new components — Default CD runs on every browser event across the entire tree; all new components should use OnPush and rely on signals or the async pipesubscribe() without takeUntilDestroyed(), the async pipe, or an ngOnDestroy unsubscribe creates a memory leakany type in templates — Angular's strictTemplates flag catches type mismatches at compile time; any silences the check and defers the error to runtimeproviders array — a component-provided service creates a new instance per component; for singleton behavior, provide at the module or root level[(ngModel)] and reactive forms in the same component — template-driven and reactive form approaches manage state separately and conflict when mixed; pick one approach per formRenderer2, the CDK, or signalsmarkForCheck() after async updates in OnPush components — signals and the async pipe trigger CD automatically; manually pushed non-signal updates in OnPush require explicit markForCheck()ChangeDetectionStrategy.OnPushinject() used instead of constructor injectionBehaviorSubject for new code)toSignal() used in templates instead of async pipe where possibletakeUntilDestroyed() or async pipeFormBuilderhttp.get<User[]>(...))jasmine.createSpyObj for service dependencies, not real services© kid-sid, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/angular of kid-sid/claude-spellbook.
Open the folder on GitHubat commit a7c2ac9
Angular next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Angular this skillkid-sid/claude-spellbook | 189 | — | ~5k | Automated safety check: Pass | MIT | |
| Angularericrisco/rsc-harness | 156 | — | ~3.4k | Automated safety check: Pass | MIT | |
| Angular Clean Architecturefmflurry/settings-opencode | 171 | — | ~3.6k | Automated safety check: Pass | MIT | |
| Angular Best Practicesaiskillstore/marketplace | 430 | 5 repos | ~3.4k | Automated safety check: Pass | None | |
| Angular Componentaiskillstore/marketplace | 430 | 1 repos | ~1.7k | Automated safety check: Pass | None | |
| Rxjs Like A Prosanity-io/sanity | 6.4k | — | ~3k | Automated safety check: Pass | MIT |
ericrisco/rsc-harness
A skill your agent uses when building, refactoring, or debugging Angular (v20/21+): standalone components, signals, zoneless change detection, @if/@for/@defer control flow, inject() DI…
fmflurry/settings-opencode
Scaffolds and extends Angular standalone feature MODULES under src/app/modules/{name} using Clean Architecture layering (presentation/application/core/infrastructure), a self-registering module…
aiskillstore/marketplace
Angular performance optimization and best practices guide. An agent skill from aiskillstore/marketplace.
aiskillstore/marketplace
Create modern Angular standalone components following v20+ best practices.
sanity-io/sanity
How to write idiomatic, efficient RxJS code. An agent skill from sanity-io/sanity.
Jeffallan/claude-skills
Builds Angular 17+ features with standalone components, signals, NgRx state, RxJS patterns and lazy-loaded routing, then checks bundle size and test coverage.
kid-sid/claude-spellbook
A skill your agent uses when building or reviewing UI components for keyboard and screen reader compatibility, adding ARIA to custom widgets, auditing a page for WCAG AA conformance, or preparing…
kid-sid/claude-spellbook
A skill your agent uses when building, wiring, or debugging an Agentex agent — choosing agent type, configuring acp.py and manifest.yaml, using adk.messages or adk.state, or resolving…
kid-sid/claude-spellbook
A skill your agent uses when building production LLM applications — designing RAG pipelines, choosing vector databases, implementing agent orchestration, optimizing cost, or adding AI safety…
kid-sid/claude-spellbook
A skill your agent uses when designing new REST endpoints, reviewing an existing API contract, adding pagination or filtering, planning a versioning strategy, or building a public or partner-facing…
kid-sid/claude-spellbook
A skill your agent uses when implementing login flows, issuing or validating JWTs, setting up OAuth2/OIDC with a provider, designing role-based or attribute-based access control, securing API…
kid-sid/claude-spellbook
A skill your agent uses when writing Python code that integrates with Azure Blob Storage, AI Search, Document Intelligence, or Key Vault — or when configuring Managed Identity auth, designing a…
Categories
A skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…. Angular is an agent skill from kid-sid/claude-spellbook. Use when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change detection, or writing TestBed component tests.
Angular fits situations like: refactoring Angular applications — choosing between signals; configuring routing with guards and lazy loading; optimizing change detection; writing TestBed component tests.
Run `npx skills add kid-sid/claude-spellbook --skill angular -a claude-code`. Or copy the skill folder (skills/angular in kid-sid/claude-spellbook) into .claude/skills/angular in your project. Claude Code loads it when a task matches its description.
Run `npx skills add kid-sid/claude-spellbook --skill angular -a codex`. Or copy the skill folder (skills/angular in kid-sid/claude-spellbook) into .agents/skills/angular in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add kid-sid/claude-spellbook --skill angular -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/angular, .gemini/skills/angular, .github/skills/angular and .opencode/skills/angular in your project.
SKILL.md names no scripts, command-line tools or credentials: Angular is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Angular is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Angular: Angular (ericrisco/rsc-harness, 156 stars), Angular Clean Architecture (fmflurry/settings-opencode, 171 stars), Angular Best Practices (aiskillstore/marketplace, 430 stars) and Angular Component (aiskillstore/marketplace, 430 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
kid-sid (a GitHub user) maintains it in kid-sid/claude-spellbook, which has 189 GitHub stars. The repository holds 54 skills in this directory. The repository was last updated on August 5, 2026.
Source: kid-sid/claude-spellbook on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.