@pavp/wavefront 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/agents/i18n-tokens.md +58 -0
  4. package/agents/mapper.md +64 -0
  5. package/agents/module-builder.md +90 -0
  6. package/agents/reviewer.md +65 -0
  7. package/agents/tester.md +84 -0
  8. package/bin/wavefront.js +91 -0
  9. package/commands/wavefront-change.md +26 -0
  10. package/commands/wavefront-execute.md +28 -0
  11. package/commands/wavefront-feature.md +25 -0
  12. package/commands/wavefront-fix.md +28 -0
  13. package/commands/wavefront-init.md +27 -0
  14. package/commands/wavefront-plan.md +30 -0
  15. package/commands/wavefront-ship.md +26 -0
  16. package/commands/wavefront-state.md +23 -0
  17. package/commands/wavefront-verify.md +24 -0
  18. package/hooks/lint-after-edit.sh +43 -0
  19. package/hooks/typecheck-on-stop.sh +27 -0
  20. package/install.sh +83 -0
  21. package/package.json +67 -0
  22. package/planning-templates/PHASE_PLAN.md +37 -0
  23. package/planning-templates/PROJECT.md +18 -0
  24. package/planning-templates/REQUIREMENTS.md +27 -0
  25. package/planning-templates/ROADMAP.md +12 -0
  26. package/planning-templates/STATE.md +21 -0
  27. package/settings.template.json +29 -0
  28. package/skills/clean-architecture/SKILL.md +46 -0
  29. package/skills/component-composition/SKILL.md +67 -0
  30. package/skills/component-composition/examples/compound-component.md +62 -0
  31. package/skills/data-fetching-react-query/SKILL.md +68 -0
  32. package/skills/design-intake/SKILL.md +69 -0
  33. package/skills/design-system-inventory/SKILL.md +38 -0
  34. package/skills/forms-rhf-zod/SKILL.md +75 -0
  35. package/skills/gateway-pattern/SKILL.md +79 -0
  36. package/skills/hook-patterns/SKILL.md +74 -0
  37. package/skills/i18n-next-intl/SKILL.md +59 -0
  38. package/skills/module-structure/SKILL.md +98 -0
  39. package/skills/mui-design-tokens/SKILL.md +60 -0
  40. package/skills/naming-conventions/SKILL.md +65 -0
  41. package/skills/repository-pattern/SKILL.md +128 -0
  42. package/skills/requirement-intake/SKILL.md +65 -0
  43. package/skills/responsive-layouts/SKILL.md +64 -0
  44. package/skills/selector-pattern/SKILL.md +59 -0
  45. package/skills/store-zustand/SKILL.md +63 -0
  46. package/skills/sync-audit/SKILL.md +52 -0
  47. package/skills/testing-jest-rtl/SKILL.md +83 -0
  48. package/uninstall.sh +36 -0
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: i18n-next-intl
3
+ description: next-intl usage in scaffold-nextjs-app — useTranslations, locale routing via @/i18n/routing, remote-fetched messages, validation keys. Read before adding user-facing strings.
4
+ source: scaffold-nextjs-app/docs/intl.md + src/i18n/request.ts, src/i18n/routing.ts
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # i18n: next-intl
9
+
10
+ All user-facing strings go through next-intl. No hardcoded copy in components.
11
+
12
+ ## Translating strings
13
+
14
+ ```tsx
15
+ 'use client';
16
+ import { useTranslations } from 'next-intl';
17
+
18
+ export const Greeting = () => {
19
+ const t = useTranslations(); // or useTranslations('namespace')
20
+ return <Typography>{t('home.title')}</Typography>;
21
+ };
22
+ ```
23
+ - Client components: `useTranslations()` hook.
24
+ - Server components: `getTranslations()` from `next-intl/server`.
25
+ - Keys are dot-paths (`home.title`, `validation.email.invalid`).
26
+
27
+ ## Locale-aware navigation (use @/i18n/routing, NOT next/link)
28
+
29
+ ```ts
30
+ import { Link, redirect, usePathname, useRouter } from '@/i18n/routing';
31
+ ```
32
+ `routing` is built with `defineRouting({ locales, defaultLocale })` + `createNavigation`. Locales come from `config.translation`. Routes use the `[locale]` App Router segment.
33
+
34
+ ## Messages are remote-fetched
35
+
36
+ `src/i18n/request.ts` (`getRequestConfig`) fetches messages from the settings API per locale into the `common` namespace (with `revalidate`). There is **no local `messages/en.json`** to edit by default — translation strings live server-side. When adding a key:
37
+ - Reference it in code (`t('feature.key')`).
38
+ - Coordinate the actual translation value with the settings/translation backend (per `docs/intl.md`), not a local file — unless the project has been changed to local messages.
39
+
40
+ ## Validation messages = i18n keys
41
+
42
+ Zod schemas return translation keys; render with `t()`:
43
+ ```ts
44
+ z.string().email('validation.email.invalid')
45
+ // in form: errorMessage={errors.email?.message && t(errors.email.message)}
46
+ ```
47
+ See [[forms-rhf-zod]].
48
+
49
+ ## Conventions
50
+ - Namespace keys by feature/module (`todo.*`, `auth.*`, `validation.*`).
51
+ - Never hardcode visible text or error strings.
52
+ - In tests, next-intl is mocked (`test/__mocks__/next-intl`) — `t('key')` typically returns the key; assert accordingly.
53
+
54
+ ## Rules
55
+ - Components stay copy-free: text via `t()`, links via `@/i18n/routing`.
56
+ - New keys: add usage in code + register the value with the translation source.
57
+
58
+ ## Related skills
59
+ [[forms-rhf-zod]] · [[mui-design-tokens]] · [[testing-jest-rtl]]
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: module-structure
3
+ description: Exact folder/file shape of a feature module and the src/ tree in scaffold-nextjs-app. Read before scaffolding or placing any file.
4
+ source: scaffold-nextjs-app/docs/project-structure.md
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Module structure
9
+
10
+ ## Module shape (every feature module)
11
+
12
+ ```
13
+ src/modules/[module-name]/
14
+ ├── api/ # HTTP integration + Zod validation
15
+ ├── components/ # module-specific UI
16
+ ├── repositories/
17
+ │ └── [entity]/
18
+ │ ├── gateways/
19
+ │ │ ├── http-gateway/
20
+ │ │ ├── local-storage-gateway/ # kebab (NOT localStorage-gateway)
21
+ │ │ │ └── helpers/[name]/[name].helper.ts
22
+ │ │ ├── index.ts
23
+ │ │ └── [entity].gateway.types.ts
24
+ │ ├── helpers/[name]/[name].helper.ts # pure business utils, one dir each
25
+ │ ├── index.ts
26
+ │ ├── [entity].query-options.ts
27
+ │ ├── [entity].repository.keys.ts # React Query key factory
28
+ │ ├── [entity].repository.types.ts
29
+ │ ├── [entity].repository.queries.ts
30
+ │ └── [entity].repository.mutations.ts
31
+ ├── selectors/[name]-selector/[name]-selector.hook.ts # one dir per selector
32
+ ├── stores/
33
+ │ ├── [module].store.ts
34
+ │ ├── [module].store.actions.ts
35
+ │ └── [module].store.types.ts
36
+ ├── views/
37
+ │ └── [view]/
38
+ │ ├── [view].view.tsx
39
+ │ ├── hooks/use-[view]-business/use-[view]-business.hook.ts
40
+ │ ├── hooks/use-[view]-controller/use-[view]-controller.hook.ts
41
+ │ └── helpers/[name]/[name].helper.ts # view-scoped helpers
42
+ ├── hooks/[name]/[name].hook.ts # shared module hooks
43
+ ├── index.ts # public API (barrel)
44
+ └── [module].types.ts # module types
45
+ ```
46
+
47
+ Reference modules in scaffold: `src/modules/todo` (full: stores, repositories, components, hooks, api, selectors, views), `src/modules/auth` (stores, repositories, api, selectors, views). Mirror their shape.
48
+
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
+
51
+ ## Surrounding src/ tree (do not put module code here)
52
+
53
+ ```
54
+ src/
55
+ ├── api/ # SHARED api config: endpoints.ts, http-client.ts, types.ts
56
+ ├── app/ # Next.js App Router; [locale]/ i18n routes; layout.tsx
57
+ ├── components/ # project-wide reusable components (not design-system-generic)
58
+ ├── core/ # cross-project shared: components, hooks, helpers, layouts,
59
+ │ # lib/ (react-query/, zustand/)
60
+ ├── hooks/ # app-wide custom hooks
61
+ ├── i18n/ # internationalization config
62
+ ├── modules/ # ← feature modules live here
63
+ ├── navigation/ # routing helpers
64
+ ├── shared/ # api/, gateways/ (base gateway types), types/
65
+ ├── styles/ # design tokens + styling
66
+ ├── types/ # global TS types
67
+ └── ui/ # design system components (Button, Modal, Form, ...)
68
+ test/ # utils/ (provider wrappers), entities/ (faker factories), __mocks__/
69
+ ```
70
+
71
+ ## Import aliases (tsconfig paths)
72
+
73
+ ```
74
+ @/* → ./src/*
75
+ @/components/* → ./src/components/*
76
+ @/modules/* → ./src/modules/*
77
+ @/core/* → ./src/core/*
78
+ @/shared/* → ./src/shared/*
79
+ @/ui/* → ./src/ui/*
80
+ @test/* → ./test/*
81
+ ```
82
+
83
+ Always use absolute imports. Examples:
84
+ ```ts
85
+ import { Button } from '@/ui';
86
+ import { todoRepository } from '@/modules/todo';
87
+ import type { Todo } from '@/modules/todo';
88
+ import { renderWithProviders, createMockTodo } from '@test/utils';
89
+ ```
90
+
91
+ ## Placement rules
92
+
93
+ - Module-only code → inside the module. Cross-module reusable → `core/` (no business logic) or `components/`.
94
+ - Generic design-system UI → `ui/`. Base gateway types → `shared/gateways/`.
95
+ - Each dir exports via `index.ts` barrel; module's `index.ts` is its only public API.
96
+
97
+ ## Related skills
98
+ [[clean-architecture]] · [[naming-conventions]] · [[repository-pattern]] · [[gateway-pattern]]
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: mui-design-tokens
3
+ description: Build UI from the @/ui design system + @/styles/tokens (Style Dictionary), never raw @mui/material or hardcoded values, in scaffold-nextjs-app.
4
+ source: scaffold-nextjs-app/docs/design-tokens.md, project-structure.md + src/modules/todo/components
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # MUI + design tokens
9
+
10
+ Views layer. Components are built from the project design system, not MUI directly, and spacing/scale come from tokens, not magic numbers.
11
+
12
+ ## Import from `@/ui`, not `@mui/material`
13
+
14
+ ```tsx
15
+ import { Box, Button, Card, CardContent, Selector, TextField, Typography } from '@/ui'; // ✅
16
+ // import { Button } from '@mui/material'; // ❌
17
+ ```
18
+ `@/ui` exposes wrapped/themed design-system components (Button, Modal, TextField, Selector, Dialog, etc.). MUI icons (`@mui/icons-material`) are imported directly. Layout primitives (`Box`, `Typography`, `Card`) also come from `@/ui`.
19
+
20
+ ## Tokens for spacing/scale, not literals
21
+
22
+ ```tsx
23
+ import tokens from '@/styles/tokens';
24
+
25
+ <Card sx={{ mb: tokens.spacing.scale6 }}>
26
+ <Box sx={{ gap: tokens.spacing.scale4 }} display="flex" />
27
+ <Box sx={{ mt: tokens.spacing.scale4 }} />
28
+ </Card>
29
+ ```
30
+ - Use `tokens.spacing.scaleN` (and other token groups) instead of hardcoded `8`, `'16px'`, etc.
31
+ - Tokens are generated by Style Dictionary (`yarn build:dictionary`) into `@/styles/tokens`.
32
+ - One-off layout values that are genuinely not design-system concerns (e.g. a fixed control height) may stay inline, but prefer tokens for spacing/color/typography.
33
+
34
+ ## Component conventions
35
+
36
+ ```tsx
37
+ 'use client';
38
+ import { memo } from 'react';
39
+
40
+ interface TodoFormProps { isCreating: boolean; onSubmit: (todo: CreateTodoRequest) => void; }
41
+
42
+ export const TodoForm = memo(({ isCreating, onSubmit }: TodoFormProps) => { /* ... */ });
43
+ TodoForm.displayName = 'TodoForm';
44
+ ```
45
+ - Functional components only. Explicit `Props` interface. Named export.
46
+ - `memo` for pure presentational components; set `displayName` when memo-wrapped.
47
+ - `'use client'` when using state/handlers.
48
+ - No `any` in props. No render-time side effects.
49
+
50
+ ## Theme breakpoints (Container maxWidth)
51
+
52
+ The theme augments MUI breakpoints with custom names. `Container maxWidth` accepts the THEME names (e.g. `"largeScreen"`), NOT MUI defaults (`"sm"`/`"md"` → type error). Match existing usage (`about-view` uses `maxWidth="largeScreen"`) or omit `maxWidth`.
53
+
54
+ ## Rules
55
+ - UI from `@/ui`; icons from `@mui/icons-material`; never raw `@mui/material` in feature code.
56
+ - Spacing/scale/color via `@/styles/tokens`; avoid hardcoded values.
57
+ - i18n strings via next-intl, not literals — see [[i18n-next-intl]].
58
+
59
+ ## Related skills
60
+ [[responsive-layouts]] · [[design-system-inventory]] · [[i18n-next-intl]] · [[naming-conventions]] · [[hook-patterns]]
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: naming-conventions
3
+ description: File/folder naming + suffixes + code casing enforced by eslint-plugin-check-file in scaffold-nextjs-app. Read before creating or renaming any file.
4
+ source: scaffold-nextjs-app/docs/file-naming-conventions.md, docs/rules-conventions.md, eslint.config.mjs
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Naming conventions
9
+
10
+ ESLint-enforced (`eslint-plugin-check-file`). Violations = lint error, not style preference.
11
+
12
+ ## Enforced by lint (hard)
13
+
14
+ - **File names:** `KEBAB_CASE` for all `*.{js,jsx,ts,tsx}` (`ignoreMiddleExtensions: true` → middle ext like `.component` allowed).
15
+ - **Folders in `src/`:** `NEXT_JS_APP_ROUTER_CASE` (kebab, plus App Router segments like `[locale]`, `(group)`).
16
+ - **Folders in `test/`:** `KEBAB_CASE`.
17
+ - **`__mocks__/`:** exempt from filename rule.
18
+
19
+ ## File suffixes (descriptive type tags)
20
+
21
+ | Suffix | Meaning | Example |
22
+ |---|---|---|
23
+ | `*.component.tsx` | React component | `todo-form.component.tsx` |
24
+ | `*.view.tsx` | page/screen view | `todo-management.view.tsx` |
25
+ | `*.hook.ts` | React hook (incl. business/controller/selector) | `use-todo-business.hook.ts` |
26
+ | `*.helper.ts` | pure util (no React) | `validation.helper.ts` |
27
+ | `*.store.ts` | Zustand store/actions | `todo.store.ts` |
28
+ | `*.types.ts` | type definitions | `todo.types.ts` |
29
+
30
+ Repository-layer files use dotted descriptors: `[entity].repository.queries.ts`, `[entity].repository.mutations.ts`, `[entity].repository.keys.ts`, `[entity].repository.types.ts`, `[entity].query-options.ts`, `[entity].gateway.types.ts`. Store: `[m].store.ts`, `[m].store.actions.ts`, `[m].store.types.ts`.
31
+
32
+ Tests colocate next to source: `*.test.ts` / `*.test.tsx`. Test filenames may carry the source suffix (`todo-form.component.test.tsx`) or just `.test` on plain TS files (`local-storage-gateway.test.ts`) — both pass lint (`ignoreMiddleExtensions`).
33
+
34
+ Do NOT skip the suffix (`button.tsx` ❌ → `button.component.tsx` ✅). No generic `utils.ts`.
35
+
36
+ ## Code element casing
37
+
38
+ | Element | Case | Example |
39
+ |---|---|---|
40
+ | Variables / functions | `camelCase` | `userName`, `handleSubmit` |
41
+ | Components | `PascalCase` | `TodoManagementView` |
42
+ | Constants | `SCREAMING_SNAKE_CASE` | `MAX_RETRY_ATTEMPTS` |
43
+ | Types / interfaces | `PascalCase` | `TodoRepository` |
44
+
45
+ ## Barrels
46
+
47
+ Every dir has `index.ts` re-exporting its public items → enables `import { X } from '@/...'`.
48
+ ```ts
49
+ // components/index.ts
50
+ export { TodoForm } from './todo-form/todo-form.component';
51
+ ```
52
+
53
+ ## Exceptions to suffix rule
54
+
55
+ `index.ts`, `package.json`, framework files (`layout.tsx`, `page.tsx`), config files.
56
+
57
+ ## Other enforced lint (relevant)
58
+
59
+ - TS strict; `array-simple` array style; `record` indexed-object style; prefer optional chaining.
60
+ - Import sort (`simple-import-sort`): react/externals → `@/` aliases → side-effect → parent → sibling → css.
61
+ - Restricted imports: pull RTL/`react-dom` test bits from `@test/utils/test-utils`, not directly (`ui/` and `test/` exempt).
62
+ - Unused vars: prefix `_` to ignore.
63
+
64
+ ## Related skills
65
+ [[module-structure]] · [[clean-architecture]]
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: repository-pattern
3
+ description: Data-access layer combining React Query hooks + key factory + query/mutation options in scaffold-nextjs-app. Read before wiring a module's data fetching.
4
+ source: scaffold-nextjs-app/docs/repository-pattern.md + src/modules/todo/repositories/todo
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Repository pattern
9
+
10
+ Application layer. Wraps gateways with React Query. The View/business hook talks ONLY to the repository, never to gateway/api directly.
11
+
12
+ ## Files (per entity)
13
+
14
+ ```
15
+ repositories/[entity]/
16
+ ├── [entity].repository.types.ts # interfaces for queries/mutations/repository
17
+ ├── [entity].repository.keys.ts # query key factory
18
+ ├── [entity].query-options.ts # queryOptions/mutationOptions factories
19
+ ├── [entity].repository.queries.ts # query hooks object
20
+ ├── [entity].repository.mutations.ts # mutation hooks object
21
+ ├── helpers/[name]/[name].helper.ts
22
+ ├── gateways/ # see [[gateway-pattern]]
23
+ └── index.ts # combined repository object
24
+ ```
25
+
26
+ ## 1. Key factory
27
+
28
+ ```ts
29
+ export const todoQueryKeys = {
30
+ all: ['todos'] as const,
31
+ lists: (ds: DataSource = 'http') => [...todoQueryKeys.all, 'list', ds] as const,
32
+ list: (filters: TodoFilters = {}, ds: DataSource = 'http') =>
33
+ [...todoQueryKeys.lists(ds), filters] as const,
34
+ details: (ds: DataSource = 'http') => [...todoQueryKeys.all, 'detail', ds] as const,
35
+ detail: (id: string | number, ds: DataSource = 'http') =>
36
+ [...todoQueryKeys.details(ds), id] as const,
37
+ } as const;
38
+ ```
39
+ Keys are namespaced by `dataSource` so http/localStorage caches don't collide.
40
+
41
+ ## 2. query/mutation options (reusable, server-component-friendly)
42
+
43
+ ```ts
44
+ const getTodosQueryOptions = (filters = {}, ds: DataSource = 'http') =>
45
+ queryOptions({
46
+ queryKey: todoQueryKeys.list(filters, ds),
47
+ queryFn: ({ signal }) => createTodoGateway(ds).findAll(filters, { signal }),
48
+ staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000,
49
+ });
50
+
51
+ const getCreateTodoMutationOptions = (ds: DataSource = 'http') =>
52
+ mutationOptions({ mutationFn: (todo) => createTodoGateway(ds).create(todo), retry: 1 });
53
+
54
+ export const todoQueryOptions = { todos: getTodosQueryOptions, /* ... */ } as const;
55
+ export const todoMutationOptions = { createTodo: getCreateTodoMutationOptions, /* ... */ } as const;
56
+ ```
57
+ queryFn forwards `signal`, calls the gateway factory. Reused by both hooks and prefetch.
58
+
59
+ ## 3. Query hooks (compose options)
60
+
61
+ ```ts
62
+ export const todoQueriesRepository: TodoQueriesRepository = {
63
+ useTodos: (filters = {}, ds = 'http', options) =>
64
+ useQuery({ ...todoQueryOptions.todos(filters, ds), ...options }),
65
+ // prefetch: { prefetchTodos: createPrefetchFunction(...) } // server components
66
+ // cancel: { cancelTodos: async (...) => queryClient.cancelQueries(...) }
67
+ };
68
+ ```
69
+
70
+ ## 4. Mutation hooks (compose options + cache writes)
71
+
72
+ ```ts
73
+ export const todoMutationsRepository: TodoMutationsRepository = {
74
+ useCreateTodo: (ds = 'http', options) => {
75
+ const queryClient = useQueryClient();
76
+ return useMutation({
77
+ ...todoMutationOptions.createTodo(ds),
78
+ // eslint-disable-next-line max-params ← REQUIRED: onSuccess has 4 params, lint cap is 3
79
+ onSuccess: (newTodo, vars, onMutateResult, ctx) => {
80
+ queryClient.invalidateQueries({ queryKey: todoQueryKeys.lists(ds) });
81
+ queryClient.setQueryData<Todo[]>(todoQueryKeys.lists(ds), (old) => old ? [...old, newTodo] : [newTodo]);
82
+ options?.onSuccess?.(newTodo, vars, onMutateResult, ctx);
83
+ },
84
+ ...options,
85
+ });
86
+ },
87
+ };
88
+ ```
89
+ Mutations own cache invalidation/optimistic writes via `queryKeys`. Always call through user `options?.onSuccess`.
90
+
91
+ ## 5. Combined repository (the public surface)
92
+
93
+ ```ts
94
+ export const todoRepository: TodoRepository = {
95
+ queries: todoQueriesRepository,
96
+ mutations: todoMutationsRepository,
97
+ queryKeys: todoQueryKeys,
98
+ queryOptions: todoQueryOptions,
99
+ mutationOptions: todoMutationOptions,
100
+ };
101
+ ```
102
+ Consumers: `todoRepository.queries.useTodos(filters, ds)` / `todoRepository.mutations.useCreateTodo(ds)`.
103
+
104
+ ## Repository types (`*.repository.types.ts`)
105
+
106
+ Interfaces import from `@/core/lib/react-query`: `QueryOptions`, `MutationOptions` (custom option subsets), and queries-repo `extends BaseRepository`. Hook return types use `UseQueryResult<T, Error>` / `UseMutationResult<T, Error, TVars>`.
107
+ ```ts
108
+ import { type UseMutationResult, type UseQueryResult } from '@tanstack/react-query';
109
+ import type { BaseRepository } from '@/core/lib/react-query';
110
+ import { MutationOptions, QueryOptions } from '@/core/lib/react-query';
111
+
112
+ export interface XQueriesRepository extends BaseRepository {
113
+ useXs: (filters?: XFilters, dataSource?: DataSource, options?: QueryOptions) => UseQueryResult<X[], Error>;
114
+ }
115
+ export interface XMutationsRepository {
116
+ useCreateX: (dataSource?: DataSource, options?: MutationOptions) => UseMutationResult<X, Error, CreateXRequest>;
117
+ }
118
+ ```
119
+ Combined-repo `queryKeys`/`queryOptions`/`mutationOptions` typed via `typeof import('./...')`.
120
+
121
+ ## Rules
122
+ - Every hook accepts `dataSource` (default `'http'`) and passthrough `options`.
123
+ - Keys derive from the factory only; never inline key arrays in components.
124
+ - Repository is the boundary: business hooks/selectors import the repository, not gateways/api.
125
+ - `DataSource = 'http' | 'localStorage'` (the scaffold ships TWO sources, not `'mock'` despite older docs). Confirm in `@/types/gateway.types`.
126
+
127
+ ## Related skills
128
+ [[gateway-pattern]] · [[selector-pattern]] · [[hook-patterns]] · [[data-fetching-react-query]] · [[clean-architecture]]
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: requirement-intake
3
+ description: Normalize a raw work item (prompt, user story, or spec) into a structured REQUIREMENTS doc with acceptance criteria, asking clarifying questions and proactively surfacing gaps. Read at the start of any wavefront feature/change/fix.
4
+ source: wavefront framework meta — used by /wavefront-feature, /wavefront-change, /wavefront-fix
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Requirement intake
9
+
10
+ Turn any input shape into a planning-ready spec. The intake is **interactive and proactive** — not a parser. Read like a senior engineer: extract intent, then challenge gaps before any code is planned.
11
+
12
+ ## Accepted inputs
13
+ - **Prompt:** one-liner ("add a notifications module").
14
+ - **User story:** "Como [rol] quiero [acción] para [beneficio]" + optional acceptance criteria.
15
+ - **Spec:** long doc / pasted ticket.
16
+
17
+ ## Output: REQUIREMENTS structure
18
+ ```
19
+ ## Work item: <short title>
20
+ Type: feature | change | fix
21
+ Source: <prompt | user story | spec>
22
+
23
+ ### Intent
24
+ <1–3 sentences: who, what, why>
25
+
26
+ ### Scope
27
+ - In: <what's included>
28
+ - Out: <explicitly excluded>
29
+
30
+ ### Entities & layers touched
31
+ <entities; which Clean Arch layers (api/gateway/repo/store/selector/hook/view/component) are affected>
32
+
33
+ ### Acceptance criteria (→ become tests)
34
+ - [ ] <criterion 1, observable/testable>
35
+ - [ ] ...
36
+
37
+ ### Open questions (must resolve before planning)
38
+ - <question> — <why it matters>
39
+
40
+ ### Suggested additions (gaps intake spotted, propose to user)
41
+ - <edge case / error state / missing criterion the story didn't mention>
42
+ ```
43
+
44
+ ## Procedure
45
+ 1. **Classify** the input (prompt/story/spec) and the worktype (feature/change/fix).
46
+ 2. **Extract** intent, scope, entities, and any stated acceptance criteria.
47
+ 3. **Map to layers:** which Clean Arch layers this likely touches (informs the plan). See [[module-structure]] [[clean-architecture]].
48
+ 4. **Challenge — the important part.** Proactively surface what the input left implicit:
49
+ - Acceptance criteria implied but not written.
50
+ - **Edge cases:** empty/loading/error states, pagination, permissions, concurrent edits.
51
+ - **Error handling:** what happens on API failure, validation failure, not-found.
52
+ - **Ambiguity:** vague terms ("fast", "some", "etc"), undefined entities, unclear ownership.
53
+ - **i18n/a11y:** user-facing strings, keyboard/screen-reader needs (this stack is i18n-first — see [[i18n-next-intl]]).
54
+ Put these in **Open questions** (blocking) and **Suggested additions** (proposals).
55
+ 5. **Ask** the blocking questions. Use a concise question list; offer recommended answers where you have a strong default. Don't proceed to planning with unresolved blockers.
56
+ 6. **Finalize** REQUIREMENTS once questions are answered; write/update `.claude/.planning/REQUIREMENTS.md`.
57
+
58
+ ## Rules
59
+ - Never invent acceptance criteria silently — propose them under Suggested additions and confirm.
60
+ - Acceptance criteria must be observable (a test can assert them).
61
+ - A vague story is a signal to ask, not to guess.
62
+ - Keep it tight: intake produces a spec, not an essay.
63
+
64
+ ## Related
65
+ [[clean-architecture]] [[module-structure]] [[hook-patterns]] [[testing-jest-rtl]] [[i18n-next-intl]]
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: responsive-layouts
3
+ description: Mobile-first responsive layout in scaffold-nextjs-app — the theme's custom breakpoints, responsive MUI sx, and the useResponsiveScreen hook. Read before building any view/component. Responsive is mandatory, not optional.
4
+ source: scaffold-nextjs-app/src/theme.ts, src/styles/breakpoints, src/core/hooks/use-responsive-screen, src/modules/*/views
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Responsive layouts
9
+
10
+ Every view/component is responsive — **mandatory, mobile-first**. A design usually arrives in one viewport (a desktop Figma, one screenshot); the implementation must still work on mobile, tablet, desktop. Never ship a fixed-width layout that breaks on small screens.
11
+
12
+ ## The theme uses CUSTOM breakpoint names (not xs/sm/md)
13
+ From `src/theme.ts` (values from `src/styles/breakpoints`):
14
+ | Key | Width |
15
+ |---|---|
16
+ | `minMobile` | 0px |
17
+ | `maxMobile` | 767px |
18
+ | `minTablet` | 768px |
19
+ | `maxTablet` | 1023px |
20
+ | `minDesktop` | 1024px |
21
+ | `largeScreen` | 1440px |
22
+ | `xlargeScreen` | 2560px |
23
+
24
+ Use THESE keys in MUI `sx`/breakpoint props — NOT default `xs/sm/md/lg` (the theme replaced them). `Container maxWidth` also takes these (`maxWidth="largeScreen"`).
25
+
26
+ ## Two responsive tools
27
+
28
+ ### 1. CSS-driven via `sx` (preferred — no JS, no re-render)
29
+ ```tsx
30
+ <Box
31
+ display="flex"
32
+ sx={{
33
+ flexDirection: { minMobile: 'column', minTablet: 'row' }, // stack on phone, row from tablet
34
+ gap: { minMobile: tokens.spacing.scale2, minTablet: tokens.spacing.scale4 },
35
+ }}
36
+ />
37
+ <Typography sx={{ fontSize: { minMobile: '1rem', minDesktop: '1.25rem' } }} />
38
+ <Grid container columns={{ minMobile: 1, minTablet: 2, minDesktop: 3 }} />
39
+ ```
40
+ Object syntax `{ minMobile: A, minTablet: B }` = mobile-first (value applies from that breakpoint up). Default to `sx` responsive for layout/spacing/sizing.
41
+
42
+ ### 2. JS-driven via `useResponsiveScreen` (when you need branching logic)
43
+ ```tsx
44
+ import { useResponsiveScreen } from '@/core';
45
+ const { isMobileDevice, isTabletDevice, isDesktopDevice, deviceType, isPortrait, isLandscape } = useResponsiveScreen();
46
+ // e.g. render a Drawer on mobile vs a sidebar on desktop; change column count; swap components
47
+ ```
48
+ Use only when CSS `sx` can't express it (different component per device, conditional rendering). Prefer `sx` first.
49
+
50
+ ## Rules (mandatory)
51
+ - Every view/component works at minMobile, minTablet, minDesktop. No fixed pixel widths that overflow mobile — use `%`, `fullWidth`, flex, or breakpoint objects.
52
+ - Mobile-first: base value = smallest screen; scale up with breakpoint keys.
53
+ - Spacing/sizing still via `tokens.spacing.scaleN` (responsive values pick among tokens).
54
+ - Touch targets adequate on mobile; no horizontal scroll on small screens.
55
+ - The four states (loading/empty/error/populated) must each be responsive too.
56
+
57
+ ## When the design source has only ONE viewport
58
+ This is the norm. Do NOT silently guess:
59
+ 1. Note that responsive behavior is unspecified.
60
+ 2. **Ask** the user how key elements should adapt (stack vs wrap vs hide; column counts; nav pattern) — see [[design-intake]]. Offer sensible defaults (mobile stacks columns, multi-col grids collapse, side-nav → top/drawer).
61
+ 3. Only after confirmation, implement. If the user defers, apply mobile-first defaults and flag them.
62
+
63
+ ## Related
64
+ [[design-intake]] [[design-system-inventory]] [[mui-design-tokens]] [[hook-patterns]]
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: selector-pattern
3
+ description: Derived-state selector hooks that combine repository data + store state with useMemo in scaffold-nextjs-app. Read before computing derived data.
4
+ source: scaffold-nextjs-app/docs/selector-pattern.md + src/modules/todo/selectors
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Selector pattern
9
+
10
+ Application layer. A selector hook computes derived/aggregated data from repository queries and/or store state, memoized. Keeps business hooks and components thin.
11
+
12
+ ## Shape (one dir per selector)
13
+
14
+ ```
15
+ selectors/
16
+ ├── use-[name]-selector/use-[name]-selector.hook.ts
17
+ └── index.ts # barrel
18
+ ```
19
+
20
+ ## Example
21
+
22
+ ```ts
23
+ // use-todo-stats-selector/use-todo-stats-selector.hook.ts
24
+ import { useMemo } from 'react';
25
+ import { todoRepository } from '@/modules/todo/repositories/todo';
26
+ import type { DataSource } from '@/types/gateway.types';
27
+
28
+ export interface TodoStats { total: number; completed: number; completionRate: number; /* ... */ }
29
+
30
+ export const useTodoStatsSelector = (dataSource: DataSource = 'http') => {
31
+ const todosQuery = todoRepository.queries.useTodos({}, dataSource);
32
+
33
+ const stats = useMemo((): TodoStats => {
34
+ const todos = todosQuery.data ?? [];
35
+ const completed = todos.filter((t) => t.completed).length;
36
+ return {
37
+ total: todos.length,
38
+ completed,
39
+ completionRate: todos.length ? Math.round((completed / todos.length) * 100) : 0,
40
+ };
41
+ }, [todosQuery.data]);
42
+
43
+ return { data: stats, isLoading: todosQuery.isLoading, error: todosQuery.error, refetch: todosQuery.refetch };
44
+ };
45
+ ```
46
+
47
+ ## Conventions
48
+ - Name `useXSelector`, accept `dataSource` (default `'http'`).
49
+ - Read source data from the **repository** (queries) and/or **store** (`useXStore`). Never fetch directly.
50
+ - Compute in **`useMemo`** keyed on the source data.
51
+ - Return a query-like shape: `{ data, isLoading, error, refetch }` so consumers treat selectors and repo queries uniformly.
52
+ - Pure derivation only — no mutations, no side effects.
53
+
54
+ ## Rules
55
+ - One selector = one derived concern (stats, filtered list, selected item). Compose multiple in a business hook.
56
+ - Selectors are the place for cross-source combination (repo data + store filters), not components.
57
+
58
+ ## Related skills
59
+ [[repository-pattern]] · [[store-zustand]] · [[hook-patterns]] · [[clean-architecture]]
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: store-zustand
3
+ description: Module-local Zustand stores with createStoreWithMiddleware (immer + persist) and nested actions in scaffold-nextjs-app. Read before adding local state.
4
+ source: scaffold-nextjs-app/src/modules/todo/stores, src/core/lib/zustand
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Zustand stores
9
+
10
+ Application layer. Local UI/domain state per module (selection, filters, transient flags). NOT server data — that's React Query's job ([[repository-pattern]]).
11
+
12
+ ## Files (per module)
13
+
14
+ ```
15
+ stores/
16
+ ├── [module].store.ts # store definition
17
+ ├── [module].store.actions.ts # actions selector hook (useXActions)
18
+ └── [module].store.types.ts # State + StoreState types
19
+ ```
20
+
21
+ ## Store definition
22
+
23
+ ```ts
24
+ // [module].store.ts
25
+ import type { Draft } from 'immer';
26
+ import { createStoreWithMiddleware } from '@/core/lib/zustand';
27
+ import type { TodoState, TodoStoreState } from './todo.store.types';
28
+
29
+ const initialState: TodoState = { selectedTodo: null, filters: {}, isCreating: false, isEditing: false };
30
+
31
+ export const useTodoStore = createStoreWithMiddleware<TodoStoreState>(
32
+ (set, _get) => ({
33
+ ...initialState,
34
+ actions: {
35
+ setSelectedTodo: (todo) => set({ selectedTodo: todo }),
36
+ setFilters: (newFilters) =>
37
+ set((draft: Draft<TodoStoreState>) => { Object.assign(draft.filters, newFilters); }), // immer
38
+ clearFilters: () => set({ filters: {} }),
39
+ },
40
+ }),
41
+ 'todo-store', // store name (persist key / devtools)
42
+ { persist: true, exclude: ['isCreating', 'isEditing'] }, // persist config
43
+ );
44
+ ```
45
+
46
+ ## Conventions
47
+ - **`createStoreWithMiddleware`** from `@/core/lib/zustand` wraps immer + persist + devtools. Never call raw `create` from `zustand`.
48
+ - **Actions nested under `actions`** key (kept out of persisted/selected state).
49
+ - **Immer**: object/array mutations use `set((draft) => {...})` with `Draft<T>`; simple replacements use `set({ ... })`.
50
+ - **Third arg = store name**; **fourth = options** (`persist`, `exclude` keys from persistence).
51
+ - Types split: `XState` (data shape) + `XStoreState` (state + `actions`) in `*.store.types.ts`.
52
+
53
+ ## Consuming
54
+ - State via selector: `const filters = useTodoStore((s) => s.filters)`.
55
+ - Actions via dedicated hook: `useTodoActions()` (in `*.store.actions.ts`) → `const { setFilters } = useTodoActions()`.
56
+ - Derived/cross-source data → use a [[selector-pattern]] hook, not the store.
57
+
58
+ ## Rules
59
+ - Store holds local state only. No fetching, no API.
60
+ - Keep transient flags out of persistence via `exclude`.
61
+
62
+ ## Related skills
63
+ [[selector-pattern]] · [[hook-patterns]] · [[clean-architecture]]