@tungvivas/angular-vibe-kit 0.1.0 → 0.3.0
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 +165 -12
- package/agents/angular-a11y-auditor.md +71 -0
- package/agents/angular-build-fixer.md +37 -0
- package/agents/angular-debugger.md +38 -0
- package/agents/angular-git-workflow.md +46 -0
- package/agents/angular-onboarding.md +56 -0
- package/agents/angular-reviewer.md +22 -0
- package/agents/angular-test-writer.md +26 -0
- package/agents/angular-ui-designer.md +55 -0
- package/bin/cli.js +131 -5
- package/commands/dev-cycle.md +44 -12
- package/commands/init.md +27 -6
- package/commands/new-feature.md +23 -43
- package/commands/plan.md +136 -0
- package/commands/review-pr.md +12 -63
- package/commands/write-context.md +6 -0
- package/commands/write-tests.md +9 -107
- package/package.json +9 -3
- package/references/feature-structure.md +44 -0
- package/references/plan-spec.md +112 -0
- package/references/review-checklist.md +88 -0
- package/references/test-spec.md +92 -0
- package/skills/angular-practices/SKILL.md +34 -0
- package/skills/clarify-request/SKILL.md +100 -0
- package/skills/component-wrapper-priority/SKILL.md +38 -0
- package/skills/explain/SKILL.md +102 -0
- package/skills/explain/templates/vi.md +184 -0
- package/skills/git-commit/SKILL.md +107 -0
- package/skills/git-commit/references/conventions.md +100 -0
- package/templates/docs/DESIGN_SYSTEM.md +21 -0
- package/templates/docs/srs/README.md +29 -0
- package/templates/rules/project-rules.md +16 -0
package/commands/review-pr.md
CHANGED
|
@@ -1,63 +1,12 @@
|
|
|
1
|
-
Review the code changes
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- [ ] Smart / dumb separation: pages own data; presentational components only render + emit
|
|
14
|
-
- [ ] HTTP calls ONLY in services — never in components
|
|
15
|
-
- [ ] No business logic in templates
|
|
16
|
-
- [ ] Feature routes are lazy-loaded
|
|
17
|
-
|
|
18
|
-
## Type Safety
|
|
19
|
-
- [ ] No `any` — interfaces/types for every model and API response
|
|
20
|
-
- [ ] Responses typed with the project's response shape recorded in `project-rules.md` (e.g. an `ApiResponse<T>` envelope, a raw DTO, or a GraphQL type — whatever the backend actually returns)
|
|
21
|
-
- [ ] No `@ts-ignore` / `@ts-expect-error`
|
|
22
|
-
|
|
23
|
-
## Dependency Injection (follow `.claude/angular-practices/`)
|
|
24
|
-
- [ ] DI matches the profile: `inject()` (v16+) or constructor injection (v12–15) — consistent across the project
|
|
25
|
-
- [ ] Services `@Injectable({ providedIn: 'root' })`
|
|
26
|
-
|
|
27
|
-
## Reactive / RxJS / Signals (follow `.claude/angular-practices/`)
|
|
28
|
-
- [ ] Subscriptions torn down: `takeUntilDestroyed` (v16+) or `takeUntil(destroy$)` + `ngOnDestroy` (v12–15)
|
|
29
|
-
- [ ] `async` pipe preferred over manual subscribe
|
|
30
|
-
- [ ] No `toPromise()` — use `firstValueFrom()` if needed
|
|
31
|
-
- [ ] Services return `Observable<T>` — do NOT subscribe internally
|
|
32
|
-
- [ ] Signals used per profile: stable for state (v16+); derived state via `computed()`. `resource`/`httpResource` are experimental (v19+) — only if the team opted in
|
|
33
|
-
|
|
34
|
-
## Error Handling
|
|
35
|
-
- [ ] Loading / error / empty states handled in list/detail pages
|
|
36
|
-
- [ ] HTTP errors surfaced to user (not swallowed)
|
|
37
|
-
- [ ] Auth errors (e.g. 401) handled centrally by an interceptor — not per-feature
|
|
38
|
-
|
|
39
|
-
## Security
|
|
40
|
-
- [ ] Auth handled per the project's model in `project-rules.md` (token in memory / httpOnly cookie / session / OAuth) — and consistent with it. If tokens are used, they are NOT in localStorage
|
|
41
|
-
- [ ] No hardcoded backend URL — uses `environment`
|
|
42
|
-
- [ ] Routes needing auth are guarded
|
|
43
|
-
- [ ] No unsanitized HTML (`innerHTML` / `bypassSecurityTrust*`) without a clear, reviewed reason
|
|
44
|
-
|
|
45
|
-
## Performance
|
|
46
|
-
- [ ] `ChangeDetectionStrategy.OnPush` on every component (or zoneless on v20+ if enabled)
|
|
47
|
-
- [ ] List tracking present: `@for` with `track` (v17+) or `*ngFor` with `trackBy` (v12–16)
|
|
48
|
-
|
|
49
|
-
## Code Quality
|
|
50
|
-
- [ ] Component file < ~300 lines; method < ~50 lines
|
|
51
|
-
- [ ] No dead code, no `console.log`
|
|
52
|
-
- [ ] Naming follows `.claude/rules/project-rules.md`
|
|
53
|
-
|
|
54
|
-
## Documentation
|
|
55
|
-
- [ ] New feature: `CONTEXT.md` created inside feature folder
|
|
56
|
-
- [ ] Logic changed: `CONTEXT.md` Refactor Log updated (append only)
|
|
57
|
-
- [ ] Endpoint changed: `docs/API_CONTRACT.md` updated
|
|
58
|
-
- [ ] `docs/PROJECT-STATUS.md` updated
|
|
59
|
-
|
|
60
|
-
## Summary
|
|
61
|
-
1. **Blockers** 🔴 — must fix before merge
|
|
62
|
-
2. **Suggestions** 🟡 — improve but not blocking
|
|
63
|
-
3. **Good parts** 🟢 — what was done well
|
|
1
|
+
Review the current code changes against this project's Angular standards.
|
|
2
|
+
|
|
3
|
+
1. Get the diff: run `git diff` (unstaged) and `git diff --staged`, or `git diff <base>...HEAD` if a
|
|
4
|
+
base branch is named. Review only the changed code.
|
|
5
|
+
2. Apply the full checklist in `.claude/references/review-checklist.md` — it defines the review
|
|
6
|
+
dimensions, the Precedence rule (a valid project convention wins; don't flag legacy), and the
|
|
7
|
+
🔴/🟡/🟢 output format. That file is the single source of truth for what "correct" means here.
|
|
8
|
+
3. Output the report **inline** in this conversation using the format from the checklist. If there are
|
|
9
|
+
no blockers, say so explicitly.
|
|
10
|
+
|
|
11
|
+
> For a large diff, or to review in the background while you keep coding, dispatch the
|
|
12
|
+
> `angular-reviewer` agent instead — it applies the same checklist in an isolated context.
|
|
@@ -45,6 +45,12 @@ Note what is local component state vs shared state.
|
|
|
45
45
|
- **{Decision}**: {reason}
|
|
46
46
|
- **{Decision}**: {reason}
|
|
47
47
|
|
|
48
|
+
## Component Wrapping Decisions
|
|
49
|
+
- **Wrapped components used**: list the `<app-*>` components used by this feature (e.g. `<app-select>`, `<app-data-table>`)
|
|
50
|
+
- **Library components used directly**: list any direct library imports and the reason (e.g. `p-tree` used directly because no wrapper exists yet, and creating one is out of scope for this feature)
|
|
51
|
+
- **New wrappers introduced**: if this feature adds a new wrapper to `shared/components/`, list it here and link to the PR/ADR
|
|
52
|
+
- **Wrappers NOT used (with reason)**: if a wrapper exists for the need but you chose not to use it, document why (e.g. "wrapper doesn't support X yet — track in #123")
|
|
53
|
+
|
|
48
54
|
## Considered and Rejected
|
|
49
55
|
- **{Option}**: rejected because {reason}
|
|
50
56
|
|
package/commands/write-tests.md
CHANGED
|
@@ -1,110 +1,12 @@
|
|
|
1
|
-
Write tests for the specified feature module.
|
|
1
|
+
Write tests for the specified feature module.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Follow `.claude/references/test-spec.md` — it is the single source of truth for how this project writes
|
|
4
|
+
tests: read-first, service unit tests (TestBed + `HttpTestingController`), component tests, E2E for
|
|
5
|
+
critical flows only, and the verify step (coverage ≥ 80%, no real backend). It detects the project's
|
|
6
|
+
runner (Jest or Karma/Jasmine) and version idioms from `.claude/angular-practices/`.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- Read the module's `CONTEXT.md` (if it exists) to understand trade-offs and edge cases
|
|
10
|
-
- Read `docs/API_CONTRACT.md` for expected request/response shapes
|
|
11
|
-
- Identify all code paths: happy path, error cases, empty states, validation
|
|
8
|
+
Write the tests **inline** in this conversation, then run them and report the result (files created,
|
|
9
|
+
what each covers, pass/fail + coverage).
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Use `HttpClientTestingModule` + `HttpTestingController` (works on every version, v12+).
|
|
16
|
-
On v15+ you may instead use the functional `provideHttpClient()` + `provideHttpClientTesting()`.
|
|
17
|
-
Follow whichever style the project already uses (see `.claude/angular-practices/`).
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
describe('{Feature}Service', () => {
|
|
21
|
-
let service: {Feature}Service;
|
|
22
|
-
let httpMock: HttpTestingController;
|
|
23
|
-
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
TestBed.configureTestingModule({
|
|
26
|
-
imports: [HttpClientTestingModule],
|
|
27
|
-
providers: [{Feature}Service],
|
|
28
|
-
});
|
|
29
|
-
service = TestBed.inject({Feature}Service);
|
|
30
|
-
httpMock = TestBed.inject(HttpTestingController);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterEach(() => httpMock.verify());
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Required Coverage Per Method
|
|
38
|
-
**getAll:**
|
|
39
|
-
- ✅ Success — returns list; assert request URL + method `GET`
|
|
40
|
-
- ✅ Empty — returns empty array, not null
|
|
41
|
-
- ✅ Server error — propagates error (500)
|
|
42
|
-
|
|
43
|
-
**getById:**
|
|
44
|
-
- ✅ Found — returns correct response; assert URL contains the id
|
|
45
|
-
- ✅ Not found — 404 error propagated
|
|
46
|
-
|
|
47
|
-
**create:**
|
|
48
|
-
- ✅ Success — assert `POST` + request body matches payload
|
|
49
|
-
- ✅ Validation error — 400
|
|
50
|
-
- ✅ Conflict — 409
|
|
51
|
-
|
|
52
|
-
**update:**
|
|
53
|
-
- ✅ Success — assert `PUT`/`PATCH` + body
|
|
54
|
-
- ✅ Not found — 404
|
|
55
|
-
|
|
56
|
-
**delete:**
|
|
57
|
-
- ✅ Success — assert `DELETE` + URL
|
|
58
|
-
- ✅ Not found — 404
|
|
59
|
-
|
|
60
|
-
### Unit Test Rules
|
|
61
|
-
- Use `httpMock.expectOne(url)` to assert and flush each request
|
|
62
|
-
- Test ONE behavior per test
|
|
63
|
-
- Naming: `methodName_scenario_expectedResult`
|
|
64
|
-
- Describe behavior in the test title, not the implementation
|
|
65
|
-
- `httpMock.verify()` in `afterEach` — no unexpected requests
|
|
66
|
-
- Never hit a real backend
|
|
67
|
-
|
|
68
|
-
## Step 3: Component Tests — `{feature}-list.component.spec.ts`
|
|
69
|
-
|
|
70
|
-
Mock the service with `jasmine.createSpyObj` (Karma) or `jest.fn()` (Jest).
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
describe('{Feature}ListComponent', () => {
|
|
74
|
-
let fixture: ComponentFixture<{Feature}ListComponent>;
|
|
75
|
-
let serviceSpy: jasmine.SpyObj<{Feature}Service>;
|
|
76
|
-
|
|
77
|
-
beforeEach(() => {
|
|
78
|
-
serviceSpy = jasmine.createSpyObj('{Feature}Service', ['getAll', 'delete']);
|
|
79
|
-
TestBed.configureTestingModule({
|
|
80
|
-
imports: [{Feature}ListComponent],
|
|
81
|
-
providers: [{ provide: {Feature}Service, useValue: serviceSpy }],
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Required Coverage
|
|
88
|
-
- ✅ Render — list shows correct data after load
|
|
89
|
-
- ✅ Loading — spinner visible while the request is pending
|
|
90
|
-
- ✅ Error — error message visible when the service errors
|
|
91
|
-
- ✅ Empty — empty-state message when no data
|
|
92
|
-
- ✅ Interaction — clicking delete calls `service.delete` with the right id
|
|
93
|
-
- ✅ Signals (v16+) — assert signal values update as expected
|
|
94
|
-
|
|
95
|
-
### Component Test Rules
|
|
96
|
-
- Mock the service — never the real HttpClient
|
|
97
|
-
- Drive change detection with `fixture.detectChanges()`
|
|
98
|
-
- Query the DOM via `fixture.nativeElement` / `DebugElement`
|
|
99
|
-
- Each test is independent — no shared mutable state
|
|
100
|
-
|
|
101
|
-
## Step 4: E2E (only for critical flows)
|
|
102
|
-
For login and the main CRUD flow, add a Playwright spec under the project's e2e folder.
|
|
103
|
-
Skip E2E for trivial pages — manual testing covers those.
|
|
104
|
-
|
|
105
|
-
## Step 5: Verify
|
|
106
|
-
- All tests pass: `ng test` / `jest`
|
|
107
|
-
- No test depends on execution order
|
|
108
|
-
- No unit test hits a real backend
|
|
109
|
-
- Test names clearly describe WHAT is tested and the EXPECTED result
|
|
110
|
-
- Coverage: every public service method has at least happy path + error case
|
|
11
|
+
> For a large feature, or to write tests in the background while you keep coding, dispatch the
|
|
12
|
+
> `angular-test-writer` agent instead — it follows the same spec in an isolated context.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tungvivas/angular-vibe-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Bootstrap a Vibe Coding workflow (CLAUDE.md, project docs, slash-commands) into any Angular project. Detects your Angular version and installs version-matched best-practice rules.",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Bootstrap a Vibe Coding workflow (CLAUDE.md, project docs, slash-commands, skills, agents) into any Angular project. Detects your Angular version and installs version-matched best-practice rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
7
7
|
"claude",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"ai",
|
|
10
10
|
"scaffold",
|
|
11
11
|
"vibe-coding",
|
|
12
|
-
"slash-commands"
|
|
12
|
+
"slash-commands",
|
|
13
|
+
"skills",
|
|
14
|
+
"agents",
|
|
15
|
+
"subagents"
|
|
13
16
|
],
|
|
14
17
|
"license": "MIT",
|
|
15
18
|
"type": "module",
|
|
@@ -17,9 +20,12 @@
|
|
|
17
20
|
"angular-vibe-kit": "bin/cli.js"
|
|
18
21
|
},
|
|
19
22
|
"files": [
|
|
23
|
+
"agents/",
|
|
20
24
|
"bin/",
|
|
21
25
|
"commands/",
|
|
22
26
|
"practices/",
|
|
27
|
+
"references/",
|
|
28
|
+
"skills/",
|
|
23
29
|
"templates/",
|
|
24
30
|
"README.md",
|
|
25
31
|
"LICENSE"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Feature Structure & Coding Rules (shared source of truth)
|
|
2
|
+
|
|
3
|
+
> Read by BOTH `/new-feature` (linear command) and `/dev-cycle` (gated orchestrator) when scaffolding
|
|
4
|
+
> and implementing a new feature module. Keep the folder shape, build order, and coding rules HERE
|
|
5
|
+
> only — the two commands differ in pacing (linear vs gated), not in what "correct" looks like.
|
|
6
|
+
|
|
7
|
+
## Folder structure
|
|
8
|
+
> **Use the project's actual layout and folder names from `.claude/rules/project-rules.md`.**
|
|
9
|
+
> The tree below is only the DEFAULT shape — if the project calls things differently
|
|
10
|
+
> (e.g. `containers/` instead of `pages/`, `ui/` or `common/` instead of `shared/`,
|
|
11
|
+
> an Nx `libs/` layout, or NgModule-based folders), follow the project, not this tree.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
{features-dir}/{feature-name}/ # e.g. features/, modules/, or an Nx lib
|
|
15
|
+
├── {feature}.routes.ts
|
|
16
|
+
├── models/
|
|
17
|
+
│ └── {feature}.model.ts # Interfaces mirroring the backend response shape
|
|
18
|
+
├── services/
|
|
19
|
+
│ └── {feature}.service.ts # All data-access calls
|
|
20
|
+
├── pages/ # Smart (container) components — project may call these containers/
|
|
21
|
+
│ ├── {feature}-list/
|
|
22
|
+
│ │ ├── {feature}-list.component.ts
|
|
23
|
+
│ │ ├── {feature}-list.component.html
|
|
24
|
+
│ │ └── {feature}-list.component.scss
|
|
25
|
+
│ └── {feature}-detail/
|
|
26
|
+
└── components/ # Dumb (presentational) components
|
|
27
|
+
└── {feature}-form/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Implementation order
|
|
31
|
+
1. **Model** — interfaces for entity, request/response, mirroring the backend's actual DTOs / response shape (per `project-rules.md` — envelope, raw DTO, or GraphQL type)
|
|
32
|
+
2. **Service** — data-access methods returning `Observable<T>` typed with the project's response shape (HttpClient, or the project's client if it uses Apollo/another)
|
|
33
|
+
3. **Routes** — lazy-loaded, guard-protected where needed
|
|
34
|
+
4. **Page components (smart)** — data, loading/error/empty states
|
|
35
|
+
5. **Presentational components (dumb)** — inputs/outputs only, no data access
|
|
36
|
+
|
|
37
|
+
## Coding rules (from `.claude/rules/project-rules.md` + `.claude/angular-practices/`)
|
|
38
|
+
- Data access ONLY in services — never in components
|
|
39
|
+
- `ChangeDetectionStrategy.OnPush` on every component
|
|
40
|
+
- Tear down subscriptions per profile: `takeUntilDestroyed` (v16+) or `takeUntil(destroy$)` + `ngOnDestroy` (v12–15)
|
|
41
|
+
- No `any` — interfaces/types for every model and response
|
|
42
|
+
- Use the project's forms approach from `project-rules.md` (Reactive Forms by default; Formly / template-driven if that's what the project uses)
|
|
43
|
+
- No hardcoded backend URL — use `environment`
|
|
44
|
+
- **Wrapper priority**: import from `@shared/components/<name>` (or the project's wrapper folder) if a wrapper exists; never import the library component directly. See `docs/DESIGN_SYSTEM.md` → Wrapped Components.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Angular Plan Spec (shared source of truth)
|
|
2
|
+
|
|
3
|
+
> Read by `/plan` (writes the plan) and by `/dev-cycle` + `/new-feature` (consume the plan).
|
|
4
|
+
> Keep the plan format HERE only — the commands are thin wrappers around this file.
|
|
5
|
+
> Adapted from [obra/superpowers](https://github.com/obra/superpowers) `writing-plans` (MIT).
|
|
6
|
+
|
|
7
|
+
A plan is written for an engineer with **zero context for this codebase**: every task names exact
|
|
8
|
+
files, exact types, and exact signatures. If a step changes code, the step shows the code.
|
|
9
|
+
|
|
10
|
+
## Where plans live
|
|
11
|
+
|
|
12
|
+
`docs/plans/YYYY-MM-DD-<feature-name>.md` — one plan per feature. Create `docs/plans/` if it
|
|
13
|
+
doesn't exist yet.
|
|
14
|
+
|
|
15
|
+
## Plan document header (required)
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
# <Feature Name> — Implementation Plan
|
|
19
|
+
|
|
20
|
+
**Goal:** <one sentence: what this builds and for whom>
|
|
21
|
+
|
|
22
|
+
**Architecture:** <2-3 sentences: approach, where it sits in the app, key data flow>
|
|
23
|
+
|
|
24
|
+
**Approach chosen:** <the approach the user approved in /plan Phase C, one line, with the
|
|
25
|
+
main alternative that was rejected and why>
|
|
26
|
+
|
|
27
|
+
## Global Constraints
|
|
28
|
+
|
|
29
|
+
Do NOT restate project rules here — point at the sources every task implicitly obeys:
|
|
30
|
+
|
|
31
|
+
- Precedence + naming + Coexistence: `.claude/rules/project-rules.md`
|
|
32
|
+
- Version idioms (DI, control flow, Signals/RxJS): `.claude/angular-practices/<version>.md`
|
|
33
|
+
- Folder shape, implementation order, coding rules: `.claude/references/feature-structure.md`
|
|
34
|
+
- Wrapper priority: `docs/DESIGN_SYSTEM.md` → Wrapped Components table
|
|
35
|
+
- Endpoints + payload shapes: `docs/API_CONTRACT.md`
|
|
36
|
+
|
|
37
|
+
<Then list ONLY feature-specific constraints the user stated, one line each, verbatim —
|
|
38
|
+
e.g. "must work offline", "max 1 request per keystroke", "reuse OrderService pagination".>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## File map (required, before any task)
|
|
42
|
+
|
|
43
|
+
Lock decomposition in before writing tasks. Use the **project's actual layout** from
|
|
44
|
+
`project-rules.md` (e.g. `containers/` not `pages/`, Nx `libs/`) — never the default tree blindly.
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
## File Map
|
|
48
|
+
|
|
49
|
+
| Action | Path | Responsibility |
|
|
50
|
+
|--------|------|----------------|
|
|
51
|
+
| Create | src/app/features/<feature>/models/<feature>.model.ts | DTOs mirroring API_CONTRACT |
|
|
52
|
+
| Create | src/app/features/<feature>/services/<feature>.service.ts | Data access only |
|
|
53
|
+
| Modify | src/app/app.routes.ts | Register lazy route |
|
|
54
|
+
| Test | src/app/features/<feature>/services/<feature>.service.spec.ts | Per test-spec.md |
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
One clear responsibility per file. Files that change together live together.
|
|
58
|
+
|
|
59
|
+
## Task structure
|
|
60
|
+
|
|
61
|
+
Order tasks by the Implementation Order in `feature-structure.md`:
|
|
62
|
+
**Model → Service → Routes → Page components → Presentational components → Tests**
|
|
63
|
+
(tests follow `.claude/references/test-spec.md`; this kit does not use red/green TDD ordering).
|
|
64
|
+
|
|
65
|
+
Each task uses this shape:
|
|
66
|
+
|
|
67
|
+
````markdown
|
|
68
|
+
### Task N: <name>
|
|
69
|
+
|
|
70
|
+
**Files:**
|
|
71
|
+
- Create: `exact/path/file.ts`
|
|
72
|
+
- Modify: `exact/path/existing.ts` (what changes)
|
|
73
|
+
- Test: `exact/path/file.spec.ts`
|
|
74
|
+
|
|
75
|
+
**Interfaces:**
|
|
76
|
+
- Consumes: <what this task uses from earlier tasks — exact type names and signatures>
|
|
77
|
+
- Produces: <what later tasks rely on — exact function names, parameter and return types.
|
|
78
|
+
A task's implementer may see only their own task; this block is how they learn the
|
|
79
|
+
names and types neighboring tasks use.>
|
|
80
|
+
|
|
81
|
+
**Steps:**
|
|
82
|
+
- [ ] Step 1: <one action, 2-10 minutes, with the actual code block if it changes code>
|
|
83
|
+
- [ ] Step 2: <verify — exact command and expected output, e.g. `npx tsc --noEmit` → clean>
|
|
84
|
+
````
|
|
85
|
+
|
|
86
|
+
Task sizing: the smallest unit that is independently verifiable (compiles / tests pass on its
|
|
87
|
+
own). Fold setup, config, and docs steps into the task whose deliverable needs them.
|
|
88
|
+
|
|
89
|
+
## No placeholders (plan failures — never write these)
|
|
90
|
+
|
|
91
|
+
- "TBD", "TODO", "implement later", "fill in details"
|
|
92
|
+
- "Add appropriate error handling" / "add validation" / "handle edge cases" (show the handling)
|
|
93
|
+
- "Write tests for the above" without naming the cases (list them per test-spec.md coverage)
|
|
94
|
+
- "Similar to Task N" — repeat the code; tasks may be read out of order
|
|
95
|
+
- A step that describes what to do without showing how (code steps require code blocks)
|
|
96
|
+
- References to types, functions, or methods not defined in any task and not existing in the codebase
|
|
97
|
+
|
|
98
|
+
## Self-review (run after writing, fix inline — no re-review needed)
|
|
99
|
+
|
|
100
|
+
1. **Requirement coverage** — for each requirement the user confirmed in `/plan`, point to the
|
|
101
|
+
task that implements it. A requirement with no task = add the task.
|
|
102
|
+
2. **Placeholder scan** — search the plan for every pattern in the list above.
|
|
103
|
+
3. **Type consistency** — types, signatures, and property names used in later tasks match what
|
|
104
|
+
earlier tasks define, AND match the shapes in `docs/API_CONTRACT.md`.
|
|
105
|
+
4. **Wrapper check** — every UI element in the plan uses the wrapper from the Wrapped
|
|
106
|
+
Components table when one exists (a raw library import where a wrapper exists is a plan bug).
|
|
107
|
+
5. **Layout check** — every path in the File Map matches the project's real folder layout from
|
|
108
|
+
`project-rules.md`.
|
|
109
|
+
|
|
110
|
+
## Output
|
|
111
|
+
|
|
112
|
+
Report the plan file path, the number of tasks, and the self-review result (what was fixed).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Angular Review Checklist (shared source of truth)
|
|
2
|
+
|
|
3
|
+
> Read by BOTH `/review-pr` (command, runs inline) and the `angular-reviewer` agent (isolated context).
|
|
4
|
+
> Keep review logic HERE only — the command and the agent are thin wrappers around this file.
|
|
5
|
+
|
|
6
|
+
## Load the project's standards first
|
|
7
|
+
- `.claude/angular-practices/*.md` — the version-matched idioms (DI, control flow, RxJS/Signals, tests).
|
|
8
|
+
- `.claude/rules/project-rules.md` — naming, folder layout, Precedence, Coexistence.
|
|
9
|
+
- `docs/DESIGN_SYSTEM.md` → Wrapped Components — the project's shared UI wrappers.
|
|
10
|
+
|
|
11
|
+
## Precedence (do not over-flag)
|
|
12
|
+
A valid project convention WINS over the best-practice profile. Flag a profile deviation as a blocker
|
|
13
|
+
only when it's an **objective defect in NEW code**. Do NOT flag legacy modules listed in the Coexistence
|
|
14
|
+
Strategy — they are kept as-is. Do NOT flag a valid project choice (naming, structure, state library,
|
|
15
|
+
response shape) just because it differs from the profile. When unsure if something is a defect or a
|
|
16
|
+
valid choice, say so rather than blocking.
|
|
17
|
+
|
|
18
|
+
## Review dimensions
|
|
19
|
+
|
|
20
|
+
### 1. Architecture
|
|
21
|
+
- [ ] Smart / dumb separation: pages own data; presentational components only render + emit
|
|
22
|
+
- [ ] HTTP calls ONLY in services — never in components
|
|
23
|
+
- [ ] No business logic in templates
|
|
24
|
+
- [ ] Feature routes are lazy-loaded
|
|
25
|
+
|
|
26
|
+
### 2. Type Safety
|
|
27
|
+
- [ ] No `any` — interfaces/types for every model and API response
|
|
28
|
+
- [ ] Responses typed with the project's response shape recorded in `project-rules.md` (envelope, raw DTO, GraphQL type — whatever the backend returns)
|
|
29
|
+
- [ ] No `@ts-ignore` / `@ts-expect-error`
|
|
30
|
+
|
|
31
|
+
### 3. Dependency Injection (per `.claude/angular-practices/`)
|
|
32
|
+
- [ ] DI matches the profile: `inject()` (v16+) or constructor injection (v12–15) — consistent across the project
|
|
33
|
+
- [ ] Services `@Injectable({ providedIn: 'root' })` unless intentionally scoped
|
|
34
|
+
|
|
35
|
+
### 4. Reactive / RxJS / Signals (per `.claude/angular-practices/`)
|
|
36
|
+
- [ ] Subscriptions torn down: `takeUntilDestroyed` (v16+) or `takeUntil(destroy$)` + `ngOnDestroy` (v12–15)
|
|
37
|
+
- [ ] `async` pipe preferred over manual subscribe
|
|
38
|
+
- [ ] No `toPromise()` — use `firstValueFrom()` if needed
|
|
39
|
+
- [ ] Services return `Observable<T>` — do NOT subscribe internally
|
|
40
|
+
- [ ] Signals used per profile: stable for state (v16+); derived state via `computed()`. `resource`/`httpResource` are experimental (v19+) — only if the team opted in
|
|
41
|
+
|
|
42
|
+
### 5. Error Handling
|
|
43
|
+
- [ ] Loading / error / empty states handled in list & detail pages
|
|
44
|
+
- [ ] HTTP errors surfaced to the user (not swallowed)
|
|
45
|
+
- [ ] Auth errors (e.g. 401) handled centrally by an interceptor — not per-feature
|
|
46
|
+
|
|
47
|
+
### 6. Security
|
|
48
|
+
- [ ] Auth handled per the project's model in `project-rules.md` (token in memory / httpOnly cookie / session / OAuth) and consistent with it. If tokens are used, they are NOT in localStorage
|
|
49
|
+
- [ ] No hardcoded backend URL — uses `environment`
|
|
50
|
+
- [ ] Routes needing auth are guarded
|
|
51
|
+
- [ ] No unsanitized HTML (`innerHTML` / `bypassSecurityTrust*`) without a clear, reviewed reason
|
|
52
|
+
|
|
53
|
+
### 7. Performance
|
|
54
|
+
- [ ] `ChangeDetectionStrategy.OnPush` on every component (or zoneless on v20+ if enabled)
|
|
55
|
+
- [ ] List tracking present: `@for` with `track` (v17+) or `*ngFor` with `trackBy` (v12–16)
|
|
56
|
+
|
|
57
|
+
### 8. Code Quality
|
|
58
|
+
- [ ] Component file < ~300 lines; method < ~50 lines
|
|
59
|
+
- [ ] No dead code, no `console.log`
|
|
60
|
+
- [ ] Naming follows `.claude/rules/project-rules.md`
|
|
61
|
+
|
|
62
|
+
### 9. Component Wrapping (per `docs/DESIGN_SYSTEM.md` → Wrapped Components)
|
|
63
|
+
- [ ] No feature imports a raw UI-library component (`p-dropdown`, `mat-form-field`, `nz-input`, etc.) when a wrapper exists in `shared/components/`
|
|
64
|
+
- [ ] UI-primitive imports go through the project's wrapper (import path matches the wrapper's location)
|
|
65
|
+
- [ ] Wrapper exists but the PR uses the library directly → 🔴 BLOCKER
|
|
66
|
+
- [ ] No wrapper exists and the library is used directly → 🟡 SUGGESTION: "Consider a wrapper in `shared/components/`"
|
|
67
|
+
- [ ] New components do NOT silently re-implement logic an existing wrapper already provides
|
|
68
|
+
- [ ] A brand-new wrapper follows the Wrapper Reference Example from `project-rules.md`
|
|
69
|
+
|
|
70
|
+
### 10. Documentation
|
|
71
|
+
- [ ] New feature: `CONTEXT.md` created inside the feature folder
|
|
72
|
+
- [ ] Logic changed: `CONTEXT.md` Refactor Log updated (append only)
|
|
73
|
+
- [ ] Endpoint changed: `docs/API_CONTRACT.md` updated
|
|
74
|
+
- [ ] `docs/PROJECT-STATUS.md` updated
|
|
75
|
+
|
|
76
|
+
## Output format
|
|
77
|
+
```
|
|
78
|
+
🔴 Blockers — must fix before merge
|
|
79
|
+
- <file:line> — <issue> → <fix>
|
|
80
|
+
🟡 Suggestions — improve but not blocking
|
|
81
|
+
- <file:line> — <issue>
|
|
82
|
+
💭 Nits — optional polish (naming, minor style not handled by the linter)
|
|
83
|
+
- <file:line> — <note>
|
|
84
|
+
🟢 Good — clean patterns / clever solutions worth calling out
|
|
85
|
+
- <note>
|
|
86
|
+
```
|
|
87
|
+
If there are no blockers, say so explicitly. Cite `file:line`. Explain the "why" behind each finding
|
|
88
|
+
and suggest rather than demand.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Angular Test Spec (shared source of truth)
|
|
2
|
+
|
|
3
|
+
> Read by BOTH `/write-tests` (command, runs inline) and the `angular-test-writer` agent (isolated).
|
|
4
|
+
> Keep test logic HERE only — the command and the agent are thin wrappers around this file.
|
|
5
|
+
|
|
6
|
+
## Load the project's standards first
|
|
7
|
+
- `.claude/angular-practices/*.md` → the **Testing** section (version's `TestBed` idioms).
|
|
8
|
+
- `.claude/rules/project-rules.md` + an existing spec (the Reference Example) — mirror its style.
|
|
9
|
+
- Detect the runner from `package.json` / `angular.json` / `jest.config` / `karma.conf`
|
|
10
|
+
(Jest or Karma/Jasmine). Use what the project already uses — never introduce a new runner.
|
|
11
|
+
|
|
12
|
+
## Step 1: Read before writing
|
|
13
|
+
- Read the feature's source: model, service, page components, presentational components, guards, pipes, resolvers.
|
|
14
|
+
- Read the feature's `CONTEXT.md` (if present) for trade-offs and edge cases.
|
|
15
|
+
- Read `docs/API_CONTRACT.md` for expected request/response shapes.
|
|
16
|
+
- Identify every code path: happy path, not-found, validation failure, loading/error/empty states, branches.
|
|
17
|
+
|
|
18
|
+
## Step 2: Service unit tests — `{feature}.service.spec.ts`
|
|
19
|
+
Use `HttpClientTestingModule` + `HttpTestingController` (works v12+). On v15+ you may use the functional
|
|
20
|
+
`provideHttpClient()` + `provideHttpClientTesting()` — follow whichever style the project already uses.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
describe('{Feature}Service', () => {
|
|
24
|
+
let service: {Feature}Service;
|
|
25
|
+
let httpMock: HttpTestingController;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
TestBed.configureTestingModule({
|
|
29
|
+
imports: [HttpClientTestingModule],
|
|
30
|
+
providers: [{Feature}Service],
|
|
31
|
+
});
|
|
32
|
+
service = TestBed.inject({Feature}Service);
|
|
33
|
+
httpMock = TestBed.inject(HttpTestingController);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => httpMock.verify());
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Required coverage per method
|
|
41
|
+
- **getAll**: success (returns list; assert URL + `GET`) · empty (empty array, not null) · server error (500 propagates)
|
|
42
|
+
- **getById**: found (assert URL contains id) · not found (404 propagates)
|
|
43
|
+
- **create**: success (assert `POST` + body matches payload) · validation error (400) · conflict (409)
|
|
44
|
+
- **update**: success (assert `PUT`/`PATCH` + body) · not found (404)
|
|
45
|
+
- **delete**: success (assert `DELETE` + URL) · not found (404)
|
|
46
|
+
|
|
47
|
+
### Service test rules
|
|
48
|
+
- Use `httpMock.expectOne(url)` to assert and flush each request.
|
|
49
|
+
- One behavior per test. Naming: `methodName_scenario_expectedResult`.
|
|
50
|
+
- `httpMock.verify()` in `afterEach` — no unexpected requests. Never hit a real backend.
|
|
51
|
+
|
|
52
|
+
## Step 3: Component tests — `{feature}-list.component.spec.ts`
|
|
53
|
+
Mock the service with `jasmine.createSpyObj` (Karma) or `jest.fn()` (Jest).
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
describe('{Feature}ListComponent', () => {
|
|
57
|
+
let fixture: ComponentFixture<{Feature}ListComponent>;
|
|
58
|
+
let serviceSpy: jasmine.SpyObj<{Feature}Service>;
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
serviceSpy = jasmine.createSpyObj('{Feature}Service', ['getAll', 'delete']);
|
|
62
|
+
TestBed.configureTestingModule({
|
|
63
|
+
imports: [{Feature}ListComponent],
|
|
64
|
+
providers: [{ provide: {Feature}Service, useValue: serviceSpy }],
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Required coverage
|
|
71
|
+
- Render (list shows correct data after load) · Loading (spinner while pending) · Error (message on service error)
|
|
72
|
+
· Empty (empty-state message) · Interaction (clicking delete calls `service.delete` with the right id)
|
|
73
|
+
· Signals (v16+): assert signal values update as expected.
|
|
74
|
+
|
|
75
|
+
### Component test rules
|
|
76
|
+
- Mock the service — never the real HttpClient.
|
|
77
|
+
- Drive change detection with `fixture.detectChanges()`; query DOM via `fixture.nativeElement` / `DebugElement`.
|
|
78
|
+
- Each test independent — no shared mutable state.
|
|
79
|
+
|
|
80
|
+
## Step 4: E2E (only for critical flows)
|
|
81
|
+
For login and the main CRUD flow, add a spec under the project's e2e folder using its configured E2E
|
|
82
|
+
tool (e.g. Playwright). Skip E2E for trivial pages — manual testing covers those.
|
|
83
|
+
|
|
84
|
+
## Step 5: Verify before reporting
|
|
85
|
+
- All new tests pass (`ng test --watch=false` / `npm test` / `jest`, or target the new spec).
|
|
86
|
+
- No test depends on execution order; no unit/component test hits a real backend.
|
|
87
|
+
- Test names describe behavior and the expected result.
|
|
88
|
+
- Coverage ≥ 80% for the feature (`ng test --code-coverage` / `jest --coverage` if configured);
|
|
89
|
+
every public service method has at least happy-path + error case.
|
|
90
|
+
|
|
91
|
+
## Output
|
|
92
|
+
List the spec files created, what each covers, and the final test-run result (pass/fail + coverage).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: angular-practices
|
|
3
|
+
description: Use when writing, reviewing, or refactoring any Angular / TypeScript code in this project — components, dependency injection, templates, services, HTTP, routing, RxJS/Signals, forms, and testing. Provides the version-matched Angular {{NG_PROFILE_LABEL}} idioms for this codebase.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Angular Practices (this project)
|
|
7
|
+
|
|
8
|
+
When you write or review Angular code in this project, follow the version-matched
|
|
9
|
+
best-practice profile that the installer selected for this codebase.
|
|
10
|
+
|
|
11
|
+
**→ Read `.claude/angular-practices/{{PRACTICE_FILE}}` and apply it.**
|
|
12
|
+
|
|
13
|
+
That file is the source of truth for HOW to write idiomatic Angular {{NG_PROFILE_LABEL}}:
|
|
14
|
+
component style (standalone vs NgModule), DI (`inject()` vs constructor), template syntax
|
|
15
|
+
(`@if/@for` vs `*ngIf/*ngFor`), thin components with a smart/dumb split, HTTP in services
|
|
16
|
+
(not components), typed reactive forms, RxJS teardown (`takeUntilDestroyed`) or Signals for
|
|
17
|
+
local state, `OnPush` change detection, and `TestBed` slice tests.
|
|
18
|
+
|
|
19
|
+
## Precedence — which rule wins (read this first)
|
|
20
|
+
|
|
21
|
+
The profile is **not** absolute. Follow this order (see `.claude/rules/project-rules.md` → Precedence):
|
|
22
|
+
|
|
23
|
+
1. **This project's conventions** (the codebase + `.claude/rules/project-rules.md`) — HIGHEST.
|
|
24
|
+
If the project does something consistently and it works, match it — even if it differs
|
|
25
|
+
from the profile (folder layout, naming, state-management library, API/response shape, auth).
|
|
26
|
+
2. **This profile** — applies only where the project has NO convention, or where existing
|
|
27
|
+
code is objectively below standard. In that case the standard applies to **NEW code only**.
|
|
28
|
+
3. **Legacy modules** (Coexistence Strategy in project-rules.md) — never refactored.
|
|
29
|
+
|
|
30
|
+
## Before importing a raw UI-library component
|
|
31
|
+
|
|
32
|
+
Check whether a shared wrapper already exists for the need — see the
|
|
33
|
+
**component-wrapper-priority** skill and `docs/DESIGN_SYSTEM.md` → Wrapped Components table.
|
|
34
|
+
Never bypass an existing wrapper with a raw library import.
|