| name | feature-consistency |
| description | Ensures feature structure consistency and co-location patterns in the web app. Use when (1) creating new features like auth, bookmarks, notifications, (2) editing existing features - adding/modifying components, hooks, utils, types, or schemas, (3) reviewing feature structure for compliance, (4) refactoring to co-locate component-specific logic, (5) adding new components with hooks or utilities. Examples of user requests that should trigger this skill include "add a new feature", "create component with hook", "add form to dashboard", "refactor profile feature", "check feature structure". |
Feature Consistency
When working with features (new or existing), ensure the structure follows these patterns:
Directory Structure
features/[feature-name]/
├── api/
│ ├── mappers/
│ │ └── [feature-name].ts # DB→app type mappers (only place for @codeforge-v2/database imports)
│ └── actions.ts # Server actions with "use server" each one in separate file
├── components/
│ ├── simple-component.tsx # Simple components - no wrapper needed
│ └── complex-component/ # Components with specific hooks/utils
│ ├── index.ts # Barrel: export { ComplexComponent } from "./complex-component"
│ ├── complex-component.tsx # The component itself
│ ├── use-[hook].ts # Component-specific hook (CO-LOCATED)
│ └── [util].ts # Component-specific util (CO-LOCATED)
├── constants/
│ └── [feature-name].ts # Feature-wide constants
├── schemas/
│ └── [feature-name].ts # Feature-wide Zod schemas
├── types/
│ └── [feature-name].ts # Feature-wide types (NO database imports!)
└── index.ts # Barrel exports ONLY
Key Rules
File Naming
index.ts = barrel exports ONLY - never put logic/definitions in index.ts
- Files with logic get descriptive names:
dashboard.ts, filter-options.ts
- Import from named files:
from "../types/dashboard" not from "../types"
Co-location Pattern (CRITICAL)
When a hook/util/schema/constant is used by ONLY ONE component:
- Create a wrapper folder for the component
- Move the hook/util/schema/constant INTO the component's folder as a separate file
- Update imports to use
./ for co-located files
When a hook/util/schema/constant is used by 2+ components:
- Keep it in feature-level
hooks/, utils/, schemas/, or constants/ folder
Types/interfaces follow a different rule:
- Used by ONLY ONE file → inline directly into that file (no separate types file)
- Used by 2+ files → feature-level
types/ folder
Type Decoupling
types/ - App types only, NO database imports
api/mappers/ - ONLY place to import from @codeforge-v2/database
API Pattern
import { ok, err, type Result } from "@/shared/api";
export async function myAction(): Promise<Result<MyData>> {
if (error) return err("Error message");
return ok(data);
}
Checklist When Modifying Features
-
Adding a component with a hook/util?
-
Adding a hook/util used by multiple components?
-
Adding types?
-
Adding schemas?
-
Adding server actions?
Quick Audit
To check if a feature follows the pattern:
grep -r "function\|const\|type\|interface" apps/web/src/features/[name]/*/index.ts
ls apps/web/src/features/[name]/hooks/
ls apps/web/src/features/[name]/utils/
Example Refactoring
Before (wrong):
feature/
├── components/
│ └── my-form.tsx
├── hooks/
│ └── use-my-form.ts # Only used by my-form.tsx - SHOULD BE CO-LOCATED
└── types/
└── index.ts # Has type definitions - SHOULD BE NAMED FILE
After (correct):
feature/
├── components/
│ └── my-form/
│ ├── index.ts # export { MyForm } from "./my-form"
│ ├── my-form.tsx
│ └── use-my-form.ts # CO-LOCATED with its component
└── types/
└── feature.ts # Named file, not index.ts