rbin-task-flow 1.4.0 → 1.5.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.
@@ -0,0 +1,712 @@
1
+ ---
2
+ description: Coding standards and architecture patterns for all projects
3
+ globs: **/*
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Coding Standards & Architecture
8
+
9
+ These rules define how code MUST be written when implementing tasks. Always follow these patterns without deviation.
10
+
11
+ ---
12
+
13
+ ## Project Structure
14
+
15
+ All projects use TypeScript and follow this structure:
16
+
17
+ ```
18
+ src/
19
+ ├── app/ # Route definitions only (Next.js App Router or Expo Router)
20
+ ├── features/ # Business domain logic, organized by feature
21
+ └── shared/ # Global reusable code
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Tech Stack
27
+
28
+ ### Front-end (Next.js)
29
+ - **Framework**: Next.js 15+ with App Router
30
+ - **Language**: TypeScript (strict mode)
31
+ - **Styling**: Tailwind CSS v4 + `clsx` + `tailwind-merge` via `cn()` helper
32
+ - **UI Components**: shadcn/ui + `lucide-react` icons
33
+ - **Data Fetching**: `@tanstack/react-query`
34
+ - **Forms**: `react-hook-form` + `zod` + `@hookform/resolvers/zod`
35
+ - **Notifications**: `sonner`
36
+ - **ESLint**: `@rbinflow/eslint-config`
37
+ - **E2E Testing**: Playwright and/or Cypress
38
+
39
+ ### Mobile (Expo / React Native)
40
+ - **Framework**: Expo with Expo Router
41
+ - **Language**: TypeScript (strict mode)
42
+ - **Styling**: NativeWind + `clsx` + `tailwind-merge` via `cn()` helper
43
+ - **Data Fetching**: `@tanstack/react-query`
44
+ - **Forms**: `react-hook-form` + `zod` + `@hookform/resolvers/zod`
45
+ - **Build**: EAS (Expo Application Services)
46
+ - **ESLint**: `@rbinflow/eslint-config`
47
+ - **E2E Testing**: Detox
48
+
49
+ ### Backend (NestJS)
50
+ - **Framework**: NestJS 10+
51
+ - **Language**: TypeScript (strict mode)
52
+ - **ORM**: Prisma
53
+ - **Database**: PostgreSQL
54
+ - **Auth**: JWT + Passport
55
+ - **Validation**: Zod (not class-validator/class-transformer)
56
+ - **Structure**: `src/app/` (controllers), `src/features/` (use-cases, repositories), `src/shared/` (guards, pipes, gateways)
57
+
58
+ ---
59
+
60
+ ## app/ — Routes Only (Front-end & Mobile) / Controllers Only (Backend)
61
+
62
+ The `app/` directory contains ONLY route definitions. Each file is a thin wrapper that imports and renders the feature page/screen.
63
+
64
+ **Front-end (Next.js App Router):**
65
+ ```typescript
66
+ // src/app/(private)/dashboard/page.tsx
67
+ import { DashboardPage } from '@/features/dashboard/pages/dashboard.page'
68
+
69
+ export default function Dashboard() {
70
+ return <DashboardPage />
71
+ }
72
+ ```
73
+
74
+ **Mobile (Expo Router):**
75
+ ```typescript
76
+ // src/app/(private)/dashboard.tsx
77
+ import { DashboardScreen } from '@/features/dashboard/screens/dashboard.screen'
78
+
79
+ export default function Dashboard() {
80
+ return <DashboardScreen />
81
+ }
82
+ ```
83
+
84
+ **Backend (NestJS):**
85
+ ```typescript
86
+ // src/app/account/controllers/create-session.controller.ts
87
+ import { Body, Controller, HttpCode, Post } from '@nestjs/common'
88
+ import { CreateSessionBodySchema, createSessionValidator } from '@/app/account/validators/create-session.validator'
89
+ import { CreateSessionUseCase } from '@/features/account/use-cases/create-session.use-case'
90
+
91
+ @Controller('/session')
92
+ export class CreateSessionController {
93
+ constructor(private createSessionUseCase: CreateSessionUseCase) {}
94
+
95
+ @Post()
96
+ @HttpCode(200)
97
+ async handle(@Body(createSessionValidator) data: CreateSessionBodySchema) {
98
+ return this.createSessionUseCase.execute(data)
99
+ }
100
+ }
101
+ ```
102
+
103
+ **Rules:**
104
+ - **Front-end/Mobile**: `page.tsx` / route files NEVER contain logic, state, or imports beyond the feature component
105
+ - **Backend**: `app/` contains controllers and validators only — no business logic
106
+ - Group routes with `(public)` and `(private)` folders (front/mobile)
107
+ - `layout.tsx` files may contain auth guards and providers
108
+ - All business logic lives in `features/`
109
+
110
+ ---
111
+
112
+ ## features/ — Feature Organization
113
+
114
+ Each feature is self-contained. Structure:
115
+
116
+ ```
117
+ features/[feature-name]/
118
+ ├── components/ # UI components specific to this feature
119
+ ├── hooks/ # Context providers and custom hooks
120
+ ├── pages/ # Page component (front-end) — orchestrator
121
+ ├── screens/ # Screen component (mobile) — orchestrator
122
+ ├── schemas/ # Zod validation schemas
123
+ ├── services/ # React Query wrappers (useQuery / useMutation)
124
+ ├── types/ # TypeScript types for this feature
125
+ ├── use-cases/ # Pure API call logic
126
+ └── utils/ # Feature-specific utilities (optional)
127
+ ```
128
+
129
+ ### Backend Feature Structure
130
+
131
+ ```
132
+ features/[feature-name]/
133
+ ├── use-cases/ # Business logic
134
+ │ └── create-account.use-case.ts
135
+ ├── repositories/ # Data access (abstract + Prisma implementation)
136
+ │ ├── account.repository.ts # Abstract class
137
+ │ └── prisma-account.repository.ts # Prisma implementation
138
+ ├── types/ # TypeScript types
139
+ │ └── account.type.ts
140
+ └── utils/ # Feature-specific utilities (optional)
141
+ ```
142
+
143
+ **Use Case (backend):**
144
+ ```typescript
145
+ // src/features/account/use-cases/create-session.use-case.ts
146
+ import { Injectable, UnauthorizedException } from '@nestjs/common'
147
+ import { AccountRepository } from '@/features/account/repositories/account.repository'
148
+ import { EncryptionGateway } from '@/shared/gateways/encryption.gateway'
149
+ import { JwtGateway } from '@/shared/gateways/jwt.gateway'
150
+
151
+ @Injectable()
152
+ export class CreateSessionUseCase {
153
+ constructor(
154
+ private accountRepository: AccountRepository,
155
+ private encryption: EncryptionGateway,
156
+ private jwt: JwtGateway,
157
+ ) {}
158
+
159
+ async execute(data: { email: string; password: string }) {
160
+ const account = await this.accountRepository.findByEmail(data.email)
161
+ if (!account) throw new UnauthorizedException('Invalid credentials')
162
+
163
+ const isValid = await this.encryption.validateHash({
164
+ value: data.password,
165
+ hashedValue: account.password,
166
+ })
167
+ if (!isValid) throw new UnauthorizedException('Invalid credentials')
168
+
169
+ return this.jwt.generateAuthTokens({ accountId: account.id })
170
+ }
171
+ }
172
+ ```
173
+
174
+ **Zod validation in NestJS (not class-validator):**
175
+ ```typescript
176
+ // src/app/account/validators/create-session.validator.ts
177
+ import { z } from 'zod'
178
+ import { ZodValidationPipe } from '@/shared/pipes/zod-validation.pipe'
179
+
180
+ const createSessionBodySchema = z.object({
181
+ email: z.string().email(),
182
+ password: z.string().min(6),
183
+ })
184
+
185
+ export type CreateSessionBodySchema = z.infer<typeof createSessionBodySchema>
186
+ export const createSessionValidator = new ZodValidationPipe(createSessionBodySchema)
187
+ ```
188
+
189
+ ```typescript
190
+ // src/shared/pipes/zod-validation.pipe.ts
191
+ import { BadRequestException, PipeTransform } from '@nestjs/common'
192
+ import { ZodError, ZodSchema } from 'zod'
193
+
194
+ export class ZodValidationPipe implements PipeTransform {
195
+ constructor(private schema: ZodSchema) {}
196
+
197
+ transform(value: unknown) {
198
+ try {
199
+ return this.schema.parse(value)
200
+ } catch (error) {
201
+ if (error instanceof ZodError) {
202
+ throw new BadRequestException({
203
+ message: 'Validation failed',
204
+ errors: error.errors.map((e) => ({ field: e.path.join('.'), message: e.message })),
205
+ })
206
+ }
207
+ throw new BadRequestException('Validation failed')
208
+ }
209
+ }
210
+ }
211
+ ```
212
+
213
+ **Gateways (abstract services in shared/):**
214
+ ```typescript
215
+ // src/shared/gateways/encryption.gateway.ts
216
+ export abstract class EncryptionGateway {
217
+ abstract createHash(value: string): Promise<string>
218
+ abstract validateHash(params: { value: string; hashedValue: string }): Promise<boolean>
219
+ }
220
+ ```
221
+
222
+ Gateways abstract external concerns (encryption, JWT, date, email). Implementations live alongside in `shared/gateways/` and are bound via NestJS modules.
223
+
224
+ ### Page / Screen — Orchestrator Pattern (Front-end/Mobile)
225
+
226
+ The page/screen is an orchestrator. It:
227
+ - Calls services (React Query hooks)
228
+ - Manages form state with `useForm`
229
+ - Passes data down to components as props
230
+ - Is NEVER a long file — delegates rendering to components
231
+
232
+ ```typescript
233
+ // src/features/dashboard/pages/dashboard.page.tsx
234
+ 'use client'
235
+
236
+ import { DashboardRevenueCard } from '@/features/dashboard/components/dashboard-revenue-card'
237
+ import { DashboardActiveCard } from '@/features/dashboard/components/dashboard-active-card'
238
+ import { GetDashboardActiveService } from '@/features/dashboard/services/get-dashboard-active.service'
239
+
240
+ export function DashboardPage() {
241
+ const { data: active, isLoading, isError } = GetDashboardActiveService()
242
+
243
+ return (
244
+ <section className="grid grid-cols-2 gap-5 my-10">
245
+ <DashboardActiveCard data={active} isLoading={isLoading} isError={isError} />
246
+ <DashboardRevenueCard />
247
+ </section>
248
+ )
249
+ }
250
+ ```
251
+
252
+ ### Components — Single Responsibility
253
+
254
+ Each component has a single responsibility. Components:
255
+ - Receive data via props (no direct service calls unless truly necessary)
256
+ - Are named after the feature: `[feature]-[description].tsx`
257
+ - Stay small and focused
258
+
259
+ ```typescript
260
+ // src/features/dashboard/components/dashboard-active-card.tsx
261
+ import { Card } from '@/shared/components/card'
262
+ import { Skeleton } from '@/shared/components/skeleton'
263
+
264
+ type DashboardActiveCardProps = {
265
+ data?: { count: number; trials: number }
266
+ isLoading: boolean
267
+ isError: boolean
268
+ }
269
+
270
+ export function DashboardActiveCard({ data, isLoading, isError }: DashboardActiveCardProps) {
271
+ if (isLoading) return <Skeleton className="h-[200px]" />
272
+ if (isError) return <ErrorState />
273
+
274
+ return (
275
+ <Card title="Active Members">
276
+ <p className="text-2xl font-bold">{data?.count}</p>
277
+ </Card>
278
+ )
279
+ }
280
+ ```
281
+
282
+ ---
283
+
284
+ ## shared/ — Global Reusable Code
285
+
286
+ ```
287
+ shared/
288
+ ├── components/
289
+ │ ├── button.tsx
290
+ │ ├── card.tsx
291
+ │ ├── data-handler.tsx
292
+ │ ├── skeleton.tsx
293
+ │ ├── dialog.tsx
294
+ │ └── form/
295
+ │ └── inputs/
296
+ │ ├── input-text.tsx
297
+ │ ├── input-select.tsx
298
+ │ └── shared/
299
+ │ ├── input-container.tsx
300
+ │ ├── input-error.tsx
301
+ │ └── input-label.tsx
302
+ ├── hooks/
303
+ │ ├── dialog.hook.tsx
304
+ │ └── drawer.hook.tsx
305
+ ├── libs/
306
+ │ ├── axios.ts
307
+ │ ├── react-query.ts
308
+ │ └── tw-merge.ts
309
+ ├── providers/
310
+ │ └── react-query-provider.tsx
311
+ ├── types/
312
+ │ └── shared.ui.type.ts
313
+ ├── constants/
314
+ │ └── server-routes.constants.ts
315
+ └── utils/
316
+ ├── validation/
317
+ │ ├── required-email.validation.ts
318
+ │ ├── required-string.validation.ts
319
+ │ └── required-phone.validation.ts
320
+ └── error.util.ts
321
+ ```
322
+
323
+ ---
324
+
325
+ ## DataHandler — Declarative Loading/Error States
326
+
327
+ Use a `DataHandler` component to handle loading, error, and empty states declaratively instead of repeating `if (isLoading)` / `if (isError)` in every component.
328
+
329
+ ```typescript
330
+ // src/shared/components/data-handler.tsx
331
+ import { ReactNode } from 'react'
332
+ import { Skeleton } from '@/shared/components/skeleton'
333
+
334
+ type DataHandlerProps = {
335
+ isLoading: boolean
336
+ isError: boolean
337
+ children: ReactNode
338
+ skeleton?: ReactNode
339
+ }
340
+
341
+ export function DataHandler({ isLoading, isError, children, skeleton }: DataHandlerProps) {
342
+ if (isLoading) return skeleton ?? <Skeleton />
343
+ if (isError) return <ErrorState />
344
+ return <>{children}</>
345
+ }
346
+ ```
347
+
348
+ Usage in pages:
349
+ ```typescript
350
+ <DataHandler isLoading={isLoading} isError={isError} skeleton={<DashboardSkeleton />}>
351
+ <DashboardRevenueCard revenue={revenue} />
352
+ </DataHandler>
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Feature `platform` — Cross-Cutting Concerns
358
+
359
+ Every project has a special `features/platform/` feature for app-wide concerns that don't belong in `shared/` (they are app-specific, not reusable across projects):
360
+
361
+ ```
362
+ features/platform/
363
+ ├── components/ # App shell (menu, sidebar, header)
364
+ ├── constants/ # App routes, cookie keys, app name
365
+ │ ├── platform-routes.constants.ts
366
+ │ └── platform-cookies.constants.ts
367
+ ├── hooks/ # App-level hooks
368
+ ├── providers/ # AppProviders composition
369
+ │ └── app-providers-client.tsx
370
+ └── i18n/ # Internationalization (optional)
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Providers Composition
376
+
377
+ Compose all providers in a dedicated `AppProviders` component instead of nesting them in `layout.tsx`:
378
+
379
+ ```typescript
380
+ // src/features/platform/providers/app-providers-client.tsx
381
+ 'use client'
382
+
383
+ import { ReactNode } from 'react'
384
+ import { Toaster } from 'sonner'
385
+ import { AuthProvider } from '@/features/auth/hooks/authentication.hook'
386
+ import { DialogProvider } from '@/shared/hooks/dialog.hook'
387
+
388
+ export function AppProvidersClient({ children }: { children: ReactNode }) {
389
+ return (
390
+ <AuthProvider>
391
+ <DialogProvider>
392
+ {children}
393
+ <Toaster position="top-right" />
394
+ </DialogProvider>
395
+ </AuthProvider>
396
+ )
397
+ }
398
+ ```
399
+
400
+ Then in the root layout:
401
+ ```typescript
402
+ // src/app/layout.tsx
403
+ export default function RootLayout({ children }: { children: ReactNode }) {
404
+ return (
405
+ <html lang="en">
406
+ <body>
407
+ <ReactQueryProvider>
408
+ <AppProvidersClient>{children}</AppProvidersClient>
409
+ </ReactQueryProvider>
410
+ </body>
411
+ </html>
412
+ )
413
+ }
414
+ ```
415
+
416
+ ---
417
+
418
+ ## cn() — Tailwind Class Merging
419
+
420
+ Always use `cn()` for class names. Never use raw string concatenation or template literals with conditional classes.
421
+
422
+ ```typescript
423
+ // src/shared/libs/tw-merge.ts
424
+ import { type ClassValue, clsx } from 'clsx'
425
+ import { twMerge } from 'tailwind-merge'
426
+
427
+ export function cn(...inputs: ClassValue[]) {
428
+ return twMerge(clsx(inputs))
429
+ }
430
+ ```
431
+
432
+ Usage:
433
+ ```typescript
434
+ import { cn } from '@/shared/libs/tw-merge'
435
+
436
+ className={cn(
437
+ 'flex flex-col rounded-lg bg-white px-5',
438
+ isActive && 'border-2 border-accent-500',
439
+ className,
440
+ )}
441
+ ```
442
+
443
+ ---
444
+
445
+ ## React Query — Service + Use Case Pattern
446
+
447
+ Split API calls into two layers:
448
+
449
+ **Use Case** — pure function, no React, just the API call:
450
+ ```typescript
451
+ // src/features/auth/use-cases/session-create.use-case.ts
452
+ import { api } from '@/shared/libs/axios'
453
+ import { serverRoutes } from '@/shared/constants/server-routes.constants'
454
+ import { LoginSchema } from '@/features/auth/schemas/login.schema'
455
+ import { SessionType } from '@/features/auth/types/session.api.type'
456
+
457
+ export async function sessionCreateUseCase(data: LoginSchema): Promise<SessionType> {
458
+ const response = await api.post(`${serverRoutes.session}/create`, data)
459
+ return response.data
460
+ }
461
+ ```
462
+
463
+ **Service** — React Query wrapper used in components:
464
+ ```typescript
465
+ // src/features/auth/services/session-create.service.tsx
466
+ 'use client'
467
+
468
+ import { useMutation } from '@tanstack/react-query'
469
+ import { sessionCreateUseCase } from '@/features/auth/use-cases/session-create.use-case'
470
+ import { SessionType } from '@/features/auth/types/session.api.type'
471
+ import { ServiceInput } from '@/shared/types/shared.ui.type'
472
+ import { handleError } from '@/shared/utils/error.util'
473
+
474
+ export function SessionCreateService({ onSuccess }: ServiceInput<SessionType>) {
475
+ return useMutation({
476
+ mutationFn: sessionCreateUseCase,
477
+ onSuccess: (data) => onSuccess?.(data),
478
+ onError: (error) => handleError(error),
479
+ })
480
+ }
481
+ ```
482
+
483
+ **Query service:**
484
+ ```typescript
485
+ // src/features/dashboard/services/get-dashboard-active.service.tsx
486
+ 'use client'
487
+
488
+ import { useQuery } from '@tanstack/react-query'
489
+ import { getDashboardActiveUseCase } from '@/features/dashboard/use-cases/get-dashboard-active.use-case'
490
+ import { DashboardActiveType } from '@/features/dashboard/types/dashboard-active.type'
491
+
492
+ export const getDashboardActiveQueryKey = 'getDashboardActiveQueryKey'
493
+
494
+ export function GetDashboardActiveService() {
495
+ return useQuery<DashboardActiveType>({
496
+ queryKey: [getDashboardActiveQueryKey],
497
+ queryFn: getDashboardActiveUseCase,
498
+ })
499
+ }
500
+ ```
501
+
502
+ **Shared service input type:**
503
+ ```typescript
504
+ // src/shared/types/shared.ui.type.ts
505
+ export type ServiceInput<TData = unknown> = {
506
+ onSuccess?: (data?: TData) => void
507
+ onError?: () => void
508
+ }
509
+ ```
510
+
511
+ ---
512
+
513
+ ## Forms — React Hook Form + Zod
514
+
515
+ **Schema:**
516
+ ```typescript
517
+ // src/features/auth/schemas/login.schema.ts
518
+ import { z } from 'zod'
519
+ import { requiredEmail } from '@/shared/utils/validation/required-email.validation'
520
+ import { requiredString } from '@/shared/utils/validation/required-string.validation'
521
+
522
+ export const loginSchema = z.object({
523
+ email: requiredEmail(),
524
+ password: requiredString({ field: 'password' }),
525
+ })
526
+
527
+ export type LoginSchema = z.infer<typeof loginSchema>
528
+ ```
529
+
530
+ **Reusable validators in shared/:**
531
+ ```typescript
532
+ // src/shared/utils/validation/required-string.validation.ts
533
+ import { z } from 'zod'
534
+
535
+ export const requiredString = ({ field, min = 1 }: { field: string; min?: number }) =>
536
+ z.string({ message: `${field} is required` }).min(min, {
537
+ message: `${field} must be at least ${min} characters`,
538
+ })
539
+ ```
540
+
541
+ **Form component:**
542
+ ```typescript
543
+ // src/features/auth/components/login-form.tsx
544
+ 'use client'
545
+
546
+ import { zodResolver } from '@hookform/resolvers/zod'
547
+ import { useForm } from 'react-hook-form'
548
+ import { LoginSchema, loginSchema } from '@/features/auth/schemas/login.schema'
549
+ import { SessionCreateService } from '@/features/auth/services/session-create.service'
550
+ import { InputText } from '@/shared/components/form/inputs/input-text'
551
+ import { Button } from '@/shared/components/button'
552
+
553
+ export function LoginForm() {
554
+ const { control, handleSubmit } = useForm<LoginSchema>({
555
+ resolver: zodResolver(loginSchema),
556
+ defaultValues: { email: '', password: '' },
557
+ })
558
+
559
+ const { mutate: createSession, isPending } = SessionCreateService({
560
+ onSuccess: () => { /* redirect */ },
561
+ })
562
+
563
+ return (
564
+ <form onSubmit={handleSubmit((data) => createSession(data))} className="flex flex-col gap-4">
565
+ <InputText name="email" control={control} placeholder="Email" />
566
+ <InputText name="password" control={control} type="password" placeholder="Password" />
567
+ <Button type="submit" isLoading={isPending}>Sign in</Button>
568
+ </form>
569
+ )
570
+ }
571
+ ```
572
+
573
+ **Input uses `Controller` from RHF and is generic:**
574
+ ```typescript
575
+ // src/shared/components/form/inputs/input-text.tsx
576
+ import { Control, Controller, FieldValues, Path, PathValue } from 'react-hook-form'
577
+ import { cn } from '@/shared/libs/tw-merge'
578
+
579
+ type InputTextProps<T extends FieldValues> = {
580
+ name: Path<T>
581
+ control: Control<T>
582
+ defaultValue?: PathValue<T, Path<T>>
583
+ placeholder?: string
584
+ type?: 'text' | 'password' | 'email'
585
+ disabled?: boolean
586
+ className?: string
587
+ }
588
+
589
+ export function InputText<T extends FieldValues>({
590
+ name, control, defaultValue = '' as PathValue<T, Path<T>>,
591
+ placeholder, type = 'text', disabled, className,
592
+ }: InputTextProps<T>) {
593
+ return (
594
+ <Controller
595
+ name={name}
596
+ control={control}
597
+ defaultValue={defaultValue}
598
+ render={({ field, fieldState: { error } }) => (
599
+ <div className={cn('flex flex-col gap-1', className)}>
600
+ <input
601
+ {...field}
602
+ type={type}
603
+ placeholder={placeholder}
604
+ disabled={disabled}
605
+ className={cn('border rounded-lg px-4 h-12 text-sm', error && 'border-red-500')}
606
+ />
607
+ {error && <span className="text-xs text-red-500">{error.message}</span>}
608
+ </div>
609
+ )}
610
+ />
611
+ )
612
+ }
613
+ ```
614
+
615
+ ---
616
+
617
+ ## File Naming Conventions
618
+
619
+ Use kebab-case for all filenames. Suffix reflects the file's role:
620
+
621
+ | Role | Suffix | Example |
622
+ |------|--------|---------|
623
+ | Page component | `.page.tsx` | `dashboard.page.tsx` |
624
+ | Screen component (mobile) | `.screen.tsx` | `login.screen.tsx` |
625
+ | UI component | `.tsx` | `dashboard-revenue-card.tsx` |
626
+ | React Query service | `.service.tsx` | `session-create.service.tsx` |
627
+ | API call (pure) | `.use-case.ts` | `session-create.use-case.ts` |
628
+ | Zod schema | `.schema.ts` | `login.schema.ts` |
629
+ | Context/hook | `.hook.tsx` | `authentication.hook.tsx` |
630
+ | API response type | `.api.type.ts` | `session.api.type.ts` |
631
+ | Domain type | `.type.ts` | `dashboard-active.type.ts` |
632
+ | Utility | `.util.ts` | `error.util.ts` |
633
+ | Validation helper | `.validation.ts` | `required-email.validation.ts` |
634
+ | Constants | `.constants.ts` | `server-routes.constants.ts` |
635
+
636
+ ---
637
+
638
+ ## TypeScript Types
639
+
640
+ - API response types: `[Name]Type` in a `.api.type.ts` file
641
+ - Domain/internal types: `[Name]Type` in a `.type.ts` file
642
+ - All types exported with `export type`
643
+ - No `any` — ever
644
+
645
+ ```typescript
646
+ // src/features/auth/types/session.api.type.ts
647
+ export type SessionType = {
648
+ accessToken: string
649
+ refreshToken: string
650
+ }
651
+ ```
652
+
653
+ ---
654
+
655
+ ## Context / Auth Hook Pattern
656
+
657
+ ```typescript
658
+ // src/features/auth/hooks/authentication.hook.tsx
659
+ 'use client'
660
+
661
+ import { createContext, useContext, ReactNode } from 'react'
662
+
663
+ type AuthContextData = {
664
+ account: AccountType | null
665
+ isAuthenticated: boolean
666
+ signOut: () => Promise<void>
667
+ }
668
+
669
+ const AuthContext = createContext<AuthContextData>({} as AuthContextData)
670
+
671
+ export function AuthProvider({ children }: { children: ReactNode }) {
672
+ // state and derived values here via useMemo
673
+ return (
674
+ <AuthContext.Provider value={{ account, isAuthenticated, signOut }}>
675
+ {children}
676
+ </AuthContext.Provider>
677
+ )
678
+ }
679
+
680
+ export const useAuth = () => useContext(AuthContext)
681
+ ```
682
+
683
+ ---
684
+
685
+ ## Testing
686
+
687
+ ### Front-end (Next.js)
688
+ - **E2E**: Playwright and/or Cypress
689
+ - Tests live in `e2e/` or `cypress/` at project root
690
+ - Test files: `[feature].spec.ts` (Playwright) or `[feature].cy.ts` (Cypress)
691
+ - Cover critical user flows: auth, main feature interactions, form submissions
692
+
693
+ ### Mobile (Expo / React Native)
694
+ - **E2E**: Detox
695
+ - Tests live in `e2e/` at project root
696
+ - Test files: `[feature].test.ts`
697
+ - Cover critical flows: login, main navigation, core feature interactions
698
+
699
+ ---
700
+
701
+ ## Critical Rules (Non-Negotiable)
702
+
703
+ 1. **`app/` is thin**: `page.tsx` only renders the feature page/screen. No logic, no state, no extra imports.
704
+ 2. **Pages are orchestrators**: Feature pages compose components — they are short. If the page is getting long, extract a component.
705
+ 3. **Components have one job**: If a component grows, split it. Name reflects its single purpose.
706
+ 4. **Always use `cn()`** for class merging — never template literals with conditional classes.
707
+ 5. **Forms always use `Controller`** from react-hook-form — never uncontrolled inputs.
708
+ 6. **API logic in use-cases**: Components and services never call `axios`/`api` directly — always through a use-case function.
709
+ 7. **Zod for all validation**: No manual validation — always define a schema and use `zodResolver`.
710
+ 8. **Type everything**: No `any`. All props, params, and return values must be typed.
711
+ 9. **Shared is truly shared**: Only put in `shared/` what is used by 2+ features.
712
+ 10. **Naming reflects role**: File name + suffix must make the file's purpose immediately obvious.
@@ -0,0 +1,158 @@
1
+ ---
2
+ description: Audit codebase against coding standards and suggest incremental improvements
3
+ globs: **/*
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # task-flow: audit
8
+
9
+ When the user runs `task-flow: audit`, perform a non-destructive analysis of the current project against the coding standards defined in [coding_standards.mdc](mdc:.cursor/rules/coding_standards.mdc).
10
+
11
+ ---
12
+
13
+ ## What This Command Does
14
+
15
+ 1. **Scans the project** to understand its current structure, tech stack, and patterns
16
+ 2. **Compares against coding_standards.mdc** to identify gaps and misalignments
17
+ 3. **Reports a compatibility score** per category
18
+ 4. **Asks the user** which improvements they want to adopt — never assumes or imposes
19
+ 5. **Respects project context** — an existing company project can't be fully restructured overnight
20
+
21
+ ---
22
+
23
+ ## How to Execute
24
+
25
+ ### Step 1 — Explore the project
26
+
27
+ Scan the codebase to understand:
28
+ - Folder structure (is there `src/app`, `features`, `shared`? something else?)
29
+ - Tech stack in use (`package.json`)
30
+ - How routes/pages are organized
31
+ - How components are structured (size, responsibilities)
32
+ - How API calls are made (fetch, axios, react-query, or raw calls in components?)
33
+ - How forms are handled (controlled, uncontrolled, react-hook-form?)
34
+ - How styles are applied (Tailwind, CSS modules, styled-components, inline?)
35
+ - File naming conventions in use
36
+ - Presence of tests and what kind
37
+
38
+ ### Step 2 — Score each category
39
+
40
+ For each category below, rate adherence: **Full / Partial / Missing**
41
+
42
+ | Category | Score | Notes |
43
+ |----------|-------|-------|
44
+ | Folder structure (`app/features/shared`) | | |
45
+ | `app/` is thin (no logic in routes) | | |
46
+ | Feature-based organization | | |
47
+ | Page/Screen as orchestrator (short, composes components) | | |
48
+ | Component single responsibility | | |
49
+ | React Query (service + use-case split) | | |
50
+ | Forms (react-hook-form + zod) | | |
51
+ | `cn()` for class merging | | |
52
+ | File naming conventions | | |
53
+ | TypeScript strictness (no `any`) | | |
54
+ | `DataHandler` pattern (or equivalent) | | |
55
+ | Providers composition | | |
56
+ | E2E tests (Playwright/Cypress/Detox) | | |
57
+
58
+ ### Step 3 — Present findings clearly
59
+
60
+ Show the score table with honest notes. Examples:
61
+ - "Routes contain business logic — pages are 200+ lines"
62
+ - "API calls are made directly in components with `fetch`"
63
+ - "No consistent naming convention found"
64
+ - "Tailwind classes concatenated with template literals instead of `cn()`"
65
+ - "Structure is MVC-like, not feature-based — migration would be significant"
66
+
67
+ ### Step 4 — Ask what to adopt
68
+
69
+ After presenting the report, ask:
70
+
71
+ > "Which of these would you like to improve in this project? I'll generate tasks for each selected item.
72
+ >
73
+ > Keep in mind: some changes (like restructuring to `app/features/shared`) may not be practical for an existing project. I'll suggest incremental, non-breaking improvements where possible."
74
+
75
+ List the improvable items as options. Let the user choose one, several, or all.
76
+
77
+ ### Step 5 — Generate tasks for selected improvements
78
+
79
+ For each selected improvement:
80
+ - Create a task in `.task-flow/tasks.input.txt` (or show the task descriptions so the user can paste them)
81
+ - Tasks must be incremental and safe — never propose wholesale rewrites unless the user explicitly asks
82
+ - Examples of incremental tasks:
83
+ - "Componentize the `UserDashboard` page — extract `UserStatsCard` and `UserActivityList` components"
84
+ - "Replace raw `fetch` calls in `auth.ts` with a `useCreateSession` service using react-query"
85
+ - "Add `cn()` helper and replace string concatenation in `Button` and `Card` components"
86
+ - "Extract `LoginForm` business logic from `login/page.tsx` into `features/auth/pages/login.page.tsx`"
87
+
88
+ ---
89
+
90
+ ## Important Principles
91
+
92
+ - **Non-destructive by default**: Suggest improvements, don't impose them
93
+ - **Incremental**: Prefer "improve this component" over "rewrite this module"
94
+ - **Context-aware**: A greenfield project can adopt everything; a legacy company project needs gradual migration
95
+ - **Honest**: If the project has a completely different architecture that works, say so — don't force standards where they don't fit
96
+ - **No auto-execution**: This command only analyzes and suggests. The user decides what to implement.
97
+
98
+ ---
99
+
100
+ ## Example Output Format
101
+
102
+ ```
103
+ ## task-flow: audit — Coding Standards Report
104
+
105
+ ### Project: [detected project name]
106
+ ### Type: [Next.js / Expo / NestJS / Monorepo]
107
+
108
+ ---
109
+
110
+ ### Compatibility Score
111
+
112
+ | Category | Score | Notes |
113
+ |----------|-------|-------|
114
+ | Folder structure | Partial | Has `src/components` and `src/pages` — no `features/` or `shared/` |
115
+ | app/ is thin | Missing | `pages/dashboard.tsx` is 340 lines with business logic |
116
+ | Feature-based organization | Missing | Code organized by type (components/, hooks/) not by domain |
117
+ | Page as orchestrator | Missing | Pages fetch data and render complex JSX directly |
118
+ | Component single responsibility | Partial | Some components are focused, others handle too much |
119
+ | React Query | Missing | Using `useEffect + fetch` directly in components |
120
+ | Forms (RHF + Zod) | Partial | Using react-hook-form but no Zod validation |
121
+ | cn() for class merging | Missing | Using template literals: `className={\`btn \${isActive ? 'active' : ''}\`}` |
122
+ | File naming | Partial | Inconsistent — mix of PascalCase and kebab-case |
123
+ | TypeScript strictness | Partial | Some `any` usages found |
124
+ | DataHandler pattern | Missing | Manual if/else for loading states in each component |
125
+ | Providers composition | Full | Already uses an AppProviders component |
126
+ | E2E tests | Missing | No test files found |
127
+
128
+ ---
129
+
130
+ ### Overall: 2 Full / 5 Partial / 6 Missing
131
+
132
+ ---
133
+
134
+ ### What would you like to improve?
135
+
136
+ I can generate tasks for any of these. Note that restructuring to `app/features/shared` would be a significant change — I'd suggest tackling it only if you're planning a larger refactor.
137
+
138
+ Quick wins (low effort, high impact):
139
+ - [ ] Add `cn()` helper and apply to all components
140
+ - [ ] Add Zod schemas to existing react-hook-form forms
141
+ - [ ] Extract `DataHandler` component and apply to data-fetching components
142
+
143
+ Medium effort:
144
+ - [ ] Migrate `useEffect + fetch` to React Query (service + use-case pattern)
145
+ - [ ] Split large pages into orchestrator + focused components
146
+
147
+ Larger effort:
148
+ - [ ] Introduce `features/` structure for new features going forward
149
+ - [ ] Add E2E tests with Playwright/Cypress
150
+
151
+ Which would you like me to generate tasks for?
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Principle
157
+
158
+ > **Audit is a conversation, not a verdict. The goal is to help the developer adopt better patterns at their own pace, respecting the reality of the project they're working on.**
@@ -21,6 +21,7 @@ alwaysApply: true
21
21
  - **AI Commands:**
22
22
  - `task-flow: sync` - Sync new tasks from tasks.input.txt
23
23
  - `task-flow: think` - Analyze codebase and suggest new tasks
24
+ - `task-flow: audit` - Audit codebase against coding standards and suggest incremental improvements
24
25
  - `task-flow: run next X` - Work on next X subtasks sequentially
25
26
  - `task-flow: run X` - Work on all pending subtasks of task X (simplified - no "task" needed)
26
27
  - `task-flow: run X,Y` - Work on multiple tasks (comma-separated)
@@ -144,5 +144,12 @@ alwaysApply: true
144
144
  - ✅ All statuses start as "pending"
145
145
  - ✅ Subtask IDs are sequential per task (1, 2, 3...)
146
146
 
147
+ - **Coding Standards:**
148
+ - When generating subtask instructions, always follow the patterns defined in [coding_standards.mdc](mdc:.cursor/rules/coding_standards.mdc)
149
+ - Subtask instructions must reference the correct file structure: `src/app/`, `src/features/`, `src/shared/`
150
+ - Subtask instructions must specify the correct file naming suffix (`.page.tsx`, `.service.tsx`, `.use-case.ts`, etc.)
151
+ - Subtask instructions must enforce the service + use-case split for API calls
152
+ - Subtask instructions must enforce `cn()` for class merging, `Controller` for form inputs, and `zodResolver` for validation
153
+
147
154
  - **Principle:**
148
- > **When generating tasks, be fast and direct. Read tasks.input.txt, generate JSONs and tasks.status.md with proper structure, save files. No exploration needed.**
155
+ > **When generating tasks, be fast and direct. Read tasks.input.txt, generate JSONs and tasks.status.md with proper structure, save files. Always follow coding_standards.mdc patterns in subtask instructions.**
package/CLAUDE.md CHANGED
@@ -21,6 +21,7 @@ This project uses RBIN Task Flow for task management:
21
21
  - **AI Commands**: Use AI-powered commands for task management:
22
22
  - `task-flow: sync` - Synchronize tasks from tasks.input.txt
23
23
  - `task-flow: think` - Analyze code and suggest new tasks
24
+ - `task-flow: audit` - Audit codebase against coding standards and suggest incremental improvements
24
25
  - `task-flow: status` - View current task status
25
26
  - `task-flow: run next X` - Work on next X subtasks
26
27
  - `task-flow: run X` - Execute all pending subtasks of task X (simplified - no "task" needed)
@@ -40,3 +41,7 @@ This project uses RBIN Task Flow for task management:
40
41
  - `.task-flow/.internal/` - Internal system files (ignore)
41
42
 
42
43
  Follow all rules defined in `.cursor/rules/` for consistent development practices.
44
+
45
+ ## Codex
46
+
47
+ When using OpenAI Codex in this repo, it reads **AGENTS.md** at the project root. That file summarizes the same norms (git, commits, comments, RBIN Task Flow) so Codex follows the same conventions as Cursor/Claude. Full details remain in `.cursor/rules/` and this file.
package/README.md CHANGED
@@ -85,6 +85,7 @@ Após inicializar, use estes comandos na IA (Cursor/Claude) para gerenciar taref
85
85
  |---------|--------------|-------------------|
86
86
  | `task-flow: sync` | **Sincroniza** tarefas do arquivo texto com o sistema | Mantém tudo sincronizado automaticamente - adiciona novas, remove deletadas, preserva seu progresso |
87
87
  | `task-flow: think` | **Descobre** tarefas que você esqueceu | Analisa código e sugere tarefas que faltam (testes, refatoração, documentação) |
88
+ | `task-flow: audit` | **Avalia** o quanto o código bate com os padrões de codificação | Analisa a codebase, dá um score por categoria e pergunta quais melhorias você quer adotar — sem impor nada |
88
89
  | `task-flow: status` | **Visualiza** o progresso rapidamente | Vê resumo com tasks completas, em andamento e quantas subtarefas faltam |
89
90
  | `task-flow: run next X` | **Automatiza** o trabalho nas próximas subtarefas | A IA trabalha nas próximas X subtarefas sequencialmente, você só acompanha |
90
91
  | `task-flow: run X` | **Completa** uma tarefa inteira de uma vez | Executa todas as subtarefas de uma tarefa específica (permite trabalho paralelo) |
@@ -428,6 +429,7 @@ After initializing, use these commands in your AI (Cursor/Claude) to automatical
428
429
  |---------|------------|-------------|
429
430
  | `task-flow: sync` | **Sync** tasks from text file with system | Keeps everything synchronized automatically - adds new, removes deleted, preserves your progress |
430
431
  | `task-flow: think` | **Discover** tasks you forgot | Analyzes code and suggests missing tasks (tests, refactoring, documentation) |
432
+ | `task-flow: audit` | **Evaluate** how well your code matches coding standards | Scans the codebase, scores it by category and asks which improvements you want to adopt — never imposes changes |
431
433
  | `task-flow: status` | **Visualize** progress quickly | See summary with completed tasks, in progress, and remaining subtasks |
432
434
  | `task-flow: run next X` | **Automate** work on next subtasks | AI works on next X subtasks sequentially, you just follow along |
433
435
  | `task-flow: run X` | **Complete** an entire task at once | Executes all subtasks of a specific task (allows parallel work) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rbin-task-flow",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "AI-powered task management for Claude and Cursor",
5
5
  "main": "index.js",
6
6
  "bin": {