agent-directives 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/directives/adaptive-routing.md +22 -8
- package/dist/manifest.d.ts +1 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js.map +1 -1
- package/manifest.json +95 -7
- package/package.json +1 -1
- package/rules/angular/coding-style.md +176 -0
- package/rules/angular/components-and-templates.md +113 -15
- package/rules/angular/patterns.md +300 -0
- package/rules/angular/project-structure.md +115 -11
- package/rules/angular/security.md +159 -0
- package/rules/angular/testing.md +198 -15
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ dependencies between files.
|
|
|
13
13
|
| **Navigation** | 1 directive | SAFE pattern for exploring codebases before implementation |
|
|
14
14
|
| **Memory** | 2 directives | Error memory and session decisions for persistent learning |
|
|
15
15
|
| **Skills** | 13 skills | Code reviewer, test reviewer, spec reviewer, product requirements writer, implementation task planner, subagent-driven development, self-audit, systematic debugging, architecture boundary reviewer, codebase health reviewer, production readiness reviewer, harness hooks reviewer, and MCP integration reviewer |
|
|
16
|
-
| **Rules** |
|
|
16
|
+
| **Rules** | 6 Angular rules | Lazy-loaded Angular project structure, components/templates, coding style, application patterns, security, and testing standards — selected by stack or explicit category |
|
|
17
17
|
| **Templates** | 4 templates | Drop-in instruction files for AGENTS.md, CLAUDE.md, Copilot, and decision logs |
|
|
18
18
|
| **Tooling** | TypeScript scripts | Validate directive wiring, assemble eval scenarios, record loaded-file manifests, and generate eval health reports |
|
|
19
19
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adaptive-routing
|
|
3
3
|
description: Selects the lightest safe workflow path, relevant directives/skills, and handoff requirements based on task intent, risk, and touched surfaces.
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
required: true
|
|
6
6
|
category: workflow
|
|
7
7
|
tools:
|
|
@@ -138,13 +138,27 @@ work but do not replace directives or skills.
|
|
|
138
138
|
- Do not load unrelated framework rule packs. Project-local instructions override
|
|
139
139
|
rule-pack guidance when they conflict.
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
### Selecting rules by discovery
|
|
142
|
+
|
|
143
|
+
Do not maintain a per-rule routing table in this directive. Rule packs grow, and
|
|
144
|
+
this directive loads on every task; an inline catalog would put every rule pack's
|
|
145
|
+
metadata in always-loaded context. Discover matching rules on demand instead:
|
|
146
|
+
|
|
147
|
+
1. **Detect the stack** from project evidence. For example, `angular.json` or an
|
|
148
|
+
`@angular/core` dependency selects the `angular` pack.
|
|
149
|
+
2. **Open the rule index.** Each rule entry in `manifest.json` carries `category`
|
|
150
|
+
(the pack), `description` (what it governs), and `applies_to` (the file globs
|
|
151
|
+
it scopes to). The `rules/<pack>/` directory holds the same entries on disk;
|
|
152
|
+
read a rule's frontmatter for the authoritative scope.
|
|
153
|
+
3. **Match before loading.** Load a rule only when its `applies_to` globs match a
|
|
154
|
+
file you will touch *and* its `description` is relevant to what the task
|
|
155
|
+
actually changes. A glob match alone is not enough — editing a
|
|
156
|
+
`*.component.ts` file does not pull in the security rule unless the task
|
|
157
|
+
involves untrusted input, HTTP, secrets, or SSR.
|
|
158
|
+
4. **Skip absent packs.** Do not load a pack whose evidence is missing, and do
|
|
159
|
+
not load a whole pack by default.
|
|
160
|
+
|
|
161
|
+
The only pack today is `rules/angular/`.
|
|
148
162
|
|
|
149
163
|
---
|
|
150
164
|
|
package/dist/manifest.d.ts
CHANGED
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAGD,eAAO,MAAM,WAAW,QAAwB,CAAC;AAEjD,wBAAgB,YAAY,IAAI,QAAQ,CAMvC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG,aAAa,EAAE,CAQ5F;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEzF"}
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AA4BzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAEjD,MAAM,UAAU,YAAY;IAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,8BAA8B,YAAY,0CAA0C,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAa,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB,EAAE,IAAmB;IACzE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QACpE,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAClF,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAChE,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAwB,EAAE,EAAU;IAC5D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC"}
|
package/manifest.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"type": "directive",
|
|
7
7
|
"path": "directives/adaptive-routing.md",
|
|
8
8
|
"description": "Selects the lightest safe workflow path, relevant directives/skills, and handoff requirements based on task intent, risk, and touched surfaces.",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.6.0",
|
|
10
10
|
"required": true,
|
|
11
11
|
"category": "workflow",
|
|
12
12
|
"tools": [
|
|
@@ -383,12 +383,31 @@
|
|
|
383
383
|
"cursor"
|
|
384
384
|
]
|
|
385
385
|
},
|
|
386
|
+
{
|
|
387
|
+
"id": "angular-coding-style",
|
|
388
|
+
"type": "rule",
|
|
389
|
+
"path": "rules/angular/coding-style.md",
|
|
390
|
+
"description": "Concrete Angular coding-style patterns — signals, dependency injection with inject(), change detection, RxJS usage, and TypeScript strictness for Angular code.",
|
|
391
|
+
"version": "1.1.0",
|
|
392
|
+
"required": false,
|
|
393
|
+
"category": "angular",
|
|
394
|
+
"tools": [
|
|
395
|
+
"claude",
|
|
396
|
+
"copilot",
|
|
397
|
+
"codex",
|
|
398
|
+
"cursor"
|
|
399
|
+
],
|
|
400
|
+
"applies_to": [
|
|
401
|
+
"src/app/**/*.ts",
|
|
402
|
+
"src/**/*.ts"
|
|
403
|
+
]
|
|
404
|
+
},
|
|
386
405
|
{
|
|
387
406
|
"id": "angular-components-and-templates",
|
|
388
407
|
"type": "rule",
|
|
389
408
|
"path": "rules/angular/components-and-templates.md",
|
|
390
|
-
"description": "Angular
|
|
391
|
-
"version": "1.
|
|
409
|
+
"description": "Concrete patterns for building modern Angular components, signal-based inputs/outputs, OnPush change detection, and v17+ control-flow templates.",
|
|
410
|
+
"version": "1.2.0",
|
|
392
411
|
"required": false,
|
|
393
412
|
"category": "angular",
|
|
394
413
|
"tools": [
|
|
@@ -396,14 +415,45 @@
|
|
|
396
415
|
"copilot",
|
|
397
416
|
"codex",
|
|
398
417
|
"cursor"
|
|
418
|
+
],
|
|
419
|
+
"applies_to": [
|
|
420
|
+
"src/app/**/*.component.ts",
|
|
421
|
+
"src/app/**/*.component.html",
|
|
422
|
+
"src/app/**/*.component.css",
|
|
423
|
+
"src/app/**/*.component.scss"
|
|
424
|
+
]
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"id": "angular-patterns",
|
|
428
|
+
"type": "rule",
|
|
429
|
+
"path": "rules/angular/patterns.md",
|
|
430
|
+
"description": "Concrete Angular application patterns — smart/dumb component split, service-layer ownership, routing/guards/resolvers, HTTP interceptors, and reactive state with signals or RxJS.",
|
|
431
|
+
"version": "1.1.0",
|
|
432
|
+
"required": false,
|
|
433
|
+
"category": "angular",
|
|
434
|
+
"tools": [
|
|
435
|
+
"claude",
|
|
436
|
+
"copilot",
|
|
437
|
+
"codex",
|
|
438
|
+
"cursor"
|
|
439
|
+
],
|
|
440
|
+
"applies_to": [
|
|
441
|
+
"src/app/**/*.component.ts",
|
|
442
|
+
"src/app/**/*.component.html",
|
|
443
|
+
"src/app/**/*.service.ts",
|
|
444
|
+
"src/app/**/*.store.ts",
|
|
445
|
+
"src/app/**/*.routes.ts",
|
|
446
|
+
"src/app/**/*.guard.ts",
|
|
447
|
+
"src/app/**/*.resolver.ts",
|
|
448
|
+
"src/app/**/*.interceptor.ts"
|
|
399
449
|
]
|
|
400
450
|
},
|
|
401
451
|
{
|
|
402
452
|
"id": "angular-project-structure",
|
|
403
453
|
"type": "rule",
|
|
404
454
|
"path": "rules/angular/project-structure.md",
|
|
405
|
-
"description": "Angular workspace and
|
|
406
|
-
"version": "1.
|
|
455
|
+
"description": "Concrete Angular workspace, file-naming, feature-folder, and provider-bootstrapping standards for agents working in Angular applications.",
|
|
456
|
+
"version": "1.2.0",
|
|
407
457
|
"required": false,
|
|
408
458
|
"category": "angular",
|
|
409
459
|
"tools": [
|
|
@@ -411,14 +461,47 @@
|
|
|
411
461
|
"copilot",
|
|
412
462
|
"codex",
|
|
413
463
|
"cursor"
|
|
464
|
+
],
|
|
465
|
+
"applies_to": [
|
|
466
|
+
"angular.json",
|
|
467
|
+
"package.json",
|
|
468
|
+
"src/main.ts",
|
|
469
|
+
"src/app/**/*.ts",
|
|
470
|
+
"src/app/**/*.html"
|
|
471
|
+
]
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"id": "angular-security",
|
|
475
|
+
"type": "rule",
|
|
476
|
+
"path": "rules/angular/security.md",
|
|
477
|
+
"description": "Concrete Angular security rules — XSS prevention, HttpClient discipline, secret handling, route guards, and SSR safety.",
|
|
478
|
+
"version": "1.1.0",
|
|
479
|
+
"required": false,
|
|
480
|
+
"category": "angular",
|
|
481
|
+
"tools": [
|
|
482
|
+
"claude",
|
|
483
|
+
"copilot",
|
|
484
|
+
"codex",
|
|
485
|
+
"cursor"
|
|
486
|
+
],
|
|
487
|
+
"applies_to": [
|
|
488
|
+
"src/app/**/*.component.ts",
|
|
489
|
+
"src/app/**/*.component.html",
|
|
490
|
+
"src/app/**/*.service.ts",
|
|
491
|
+
"src/app/**/*.interceptor.ts",
|
|
492
|
+
"src/app/**/*.guard.ts",
|
|
493
|
+
"src/app/**/*.routes.ts",
|
|
494
|
+
"src/environments/**/*.ts",
|
|
495
|
+
"src/**/*.server.ts",
|
|
496
|
+
"server.ts"
|
|
414
497
|
]
|
|
415
498
|
},
|
|
416
499
|
{
|
|
417
500
|
"id": "angular-testing",
|
|
418
501
|
"type": "rule",
|
|
419
502
|
"path": "rules/angular/testing.md",
|
|
420
|
-
"description": "Angular testing
|
|
421
|
-
"version": "1.
|
|
503
|
+
"description": "Concrete Angular testing patterns — TestBed configuration, signal-input harnesses, router and HTTP testing utilities, and behavior-first test design.",
|
|
504
|
+
"version": "1.2.0",
|
|
422
505
|
"required": false,
|
|
423
506
|
"category": "angular",
|
|
424
507
|
"tools": [
|
|
@@ -426,6 +509,11 @@
|
|
|
426
509
|
"copilot",
|
|
427
510
|
"codex",
|
|
428
511
|
"cursor"
|
|
512
|
+
],
|
|
513
|
+
"applies_to": [
|
|
514
|
+
"src/app/**/*.spec.ts",
|
|
515
|
+
"src/app/**/*.test.ts",
|
|
516
|
+
"src/**/*.spec.ts"
|
|
429
517
|
]
|
|
430
518
|
}
|
|
431
519
|
]
|
package/package.json
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: angular-coding-style
|
|
3
|
+
description: Concrete Angular coding-style patterns — signals, dependency injection with inject(), change detection, RxJS usage, and TypeScript strictness for Angular code.
|
|
4
|
+
version: 1.1.0
|
|
5
|
+
required: false
|
|
6
|
+
category: angular
|
|
7
|
+
tools:
|
|
8
|
+
- claude
|
|
9
|
+
- copilot
|
|
10
|
+
- codex
|
|
11
|
+
- cursor
|
|
12
|
+
source_urls:
|
|
13
|
+
- https://angular.dev/style-guide
|
|
14
|
+
- https://angular.dev/guide/signals
|
|
15
|
+
- https://angular.dev/guide/di
|
|
16
|
+
- https://angular.dev/guide/components/dependency-injection
|
|
17
|
+
- https://rxjs.dev/guide/operators
|
|
18
|
+
- https://github.com/angular/angular/tree/main/skills/dev-skills/angular-developer/references
|
|
19
|
+
applies_to:
|
|
20
|
+
- src/app/**/*.ts
|
|
21
|
+
- src/**/*.ts
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Angular Coding Style Rules
|
|
25
|
+
|
|
26
|
+
**Load when:** Writing or reviewing Angular TypeScript — components, services, directives, pipes, guards, resolvers, interceptors, or any code that uses Angular's DI / signals / RxJS surface.
|
|
27
|
+
|
|
28
|
+
## Version Awareness
|
|
29
|
+
|
|
30
|
+
Many of the patterns below (`inject()`, signal primitives, `linkedSignal`, `resource`, functional guards/interceptors, `takeUntilDestroyed`) are version-gated. Check `package.json` / `ng version` and use the patterns the installed version supports — do not introduce a v18+ API into a v15 project.
|
|
31
|
+
|
|
32
|
+
## Sources
|
|
33
|
+
|
|
34
|
+
- Angular Style Guide — https://angular.dev/style-guide
|
|
35
|
+
- Angular Signals Guide — https://angular.dev/guide/signals
|
|
36
|
+
- Angular Dependency Injection Guide — https://angular.dev/guide/di
|
|
37
|
+
- RxJS Operators — https://rxjs.dev/guide/operators
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
40
|
+
|
|
41
|
+
### Dependency injection
|
|
42
|
+
|
|
43
|
+
Use `inject()` over constructor injection in components, services, guards, resolvers, interceptors, and pipes. Keep constructors empty (or remove them) so DI calls live at field declarations next to their use.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
@Injectable({ providedIn: 'root' })
|
|
47
|
+
export class UserService {
|
|
48
|
+
private http = inject(HttpClient);
|
|
49
|
+
private router = inject(Router);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Avoid on new code — verbose and harder to tree-shake/refactor
|
|
55
|
+
constructor(private http: HttpClient, private router: Router) {}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use `InjectionToken` for non-class dependencies (config, env, capability flags):
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
export const API_URL = new InjectionToken<string>('API_URL');
|
|
62
|
+
|
|
63
|
+
// In providers:
|
|
64
|
+
{ provide: API_URL, useValue: 'https://api.example.com' }
|
|
65
|
+
|
|
66
|
+
// At call site:
|
|
67
|
+
private apiUrl = inject(API_URL);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- Prefer `providedIn: 'root'` for app-wide singletons. Scope a service to a component/route only when its lifecycle must follow that subtree.
|
|
71
|
+
- `inject()` runs in an injection context. Calls outside a constructor/field initializer/factory need `runInInjectionContext` — do not stash `inject` calls inside arbitrary functions.
|
|
72
|
+
|
|
73
|
+
### Signals
|
|
74
|
+
|
|
75
|
+
Use signals for reactive state. Three primitives:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
count = signal(0); // writable
|
|
79
|
+
doubled = computed(() => this.count() * 2); // derived, read-only
|
|
80
|
+
selectedItem = linkedSignal(() => this.items()[0]); // writable derived: resets with source, also user-settable
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- Never duplicate derived data in a separate writable signal updated through `effect()` — use `computed` (or `linkedSignal` if it must also be writable).
|
|
84
|
+
- Bridge Observables to signals with `toSignal(stream$, { initialValue: ... })`. Do not assign Observable values into signals from manual `.subscribe()` callbacks.
|
|
85
|
+
- For async fetching backed by signals, prefer `resource({ request, loader })` on v19+ projects:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
userResource = resource({
|
|
89
|
+
request: () => ({ id: this.userId() }),
|
|
90
|
+
loader: ({ request }) => fetch(`/api/users/${request.id}`).then(r => r.json()),
|
|
91
|
+
});
|
|
92
|
+
// Access: userResource.value(), userResource.isLoading(), userResource.error(), userResource.reload()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `effect()` usage
|
|
96
|
+
|
|
97
|
+
Reserve `effect()` for genuine side effects that must react to signal changes — logging, third-party DOM libraries, telemetry. Never use `effect()` to keep two signals in sync; that is what `computed` / `linkedSignal` are for.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Correct — side effect that must react
|
|
101
|
+
effect(() => analytics.track('user_changed', { id: this.user().id }));
|
|
102
|
+
|
|
103
|
+
// Wrong — derived state, use computed
|
|
104
|
+
effect(() => this.fullName.set(`${this.first()} ${this.last()}`));
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For DOM work that must run after the view renders, use `afterRenderEffect` (or `afterNextRender` for one-shot work).
|
|
108
|
+
|
|
109
|
+
### Change detection
|
|
110
|
+
|
|
111
|
+
- Default to `ChangeDetectionStrategy.OnPush` on every new component.
|
|
112
|
+
- Drive view updates via signals or the `async` pipe. Avoid `ChangeDetectorRef.markForCheck()` / `detectChanges()` unless you have a narrow, documented reason (e.g., third-party callback outside the zone).
|
|
113
|
+
- Consider zoneless change detection (`provideExperimentalZonelessChangeDetection()` / `provideZonelessChangeDetection()` on supporting versions) only as a project-level decision, never as a side effect of a feature change.
|
|
114
|
+
|
|
115
|
+
### RxJS
|
|
116
|
+
|
|
117
|
+
When the project still uses RxJS for streams alongside (or instead of) signals:
|
|
118
|
+
|
|
119
|
+
| Operator | Use for |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `switchMap` | Search, navigation, latest-wins — cancels prior inner stream |
|
|
122
|
+
| `mergeMap` | Independent parallel inner streams |
|
|
123
|
+
| `exhaustMap` | Form submissions, idempotency — ignores new emissions until current completes |
|
|
124
|
+
| `concatMap` | Ordered queueing |
|
|
125
|
+
|
|
126
|
+
- Always handle errors with `catchError`. Never let a long-lived stream die silently and stop emitting.
|
|
127
|
+
- Use `takeUntilDestroyed(this.destroyRef)` for manual subscriptions in components/directives. Do not roll a manual `ngOnDestroy` + `Subject<void>` + `takeUntil` on new code.
|
|
128
|
+
- Prefer the `async` pipe in templates over manual subscribe + assign. If the data is needed in component logic too, expose it as a signal via `toSignal`.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
search$ = this.query$.pipe(
|
|
132
|
+
debounceTime(300),
|
|
133
|
+
distinctUntilChanged(),
|
|
134
|
+
switchMap(q => this.service.search(q).pipe(catchError(() => of([])))),
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### TypeScript strictness
|
|
139
|
+
|
|
140
|
+
- Run with `strict: true` and `strictTemplates: true` (Angular compiler option). Do not weaken either to make a change compile.
|
|
141
|
+
- Type every public surface: component inputs/outputs, service method signatures, route data, interceptor return types.
|
|
142
|
+
- Treat external data as `unknown` at the boundary. Narrow it with a validator (Zod, io-ts, hand-rolled type guard) before binding it to typed signals or components.
|
|
143
|
+
- Do not use `any`, `as any`, or non-null `!` to silence template type errors — fix the type or narrow the value.
|
|
144
|
+
|
|
145
|
+
### File-level style
|
|
146
|
+
|
|
147
|
+
- One artifact per file. Co-locate template (`.html`) and styles (`.scss`/`.css`) with the component class.
|
|
148
|
+
- Imports ordered: Angular core/common, third-party, project absolute (`@/...`), project relative (`./...`). Match the project's existing convention if it differs.
|
|
149
|
+
- Member order inside a class: signal fields → injected dependencies → inputs/outputs → other state → constructor (if any) → lifecycle hooks → public methods → private methods. Adjust to project style if it has a stronger local convention.
|
|
150
|
+
|
|
151
|
+
## Deep-Dive Reference Materials
|
|
152
|
+
|
|
153
|
+
Coding agents should fetch the raw text of these references programmatically when writing or modifying reactivity, dependency injection, and general code style:
|
|
154
|
+
|
|
155
|
+
- **Signals Overview:** Core signal concepts (`signal`, `computed`), reactive contexts, `untracked`, and async boundaries. Read [signals-overview.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/signals-overview.md)
|
|
156
|
+
- **Dependent State (`linkedSignal`):** Creating writable state linked to source signals. Read [linked-signal.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/linked-signal.md)
|
|
157
|
+
- **Async Reactivity (`resource`):** Fetching asynchronous data directly into signal state. Read [resource.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/resource.md)
|
|
158
|
+
- **Side Effects (`effect`):** Syncing signals to imperative APIs, cleanup, and `afterRenderEffect`. Read [effects.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/effects.md)
|
|
159
|
+
- **DI Fundamentals:** Dependency injection overview, services, and the `inject()` function. Read [di-fundamentals.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/di-fundamentals.md)
|
|
160
|
+
- **Injection Context:** Allowed execution contexts for `inject()` and `runInInjectionContext`. Read [injection-context.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/injection-context.md)
|
|
161
|
+
|
|
162
|
+
## Anti-patterns to refuse
|
|
163
|
+
|
|
164
|
+
- Constructor-injected dependencies on a project that has already adopted `inject()`.
|
|
165
|
+
- `effect()` used to synchronize signals.
|
|
166
|
+
- Manual `.subscribe()` in components without `takeUntilDestroyed`, or with a hand-rolled `destroy$` subject.
|
|
167
|
+
- `any` introduced to make typed templates compile, or `as any` casts on `HttpClient` responses.
|
|
168
|
+
- Calling `inject()` outside an injection context (e.g., inside an arbitrary helper function not invoked from a factory).
|
|
169
|
+
|
|
170
|
+
## Evidence
|
|
171
|
+
|
|
172
|
+
For style-aligned changes, run the project's lint and typecheck:
|
|
173
|
+
|
|
174
|
+
- `ng lint` (or the project's lint script) on touched files
|
|
175
|
+
- `tsc --noEmit` or `ng build` to verify typed templates and the public TypeScript surface
|
|
176
|
+
- Targeted tests covering the changed behavior (see `rules/angular/testing.md`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: angular-components-and-templates
|
|
3
|
-
description: Angular
|
|
4
|
-
version: 1.
|
|
3
|
+
description: Concrete patterns for building modern Angular components, signal-based inputs/outputs, OnPush change detection, and v17+ control-flow templates.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
required: false
|
|
6
6
|
category: angular
|
|
7
7
|
tools:
|
|
@@ -13,6 +13,10 @@ source_urls:
|
|
|
13
13
|
- https://angular.dev/style-guide
|
|
14
14
|
- https://angular.dev/guide/components
|
|
15
15
|
- https://angular.dev/guide/templates
|
|
16
|
+
- https://angular.dev/guide/signals
|
|
17
|
+
- https://angular.dev/guide/components/inputs
|
|
18
|
+
- https://angular.dev/guide/components/outputs
|
|
19
|
+
- https://github.com/angular/angular/tree/main/skills/dev-skills/angular-developer/references
|
|
16
20
|
applies_to:
|
|
17
21
|
- src/app/**/*.component.ts
|
|
18
22
|
- src/app/**/*.component.html
|
|
@@ -22,31 +26,125 @@ applies_to:
|
|
|
22
26
|
|
|
23
27
|
# Angular Components and Templates Rules
|
|
24
28
|
|
|
25
|
-
**Load when:** Creating, changing, or reviewing Angular components, templates, component styles, inputs
|
|
29
|
+
**Load when:** Creating, changing, or reviewing Angular components, templates, component styles, inputs/outputs, lifecycle behavior, or view logic.
|
|
26
30
|
|
|
27
|
-
##
|
|
31
|
+
## Version Awareness
|
|
32
|
+
|
|
33
|
+
Check the project's Angular version before writing code — APIs differ significantly between versions. Use `ng version` or inspect `package.json`. Match the project's existing component style first; do not migrate component styles as part of an unrelated change.
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
## Sources
|
|
30
36
|
|
|
31
37
|
- Angular Style Guide — https://angular.dev/style-guide
|
|
32
38
|
- Angular Components Guide — https://angular.dev/guide/components
|
|
33
39
|
- Angular Templates Guide — https://angular.dev/guide/templates
|
|
40
|
+
- Angular Signals Guide — https://angular.dev/guide/signals
|
|
34
41
|
|
|
35
42
|
## Rules
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
### Component shape
|
|
45
|
+
|
|
46
|
+
- Prefer standalone components (v17+ default). Add `standalone: true` and declare template dependencies in `imports`. Do not mix standalone and NgModule registration for the same component.
|
|
47
|
+
- Default to `ChangeDetectionStrategy.OnPush` on all new components.
|
|
48
|
+
- Keep components focused on presentation and UI coordination. Move data access, business rules, and cross-component state to services or state primitives the project already uses.
|
|
49
|
+
- One artifact per file — `user-card.component.ts`, `user-card.component.html`, `user-card.component.scss`, `user-card.component.spec.ts`.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
@Component({
|
|
53
|
+
selector: 'app-user-card',
|
|
54
|
+
standalone: true,
|
|
55
|
+
imports: [RouterLink, DatePipe],
|
|
56
|
+
templateUrl: './user-card.component.html',
|
|
57
|
+
styleUrl: './user-card.component.scss',
|
|
58
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
59
|
+
})
|
|
60
|
+
export class UserCardComponent {
|
|
61
|
+
user = input.required<User>();
|
|
62
|
+
highlight = input(false, { transform: booleanAttribute });
|
|
63
|
+
select = output<string>();
|
|
64
|
+
|
|
65
|
+
onClick() {
|
|
66
|
+
this.select.emit(this.user().id);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Inputs and outputs
|
|
72
|
+
|
|
73
|
+
- On v17.1+ projects, prefer signal-based `input()` / `input.required()` / `output()` over decorators. Stay on `@Input()`/`@Output()` only if the project is uniformly decorator-based and the file you are editing already uses them.
|
|
74
|
+
- Type every input and output. Do not introduce `any` to make a binding compile — narrow the source data at the boundary instead.
|
|
75
|
+
- Use `input.required<T>()` when a parent must supply the value. Provide defaults (`input(false)`) only when the absence has a meaningful, documented behavior.
|
|
76
|
+
- Two-way binding uses `model()` (e.g., `value = model<string>('')`). Do not invent custom `xChange` outputs when `model()` covers the use case.
|
|
77
|
+
|
|
78
|
+
### Templates (v17+ control flow)
|
|
79
|
+
|
|
80
|
+
Use the built-in block syntax. Always provide `track` in `@for`.
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
@if (isLoading()) {
|
|
84
|
+
<app-spinner />
|
|
85
|
+
} @else if (error()) {
|
|
86
|
+
<app-error [message]="error()" />
|
|
87
|
+
} @else {
|
|
88
|
+
@for (item of items(); track item.id) {
|
|
89
|
+
<app-item [item]="item" (select)="onSelect($event)" />
|
|
90
|
+
} @empty {
|
|
91
|
+
<p>No items found.</p>
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@switch (status()) {
|
|
96
|
+
@case ('active') { <app-active /> }
|
|
97
|
+
@case ('pending') { <app-pending /> }
|
|
98
|
+
@default { <app-unknown /> }
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- Keep templates declarative. Move complex branching, transformations, or side effects into the component class, a `computed`, or a pure pipe.
|
|
103
|
+
- Call signals as functions in the template (`items()`, not `items`). Do not access `.value` on a signal — that is not the public API.
|
|
104
|
+
- Use the `async` pipe for Observables in templates rather than subscribing manually. Prefer converting to signals via `toSignal()` when possible.
|
|
105
|
+
- Use `@defer` for non-critical UI blocks (below the fold, behind interaction). Pair with `@placeholder`, `@loading`, and `@error` blocks.
|
|
106
|
+
|
|
107
|
+
### Lifecycle and DOM access
|
|
108
|
+
|
|
109
|
+
- Use `inject()` over constructor injection. Keep constructors empty.
|
|
110
|
+
- For DOM work that must run after the view renders, prefer `afterRenderEffect` / `afterNextRender`. Do not reach into the DOM from `ngOnInit`.
|
|
111
|
+
- Use `viewChild()` / `viewChildren()` / `contentChild()` (signal queries) over the decorator forms on v17.2+ projects.
|
|
112
|
+
- Avoid `ngOnChanges` when an `input()` signal plus a `computed` or `effect` expresses the dependency directly.
|
|
113
|
+
|
|
114
|
+
### Styles
|
|
115
|
+
|
|
116
|
+
- Keep `ViewEncapsulation.Emulated` (default). Use `ViewEncapsulation.None` only for design-system roots that intentionally bleed styles.
|
|
117
|
+
- Scope styles to the component file. Use `:host`, `:host-context`, and CSS custom properties for themeable values rather than global selectors.
|
|
118
|
+
- Preserve existing accessibility patterns. New interactive elements need keyboard support, programmatic labels, focus management, and semantic HTML appropriate to the component.
|
|
119
|
+
|
|
120
|
+
### Change detection hygiene
|
|
121
|
+
|
|
122
|
+
- Signals and the `async` pipe drive change detection on `OnPush` components automatically. Do not reach for `ChangeDetectorRef.markForCheck()` or `detectChanges()` unless you have a documented, narrow reason.
|
|
123
|
+
- Do not mutate input objects in place when `OnPush` is in effect — produce new references.
|
|
124
|
+
|
|
125
|
+
## Deep-Dive Reference Materials
|
|
126
|
+
|
|
127
|
+
Coding agents should fetch the raw text of these references programmatically when editing related UI and components:
|
|
128
|
+
|
|
129
|
+
- **Component Fundamentals:** Standalone anatomy, template control flow (`@if`, `@for`). Read [components.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/components.md)
|
|
130
|
+
- **Inputs:** Signal-based inputs, transforms, and model inputs. Read [inputs.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/inputs.md)
|
|
131
|
+
- **Outputs:** Signal-based outputs and custom event best practices. Read [outputs.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/outputs.md)
|
|
132
|
+
- **Host Elements:** Host bindings and attribute injection. Read [host-elements.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/host-elements.md)
|
|
133
|
+
- **Component Styling:** Reusable component styles and encapsulation. Read [component-styling.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/component-styling.md)
|
|
134
|
+
- **Tailwind CSS:** Integrating and styling components with Tailwind. Read [tailwind-css.md](https://raw.githubusercontent.com/angular/angular/main/skills/dev-skills/angular-developer/references/tailwind-css.md)
|
|
135
|
+
|
|
136
|
+
## Anti-patterns to refuse
|
|
137
|
+
|
|
138
|
+
- `@Component({ standalone: false })` on new components without an NgModule that already owns it.
|
|
139
|
+
- `ChangeDetectionStrategy.Default` on new components.
|
|
140
|
+
- Using `*ngIf` / `*ngFor` / `*ngSwitch` on projects that have adopted block syntax.
|
|
141
|
+
- Calling `.subscribe()` from a template, or storing computed/derived values in plain signals updated via `effect()`.
|
|
142
|
+
- Typing inputs/outputs as `any`, or bypassing the type system with `!` to silence template errors.
|
|
45
143
|
|
|
46
144
|
## Evidence
|
|
47
145
|
|
|
48
146
|
For component behavior changes, include one of:
|
|
49
147
|
|
|
50
148
|
- updated component/unit tests,
|
|
51
|
-
- existing tests that cover the changed behavior,
|
|
52
|
-
- or a stated reason tests are unavailable plus a lower-confidence fallback such as build/typecheck
|
|
149
|
+
- existing tests that already cover the changed behavior,
|
|
150
|
+
- or a stated reason tests are unavailable plus a lower-confidence fallback such as build/typecheck output (`ng build`, `tsc --noEmit`).
|