@pavp/wavefront 1.2.1 → 1.2.2
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/agents/i18n-tokens.md +3 -2
- package/agents/module-builder.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/tester.md +7 -0
- package/package.json +1 -1
- package/skills/component-composition/SKILL.md +1 -1
- package/skills/design-system-inventory/SKILL.md +1 -1
- package/skills/i18n-next-intl/SKILL.md +24 -4
- package/skills/module-structure/SKILL.md +1 -1
package/agents/i18n-tokens.md
CHANGED
|
@@ -9,7 +9,8 @@ The localization + design-token enforcer for scaffold-nextjs-app. Sweeps a modul
|
|
|
9
9
|
|
|
10
10
|
## Operating rules
|
|
11
11
|
- Always: read the i18n + tokens skills first; replace visible literals with `t('namespace.key')`; namespace keys by module; replace hardcoded spacing/scale/color with `tokens.*`; swap raw `@mui/material` imports for `@/ui`; swap `next/link`/`next/navigation` for `@/i18n/routing`; keep Zod validation messages as i18n keys rendered through `t()`.
|
|
12
|
-
- Always:
|
|
12
|
+
- Always (new namespace): if you introduce a namespace that isn't already registered, **add its entry to the `messages` object in `src/i18n/request.ts`** (mirror the existing `common` fetch) — a namespace with no `request.ts` entry resolves to nothing. Prefer reusing `common` for app-wide strings; only add a namespace when the feature warrants one. See [[i18n-next-intl]].
|
|
13
|
+
- Always: run `yarn lint:fix` (eslint + stylelint auto-fix; fall back to `eslint . --fix && stylelint --fix` if no `lint:fix` script — never bare `eslint --fix <file>`), then `yarn lint` + `tsc --noEmit` to confirm clean after edits; verify no behavior change.
|
|
13
14
|
- Ask first: registering the actual translation values (messages are remote-fetched per `src/i18n/request.ts` — coordinate with the translation backend, don't invent a local messages file unless the project uses one); introducing a new token (prefer existing tokens; new tokens touch Style Dictionary).
|
|
14
15
|
- Never: leave user-facing copy as literals; hardcode spacing/color when a token exists; import raw `@mui/material` in feature code; use `next/link` for locale routes; change component logic while localizing.
|
|
15
16
|
|
|
@@ -28,7 +29,7 @@ The localization + design-token enforcer for scaffold-nextjs-app. Sweeps a modul
|
|
|
28
29
|
- Imports → `@/ui`, `@/i18n/routing`.
|
|
29
30
|
- Zod messages → ensure rendered via `t(errors.field?.message)`.
|
|
30
31
|
5. List the new translation keys introduced; flag that their values must be registered with the translation source.
|
|
31
|
-
6. Run `yarn lint` + `tsc --noEmit`; confirm clean. Hand off to reviewer.
|
|
32
|
+
6. Run `yarn lint:fix`, then `yarn lint` + `tsc --noEmit`; confirm clean. Hand off to reviewer.
|
|
32
33
|
|
|
33
34
|
## Examples
|
|
34
35
|
|
package/agents/module-builder.md
CHANGED
|
@@ -16,7 +16,7 @@ The implementer that owns the full Clean Architecture chain for a feature module
|
|
|
16
16
|
- Always (UI): before writing views/components, read `design-system-inventory` (catalog + continuity) and `responsive-layouts`; if a design-spec exists, implement it. With NO design, follow the continuity rule — mirror the closest existing screen's components/layout/spacing. Always implement the four states (loading/empty/error/populated). For the **error** state, every data-fetching view returns the error-boundary pattern (an error+retry component wired to the query's `refetch`) when its business hook reports `error` — never let a failed fetch render blank (see `error-boundary` skill). Never hand-roll raw `<div>`/`<input>` to match a visual — translate to `@/ui` + tokens.
|
|
17
17
|
- Always (responsive — mandatory): every view/component is mobile-first responsive using the theme's custom breakpoints (`minMobile`/`minTablet`/`minDesktop`/`largeScreen`, NOT xs/sm/md) via `sx` objects (`{ minMobile: ..., minTablet: ... }`) or `useResponsiveScreen` for branching. No fixed pixel widths that overflow mobile; the four states are responsive too.
|
|
18
18
|
- Always (composition — mandatory): decompose UI into focused subcomponents under `components/` (mirror todo: `*-form`, `*-item`, `*-list`, sections) — the view ORCHESTRATES + wires hooks, it does NOT hold all markup inline. Apply the `component-composition` "when to extract" decision rule: **anything rendered inside `.map()` is ALWAYS its own component** (a list extracts its item — never inline JSX in the map); also extract on distinct responsibility, reuse, own state/handlers, ~30–40+ line JSX blocks. Extraction is recursive — components have subcomponents too. Compose via children/props; for multi-part UI use the compound-component pattern. NO inheritance, NO boolean-prop explosion. Each component independently renderable + testable. Don't over-fragment trivial markup.
|
|
19
|
-
- Always: after generating, run `tsc --noEmit` (or `yarn typecheck`) and `yarn lint
|
|
19
|
+
- Always: after generating, run `yarn lint:fix` (the project's combined eslint + stylelint auto-fixer — covers CSS/SCSS too; fall back to `eslint . --fix && stylelint --fix` only if there is no `lint:fix` script). Never hand-fix file-by-file with bare `eslint --fix <file>`. Then run `tsc --noEmit` (or `yarn typecheck`) and `yarn lint` to confirm clean; fix any remaining issues; then hand off to `tester` and `reviewer`.
|
|
20
20
|
- Ask first: adding a new dependency; changing shared files (`@/api/endpoints`, `@/types/gateway.types`, theme/tokens); deviating from a documented pattern; generating a non-http gateway (localStorage/mock) when not requested.
|
|
21
21
|
- Always (modify mode): start from the mapper change-plan; touch ONLY files it lists as ADD/MODIFY; preserve existing public API unless the plan says to change it; keep edits minimal and local; run the module's existing tests after editing and keep them green (regression).
|
|
22
22
|
- Never: skip a layer (no View/component calling api or gateway directly — go through the repository); put side effects in render; use `any` at boundaries; hardcode URLs (use `endpoints`), spacing/color (use tokens), or user-facing strings (use next-intl); import raw `@mui/material` in feature code; call raw `create` from zustand (use `createStoreWithMiddleware`).
|
|
@@ -55,7 +55,7 @@ The implementer that owns the full Clean Architecture chain for a feature module
|
|
|
55
55
|
g. `views/[view]/` — `*.view.tsx` + `hooks/use-*-business` + `hooks/use-*-controller` + view `helpers/`.
|
|
56
56
|
h. `components/` — presentational components from `@/ui` + tokens.
|
|
57
57
|
i. `index.ts` — module public API barrel.
|
|
58
|
-
5. Verify: `tsc --noEmit` + `yarn lint
|
|
58
|
+
5. Verify: `yarn lint:fix` (eslint + stylelint auto-fix), then `tsc --noEmit` + `yarn lint` to confirm clean; self-check against `module-structure`.
|
|
59
59
|
6. Hand off: tell `tester` to add colocated tests and `reviewer` to validate.
|
|
60
60
|
|
|
61
61
|
## Examples
|
package/agents/reviewer.md
CHANGED
|
@@ -35,7 +35,7 @@ Convention and architecture guardian for scaffold-nextjs-app projects. Reads cod
|
|
|
35
35
|
- Imports: import-sort order; restricted-import rule (test bits from `@test/utils/test-utils`).
|
|
36
36
|
- UI/responsive: UI from `@/ui` (not raw `@mui/material`); spacing/color via tokens (no literals); strings via next-intl; **responsive present** — no fixed pixel widths that overflow mobile, breakpoint usage via theme keys (`minMobile`/`minTablet`/`minDesktop`, not xs/sm/md); the four states (loading/empty/error/populated) handled — for a data-fetching view the **error** state must render a recoverable error+retry UI (error-boundary pattern wired to `refetch`); BLOCKER if a failed query renders blank.
|
|
37
37
|
- Composition: UI decomposed into `components/` subcomponents (no monolithic view holding form+list+states inline); composition over inheritance; no boolean-prop explosion; each component independently testable. **Flag inline JSX inside `.map()`** — a mapped row/item must be its own component (e.g. list → item), not inline markup. Apply the "when to extract" rule (map / distinct responsibility / own state / ~30–40+ lines); also flag over-fragmentation of trivial markup.
|
|
38
|
-
- Test coverage (per layer)
|
|
38
|
+
- Test coverage (per layer) — **BLOCKER if incomplete:** enumerate the module's logic files and flag ANY without a colocated test — `*-api.ts` (`*-api.test.ts`), gateway, **`*.repository.queries.ts` AND `*.repository.mutations.ts` (each needs its own test)**, store, each selector, business+controller hooks, helpers, **EACH `components/*` subcomponent**, view. A partial test set (e.g. hooks tested but components/api/repository.queries missing) is a BLOCKER, not a nit. One happy-path test ≠ covered. Note missing unit vs integration.
|
|
39
39
|
4. Tooling pass: run `yarn lint` and `yarn typecheck` if present; capture real errors.
|
|
40
40
|
5. Report: grouped by BLOCKER / WARNING / NIT, each `path:line — problem — fix`. End with PASS/FAIL verdict + concise fix list. No edits.
|
|
41
41
|
|
package/agents/tester.md
CHANGED
|
@@ -31,6 +31,13 @@ Mirror the bundled reference (`.claude/reference/module/note-module.md` shows a
|
|
|
31
31
|
|
|
32
32
|
A module is NOT done when only the happy-path hook is tested. Missing-layer tests are a gap to fill, not optional.
|
|
33
33
|
|
|
34
|
+
## Completeness gate (BLOCKING — do this before handing off)
|
|
35
|
+
Coverage discipline is NOT a suggestion — verify it. After writing tests:
|
|
36
|
+
1. **Enumerate** the module's generated files with logic. `Glob`/`ls` the module dir for: `*-api.ts`, `gateways/**/*.ts`, `*.repository.queries.ts`, `*.repository.mutations.ts`, `*.store.ts`, `selectors/**/*.hook.ts`, `*-business.hook.ts`, `*-controller.hook.ts`, `*.helper.ts`, `components/**/*.component.tsx`, `*.view.tsx`.
|
|
37
|
+
2. **For EACH** of those, confirm a sibling `*.test.ts(x)` exists. Build the checklist explicitly — name every source file and whether its test is present.
|
|
38
|
+
3. **If ANY is missing, you are NOT done** — write the missing test(s) now. Do not hand off a partial test set. Common misses to double-check every time: **each `components/*` subcomponent**, **`*-api.test.ts`**, **`*.repository.queries.test.ts`** AND **`*.repository.mutations.test.ts`** (both, separately).
|
|
39
|
+
4. Only after every logic layer has its colocated test, run the suite and hand off.
|
|
40
|
+
|
|
34
41
|
## Operating rules
|
|
35
42
|
- Always: read the testing skill first; colocate tests next to source; import test helpers ONLY from `@test/utils` (RTL/react-dom direct import is a lint error); use faker factories from `@test/entities/[entity].mock.ts` (create one if missing); query by role/label before text/testid; wrap async assertions in `waitFor`; clear `jest.fn()` mocks in `beforeEach`.
|
|
36
43
|
- Always: cover EVERY logic layer (see Coverage discipline above), not just one hook; include edge/error cases + the four UI states, not only the happy path.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pavp/wavefront",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Claude Code frontend-engineering agent framework for scaffold-nextjs-app apps (Next.js/React/MUI/Clean Architecture). Installs agents, skills, and an orchestration loop into a project's .claude/.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"wavefront": "bin/wavefront.js"
|
|
@@ -20,7 +20,7 @@ src/modules/<m>/
|
|
|
20
20
|
│ └── index.ts
|
|
21
21
|
└── views/<m>-management/<m>-management.view.tsx # composes the above + hooks
|
|
22
22
|
```
|
|
23
|
-
Reference:
|
|
23
|
+
Reference: the bundled `.claude/reference/module/note-module.md` (§9 components — `note-form`, `note-item`, `note-list`, `error-boundary`) — each its own dir, `*.component.tsx`, `*.test.tsx`. The view wires them to the business/controller hooks. (Don't depend on the app keeping `src/modules/todo`.)
|
|
24
24
|
|
|
25
25
|
## When to extract a subcomponent (decision rule)
|
|
26
26
|
|
|
@@ -12,7 +12,7 @@ The continuity engine. Whatever the design source (or none), generated UI must l
|
|
|
12
12
|
## Step 0 — inventory the actual app (do this each run)
|
|
13
13
|
1. `@/ui` exports: read `src/ui/index.ts` → the available components (Button, TextField, Selector, Dialog, Modal, Card, Box, Typography, Container, Grid, List, Checkbox, RadioButton, Switch, Pagination, Toast, etc. — read the real list, it evolves).
|
|
14
14
|
2. Tokens: read `src/styles/tokens.ts` + subdirs → `tokens.spacing.scaleN`, `tokens.colors.*`, `tokens.typography.*`, `tokens.breakpoints.*`. Use these, never literals.
|
|
15
|
-
3. Composition patterns: read 1–2
|
|
15
|
+
3. Composition patterns: `Glob` `src/modules/*/views/**/*.view.tsx` and read 1–2 of whatever views the app ACTUALLY has → how they lay out forms, lists, cards; what wraps what; how loading/empty/error states render. Don't assume a specific module (`todo`/`about` may not exist — real projects delete the examples). If the app has no feature views yet, fall back to the bundled `.claude/reference/module/note-module.md` for the composition pattern.
|
|
16
16
|
|
|
17
17
|
## Component selection map (intent → @/ui)
|
|
18
18
|
Match design intent to the real component, never raw `@mui/material`:
|
|
@@ -31,11 +31,30 @@ import { Link, redirect, usePathname, useRouter } from '@/i18n/routing';
|
|
|
31
31
|
```
|
|
32
32
|
`routing` is built with `defineRouting({ locales, defaultLocale })` + `createNavigation`. Locales come from `config.translation`. Routes use the `[locale]` App Router segment.
|
|
33
33
|
|
|
34
|
-
## Messages are remote-fetched
|
|
34
|
+
## Messages are remote-fetched, and each namespace is registered in `request.ts`
|
|
35
35
|
|
|
36
|
-
`src/i18n/request.ts` (`getRequestConfig`) fetches messages from the settings API per locale
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
`src/i18n/request.ts` (`getRequestConfig`) fetches messages from the settings API per locale. There is **no local `messages/en.json`** by default — strings live server-side. Crucially, the `messages` object lists **one entry per namespace**, each its own fetch:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
return {
|
|
40
|
+
locale,
|
|
41
|
+
messages: {
|
|
42
|
+
common: await fetch(`${config.apiUrl}${endpoints.SETTINGS.TRANSLATION}/${locale}/common`, {
|
|
43
|
+
next: { revalidate },
|
|
44
|
+
} as RequestInit)
|
|
45
|
+
.then((res) => res.json())
|
|
46
|
+
.catch(() => ({})),
|
|
47
|
+
// each additional namespace needs its OWN entry, mirroring `common`:
|
|
48
|
+
// favorites: await fetch(`${config.apiUrl}${endpoints.SETTINGS.TRANSLATION}/${locale}/favorites`, …).then(...).catch(() => ({})),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**A new namespace does not exist until you add it here.** Using `useTranslations('favorites')` without a `favorites:` entry in `request.ts` yields missing messages. So when introducing a new feature namespace:
|
|
54
|
+
- **Edit `src/i18n/request.ts`** — add a `<namespace>: await fetch(\`${config.apiUrl}${endpoints.SETTINGS.TRANSLATION}/${locale}/<namespace>\`, { next: { revalidate } } as RequestInit).then((res) => res.json()).catch(() => ({}))` entry to `messages`, mirroring `common`.
|
|
55
|
+
- Reference keys in code (`t('feature.key')` with `useTranslations('feature')`, or fully-qualified from `common`).
|
|
56
|
+
- Register the actual values with the settings/translation backend (per `docs/intl.md`).
|
|
57
|
+
- (Reuse `common` instead of a new namespace when the strings are app-wide — only add a namespace when the feature warrants its own.)
|
|
39
58
|
|
|
40
59
|
## Validation messages = i18n keys
|
|
41
60
|
|
|
@@ -54,6 +73,7 @@ See [[forms-rhf-zod]].
|
|
|
54
73
|
## Rules
|
|
55
74
|
- Components stay copy-free: text via `t()`, links via `@/i18n/routing`.
|
|
56
75
|
- New keys: add usage in code + register the value with the translation source.
|
|
76
|
+
- **New namespace: add its `messages` entry in `src/i18n/request.ts` (mirror `common`) — without it the namespace resolves to nothing.**
|
|
57
77
|
|
|
58
78
|
## Related skills
|
|
59
79
|
[[forms-rhf-zod]] · [[mui-design-tokens]] · [[testing-jest-rtl]]
|
|
@@ -44,7 +44,7 @@ src/modules/[module-name]/
|
|
|
44
44
|
└── [module].types.ts # module types
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Canonical reference: the bundled `.claude/reference/module/note-module.md` shows the full module shape (stores, repositories, components, hooks, api, selectors, views) — mirror it. Do NOT rely on `src/modules/todo`/`auth` existing in the target app; real projects delete the example modules.
|
|
48
48
|
|
|
49
49
|
Nesting note: components, views, selectors, hooks each use a **one-dir-per-unit** pattern (`use-x-selector/use-x-selector.hook.ts`). Views and components may nest their own `hooks/` (business+controller) and `helpers/`. Tests colocate next to source as `*.test.ts(x)`.
|
|
50
50
|
|