feature-module-architect
SKILL.md
Feature Module Architect
Quick Start
This skill scaffolds feature modules following project architecture:
- Feature structure: Standard directory layout with components, hooks, services, types, utils
- File size limit: 500 LOC maximum per file (hard limit)
- Colocation: Keep related code together within feature directory
- Public API: Export only what other features need via
index.ts
When to Use
- Creating new feature modules
- Refactoring files exceeding 500 LOC
- Organizing scattered feature code
- Need feature architecture guidance
Standard Feature Structure
src/features/{feature-name}/
├── components/ # React components for this feature
│ ├── FeatureComponent.tsx
│ ├── FeatureComponent.test.tsx
│ └── index.ts
├── hooks/ # Custom React hooks
│ ├── useFeatureData.ts
│ ├── useFeatureData.test.ts
│ └── index.ts
├── services/ # Business logic and API calls
│ ├── featureService.ts
│ ├── featureService.test.ts
│ └── index.ts
├── types/ # TypeScript interfaces and types
│ ├── feature.types.ts
│ └── index.ts
├── utils/ # Pure utility functions
│ ├── featureUtils.ts
│ ├── featureUtils.test.ts
│ └── index.ts
└── index.ts # Public API (exports for other features)
Existing Feature Examples
AI Generation (src/features/ai-generation/):
- Components: GenerationForm, GenerationHistory
- Hooks: useGeneration, useAIProvider
- Services: generationService, aiGatewayClient
- Types: GenerationRequest, GenerationResponse
Project Management (src/features/project-management/):
- Components: ProjectCard, ProjectList, ProjectForm
- Hooks: useProjects, useProjectMutations
- Services: projectService
- Types: Project, ProjectMetadata
World Building (src/features/world-building/):
- Components: WorldMap, LocationEditor
- Hooks: useWorldState
- Services: worldService
- Types: WorldElement, Location
File Size Enforcement
Hard Limit: 500 LOC per file (from AGENTS.md)
Check file sizes:
# Count lines in all TypeScript files
wc -l src/features/**/*.ts src/features/**/*.tsx
# Find files exceeding 500 LOC
find src/features -name "*.ts" -o -name "*.tsx" | xargs wc -l | awk '$1 > 500'
Refactoring Strategy
When a file exceeds 500 LOC, split by responsibility:
Before (600 LOC component):
// ProjectDashboard.tsx (600 LOC) ❌
export const ProjectDashboard: React.FC = () => {
// 100 LOC of state/hooks
// 200 LOC of handlers
// 300 LOC of JSX
};
After (split into 3 files, each <200 LOC):
// useProjectDashboard.ts (100 LOC)
export function useProjectDashboard() {
// State and effects
}
// projectDashboardHandlers.ts (100 LOC)
export function createHandlers(projects: Project[]) {
// Event handlers
}
// ProjectDashboard.tsx (150 LOC)
export const ProjectDashboard: React.FC = () => {
const state = useProjectDashboard();
const handlers = createHandlers(state.projects);
return <div>{/* JSX */}</div>;
};
Colocation Principle
Keep related code together:
✅ Good - Feature-specific code within feature:
src/features/ai-generation/
├── components/GenerationForm.tsx
├── hooks/useGeneration.ts # Only used by GenerationForm
└── types/generation.types.ts # Only used by this feature
❌ Bad - Scattered across global directories:
src/
├── components/GenerationForm.tsx
├── hooks/useGeneration.ts # Generic hooks directory
└── types/generation.types.ts # Generic types directory
Public API Pattern
Each feature exports a public API via index.ts:
// src/features/ai-generation/index.ts
export { GenerationForm } from './components/GenerationForm';
export { useGeneration } from './hooks/useGeneration';
export type {
GenerationRequest,
GenerationResponse,
} from './types/generation.types';
// Keep internal utilities private (don't export)
Usage by other features:
// ✅ Import from feature public API
import { GenerationForm, useGeneration } from '@/features/ai-generation';
// ❌ Import from internal paths (breaks encapsulation)
import { GenerationForm } from '@/features/ai-generation/components/GenerationForm';
Component Organization
Small Components (<100 LOC)
Keep component and styles together:
// Button.tsx (80 LOC)
export const Button: React.FC<ButtonProps> = ({ children, ...props }) => {
return (
<button
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
{...props}
>
{children}
</button>
);
};
Large Components (>100 LOC)
Extract hooks and handlers:
// useProjectForm.ts
export function useProjectForm(initialValues: Project) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (field: string, value: any) => {
setValues(prev => ({ ...prev, [field]: value }));
};
return { values, errors, handleChange };
}
// ProjectForm.tsx (<150 LOC)
export const ProjectForm: React.FC<ProjectFormProps> = ({ initialValues }) => {
const { values, errors, handleChange } = useProjectForm(initialValues);
return (
<form>
{/* JSX using values, errors, handleChange */}
</form>
);
};
Scaffolding Checklist
When creating a new feature:
- Create feature directory:
src/features/{feature-name}/ - Add
components/with index.ts - Add
hooks/with index.ts (if needed) - Add
services/with index.ts - Add
types/with index.ts - Add
utils/with index.ts (if needed) - Create root
index.tswith public API exports - Add test files next to implementation files
- Verify no file exceeds 500 LOC
- Update feature integration points
Common Patterns
Service Pattern
// src/features/projects/services/projectService.ts
import { db } from '@/lib/database';
import type { Project } from '../types/project.types';
export const projectService = {
async getAll(): Promise<Project[]> {
return db.select().from('projects');
},
async getById(id: string): Promise<Project | null> {
const result = await db.select().from('projects').where('id', id);
return result[0] ?? null;
},
async create(data: Omit<Project, 'id'>): Promise<Project> {
const id = crypto.randomUUID();
await db.insert({ id, ...data }).into('projects');
return { id, ...data };
},
};
Hook Pattern
// src/features/projects/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { projectService } from '../services/projectService';
export function useProjects() {
return useQuery({
queryKey: ['projects'],
queryFn: () => projectService.getAll(),
});
}
Type Pattern
// src/features/projects/types/project.types.ts
export interface Project {
id: string;
title: string;
description?: string;
genre: ProjectGenre;
createdAt: number;
updatedAt: number;
}
export type ProjectGenre = 'fantasy' | 'scifi' | 'mystery' | 'romance';
export interface ProjectMetadata {
wordCount: number;
chapterCount: number;
}
Success Criteria
- All files under 500 LOC
- Feature code colocated within feature directory
- Public API clearly defined in root
index.ts - Test files next to implementation files
- Consistent directory structure across features
- No cross-feature internal imports
References
- AGENTS.md - Colocation principle and file size limits
- Existing features in
src/features/- Reference implementations
Weekly Installs
5
Repository
d-oit/do-novelist-aiFirst Seen
Jan 31, 2026
Security Audits
Installed on
opencode5
cursor5
claude-code4
github-copilot4
codex4
amp4