Automated system that analyzes error stack traces and generates Pull Requests with fixes using Artificial Intelligence.
Live Project: Bug to PR π
- Overview
- Architecture
- Backend - Design Patterns
- Frontend - Design Patterns
- Project Structure
- Technologies
- Configuration and Execution
- API Endpoints
Bug to PR Agent is a full-stack application that automates the bug fixing process. The system:
- Receives an error stack trace from the user
- Analyzes the repository code on GitHub in the specified branch
- Uses AI (Gemini or OpenAI) to generate a fix patch
- Creates a new branch and applies the patch
- Opens a Pull Request automatically
Stack Trace β Parser β AI Analysis β Patch Generation β GitHub PR Creation
The project follows a layered architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (Next.js) β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Pages ββ β Services ββ β API β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP/REST
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Backend (Express) β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Routes ββ βControllersββ βUse Casesβ β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β βServices β βRepositoryβ βContracts β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Database (PostgreSQL + Prisma) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The backend was developed following Clean Architecture and SOLID principles, utilizing various design patterns:
Location: backend/src/modules/*/factory/
Purpose: Centralize controller creation, ensuring correct dependency injection.
Example:
// auth.factory.ts
export function makeCreateUserController() {
return new CreateUserController(authContainer.createUserUseCase);
}Benefits:
- Isolation of creation logic
- Facilitates testing (mocks can be injected)
- Single Responsibility Principle
Location: backend/src/modules/*/di/
Purpose: Manage dependencies and ensure singleton instances when necessary.
Example:
// auth.container.ts
class AuthContainer {
get repository(): IAuthContract {
if (!this._repository) {
this._repository = new PrismaAuthRepository(prisma);
}
return this._repository;
}
get createUserUseCase(): CreateUserUseCase {
return new CreateUserUseCase(
this.repository,
this.hashService,
this.tokenEncrypter
);
}
}Benefits:
- Centralized dependency control
- Lazy loading (instances created on demand)
- Facilitates maintenance and testing
Location: backend/src/modules/*/repository/
Purpose: Abstract data access, allowing implementation swap without affecting business logic.
Structure:
Contract (Interface) β Repository (Implementation) β Database
Example:
// Contract
export interface IAuthContract {
createUser: (dto: CreateUserDto) => Promise<CreateUserResponseDto>;
getUserByEmail: (email: string) => Promise<CreateSessionUserDto | null>;
}
// Implementation
export class PrismaAuthRepository implements IAuthContract {
constructor(private readonly prisma: PrismaClient) {}
// ... implementation
}Benefits:
- Decoupling of data layer
- Facilitates testing (mocks)
- Allows swapping ORM without affecting use cases
Location: backend/src/modules/*/use-cases/
Purpose: Encapsulate business rules in specific use cases.
Characteristics:
- Each use case has a single responsibility
- Receives DTOs as input
- Returns
Either<Error, Success>(Functional Error Handling) - Doesn't know implementation details (HTTP, Database, etc)
Example:
export class CreateUserUseCase {
constructor(
private readonly authContract: IAuthContract,
private readonly hashGenerator: HashGenerator,
private readonly tokenEncrypter: TokenEncrypter
) {}
async execute(dto: CreateUserDto): Promise<Either<UserAlreadyExistsError, CreateUserResponseDto>> {
// Pure business logic
}
}Benefits:
- Testability
- Reusability
- Maintainability
Location: backend/src/modules/bug-to-pr/strategies/
Purpose: Allow swapping algorithms (AI providers) at runtime.
Structure:
IAIStrategy (Interface)
βββ GeminiStrategy
βββ OpenAIStrategy
βββ MockStrategy (for tests)
Example:
// Factory that chooses the strategy
export class AIStrategyFactory {
static create(provider?: AIProvider): IAIStrategy {
switch (provider || env.AI_PROVIDER) {
case 'gemini': return new GeminiStrategy(env.GEMINI_API_KEY);
case 'openai': return new OpenAIStrategy(env.OPENAI_API_KEY);
}
}
}
// Usage in Service
const strategy = AIStrategyFactory.create(dto.aiProvider);
const aiService = new AIService(strategy);Benefits:
- Extensibility (easy to add new providers)
- Open/Closed Principle
- Testability
Location: backend/src/modules/*/contract/
Purpose: Define clear contracts between layers, following Interface Segregation Principle.
Example:
export interface IGithubService {
getFileContent(...): Promise<GitHubFileContent>;
createBranch(...): Promise<void>;
getUserRepositories(...): Promise<GitHubRepositoryDto[]>;
// ... specific methods
}Benefits:
- Decoupling
- Testability (mocks)
- Implicit documentation
Location: backend/src/@types/either.ts
Purpose: Handle errors functionally, without exceptions.
Structure:
type Either<L, R> = Left<L, R> | Right<L, R>
// Left = Error
// Right = SuccessExample:
async execute(dto: Dto): Promise<Either<GithubError, SuccessDto>> {
if (error) {
return left(new GithubError(400));
}
return right(successData);
}
// Usage in Controller
if (result.isLeft()) {
return response.status(result.value.statusCode).json({
error: result.value.message
});
}Benefits:
- Explicit errors in type
- Forces error handling
- More predictable code
Location: backend/src/modules/*/dto/
Purpose: Transfer data between layers in a typed and validated way.
Example:
export interface GeneratePRDto {
stackTrace: string;
owner: string;
repo: string;
branch: string;
userId?: string;
aiProvider?: 'gemini' | 'openai';
}Benefits:
- Type safety
- Centralized validation
- Documentation
Location: backend/src/modules/auth/middleware/
Purpose: Intercept requests for authentication/authorization.
Example:
export const authenticate = (req, res, next) => {
// Validates JWT token
// Injects user into request
next();
}Location: backend/src/modules/*/validator/ and backend/src/shared/zod-validator.middleware.ts
Purpose: Validate input data using Zod schemas.
Example:
const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
// Middleware
export const validate = (schema: ZodSchema) => {
return (req, res, next) => {
const validatedData = schema.parse(req.body);
req.body = validatedData;
next();
};
};The frontend was developed following Component-Based Architecture and Separation of Concerns:
Location: frontend/src/services/
Purpose: Abstract API communication, centralizing HTTP logic.
Structure:
ApiClient (base) β Services (specific) β Components
Example:
// api.ts - Base client
class ApiClient {
private async request<T>(endpoint: string, options: RequestInit) {
// Centralized HTTP logic
// JWT injection
// Error handling
}
}
// auth.ts - Specific service
class AuthService {
async login(credentials: LoginCredentials): Promise<User> {
const response = await api.post("/auth/sessions", credentials);
// Data transformation
return mappedUser;
}
}Benefits:
- Reusability
- Maintainability
- Testability
Location: frontend/src/hooks/
Purpose: Encapsulate state logic and side effects.
Examples:
useRepositories()- Manages GitHub repositories stateuseAnalysis()- Manages stack trace analysis flowuseLocalStorage()- Abstracts localStorageuseScroll()- Manages scroll behavior
Example:
export function useRepositories() {
const [repositories, setRepositories] = useState([]);
const [isLoading, setIsLoading] = useState(true);
// Fetch logic, grouping, etc.
return { repositories, owners, repositoriesByOwner, isLoading, refetch };
}Benefits:
- Logic reusability
- Separation of concerns
- Testability
Location: frontend/src/components/
Structure:
shared/ β Reusable components (Header, Footer, AuthGuard)
features/ β Feature-specific components
βββ analysis/ β Analysis-related components
βββ hero/ β Landing page components
βββ home/ β Home components
ui/ β Base components (Button, Input, Card)
Example:
// Component composition
<AuthGuard>
<Header variant="app" />
<AnalysisForm />
<Footer />
</AuthGuard>Benefits:
- Reusability
- Maintainability
- Isolated testability
Location: frontend/src/components/shared/auth-guard.tsx
Purpose: Protect routes that require authentication.
Example:
export function AuthGuard({ children }: AuthGuardProps) {
const isAuthenticated = authService.isAuthenticated();
if (!isAuthenticated) {
router.push(ROUTES.LOGIN);
return null;
}
return <>{children}</>;
}Usage:
<AuthGuard>
<DashboardPage />
</AuthGuard>Location: frontend/src/validators/
Purpose: Validate forms on the frontend, mirroring backend validations.
Example:
export const createUserSchema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "Password must be at least 8 characters"),
githubToken: z.string().min(1, "GitHub Token is required"),
});Benefits:
- Consistent frontend/backend validation
- Type safety
- Standardized error messages
Location: frontend/src/types/
Purpose: Centralize type definitions.
Structure:
types/
βββ auth.ts
βββ analysis.ts
βββ api.ts
βββ repositories.ts
βββ index.ts (barrel export)
Benefits:
- Type safety
- Autocomplete
- Documentation
Location: frontend/src/constants/
Purpose: Centralize constant values (routes, configurations).
Example:
export const ROUTES = {
HOME: "/",
LOGIN: "/login",
DASHBOARD: "/dashboard",
HISTORY: "/history",
} as const;Structure:
features/
analysis/
βββ analysis-form.tsx
βββ history-list.tsx
βββ pr-result.tsx
βββ index.ts
Benefits:
- Domain-based organization
- Facilitates code location
- Scalability
backend/
βββ src/
β βββ @types/ # Custom types (Either)
β βββ config/ # Configurations (env)
β βββ models/ # Prisma Client
β βββ modules/ # Application modules
β β βββ auth/ # Authentication module
β β β βββ contract/ # Interfaces
β β β βββ controllers/ # HTTP Controllers
β β β βββ cryptography/# Cryptography (JWT, AES)
β β β βββ di/ # Dependency Injection
β β β βββ dto/ # Data Transfer Objects
β β β βββ errors/ # Custom errors
β β β βββ factory/ # Factory Pattern
β β β βββ middleware/ # Express Middleware
β β β βββ repository/ # Repository Pattern
β β β βββ routes/ # Express Routes
β β β βββ use-cases/ # Business Logic
β β β βββ validator/ # Zod Schemas
β β βββ bug-to-pr/ # Main module
β β βββ constants/ # Constants
β β βββ contract/ # Interfaces
β β βββ controllers/ # HTTP Controllers
β β βββ di/ # Dependency Injection
β β βββ dto/ # Data Transfer Objects
β β βββ errors/ # Custom errors
β β βββ factory/ # Factory Pattern
β β βββ repository/ # Repository Pattern
β β βββ routes/ # Express Routes
β β βββ services/ # Domain Services
β β βββ strategies/ # Strategy Pattern (AI)
β β βββ use-cases/ # Business Logic
β β βββ utils/ # Utilities
β β βββ validator/ # Zod Schemas
β βββ routes/ # Route binding
β βββ shared/ # Shared code
β βββ server.ts # Entry point
βββ prisma/
β βββ schema.prisma # Database schema
βββ package.json
frontend/
βββ app/ # Next.js App Router
β βββ dashboard/ # Dashboard page
β βββ history/ # History page
β βββ login/ # Login page
β βββ page.tsx # Landing page
βββ src/
β βββ components/ # React Components
β β βββ features/ # Feature components
β β βββ shared/ # Shared components
β β βββ ui/ # Base components (shadcn)
β βββ config/ # Configurations
β βββ constants/ # Constants
β βββ hooks/ # Custom Hooks
β βββ lib/ # Utilities
β βββ services/ # API Services
β βββ types/ # TypeScript Types
β βββ validators/ # Zod Validators
βββ package.json
- Runtime: Node.js
- Framework: Express.js
- Language: TypeScript
- ORM: Prisma
- Database: PostgreSQL
- Validation: Zod
- Authentication: JWT (jsonwebtoken)
- Cryptography: bcryptjs, AES (token encryption)
- AI Providers:
- Google Gemini (@google/generative-ai)
- OpenAI (openai)
- GitHub API: @octokit/rest
- Testing: Vitest
- Code Quality: TypeScript strict mode
- Framework: Next.js 16 (App Router)
- Language: TypeScript
- UI Library: React 19
- Styling: Tailwind CSS 4
- UI Components: Shadcn UI (Radix UI)
- Icons: Lucide React
- Animations: Framer Motion, GSAP
- Forms: React (controlled components)
- Validation: Zod
- State Management: React Hooks (useState, useEffect, useCallback)
- HTTP Client: Fetch API (custom ApiClient)
- Notifications: Sonner
- Theming: next-themes (dark mode)
- Node.js 20+
- PostgreSQL
- pnpm (or npm/yarn)
cd backend
# Install dependencies
npm install
# Configure environment variables
cp .env.example .env
# Edit .env with your credentials
# Configure database
npm run prisma:generate
npm run prisma:migrate
# Run in development
npm run dev
# Build for production
npm run build
npm startcd frontend
# Install dependencies
pnpm install
# Configure environment variables
# Create .env.local with NEXT_PUBLIC_API_URL
# Run in development
pnpm dev
# Build for production
pnpm build
pnpm startBackend (.env):
NODE_ENV=development
PORT=3001
JWT_SECRET=your-secret-key
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
ENCRYPTION_KEY=your-32-char-encryption-key
AI_PROVIDER=gemini
GEMINI_API_KEY=your-gemini-key
OPENAI_API_KEY=your-openai-keyFrontend (.env.local):
NEXT_PUBLIC_API_URL=http://localhost:3001/api/v1POST /api/v1/auth/users- Create userPOST /api/v1/auth/sessions- Login
POST /api/v1/bug-to-pr/generate- Generate PR from stack traceGET /api/v1/bug-to-pr/repositories- List user repositoriesGET /api/v1/bug-to-pr/history- History of generated PRs
GET /api/v1/health- Server status
- Single Responsibility: Each class/component has one responsibility
- Open/Closed: Extensible via Strategy Pattern
- Liskov Substitution: Well-defined interfaces
- Interface Segregation: Specific contracts
- Dependency Inversion: Dependencies via interfaces
- Well-defined layers: Controllers β Use Cases β Services/Repository
- Framework independence: Business logic doesn't depend on Express
- Testability: Easy to create mocks and unit tests
- UI independence: Use cases don't know about HTTP
- Shared validations (Zod schemas)
- Reusable services
- Custom hooks
- Composed components
- Either Pattern: Chosen for functional error handling, avoiding unhandled exceptions
- Repository Pattern: Allows swapping ORM without affecting business logic
- Strategy Pattern for AI: Facilitates adding new AI providers
- DI Containers: Centralizes dependencies, facilitates testing
- Zod in Frontend and Backend: Ensures consistent validation
ISC
Gabriel Melo, Lucas Nery, Luiz Renan and Eduardo Zago Project developed for Hackathon Base 2025.