# Project export: HireSense

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2025
- Tagline: Traditional ATS has limitations, that’s why we wanted to introduce our product that improves the traditional ATS system.
- Devpost: https://devpost.com/software/hiresense-x5j61u
- GitHub: https://github.com/Jaysuun01/AIBerkHack.git
- Video: https://www.youtube.com/embed/2syGxs7GPGg?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Fetch AI: Best Use of Fetch AI)
- Team: 4 GitHub contributor(s) — Eugene Lacatis (11 commits), Jason (10 commits), mceballos123 (8 commits), ThangAttack (1 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 63 recognized source files, 389 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (73 of 73)

```
.gitignore
backend/.gitignore
backend/api_server.py
backend/api/job_postings.py
backend/api/main.py
backend/db/__init__.py
backend/db/supabase_client.py
backend/db/supabase_setup.sql
backend/file_upload_server_simple.py
backend/hiring_agents/__init__.py
backend/hiring_agents/anti_hire_agent.py
backend/hiring_agents/coordinator_agent.py
backend/hiring_agents/decision_agent.py
backend/hiring_agents/intersection_agent.py
backend/hiring_agents/job_parser_agent.py
backend/hiring_agents/llm_client.py
backend/hiring_agents/models.py
backend/hiring_agents/pdf_parser.py
backend/hiring_agents/pro_hire_agent.py
backend/hiring_agents/resume_improvement_agent.py
backend/hiring_agents/resume_parser_agent.py
backend/main.py
backend/private_keys.json
backend/README.md
backend/requirements.txt
frontend/.gitignore
frontend/components.json
frontend/eslint.config.mjs
frontend/next.config.ts
frontend/package.json
frontend/postcss.config.mjs
frontend/README.md
frontend/src/app/applicant/[id]/page.tsx
frontend/src/app/globals.css
frontend/src/app/job-posts/[id]/page.tsx
frontend/src/app/job-posts/page.tsx
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/app/resume-uploads/page.tsx
frontend/src/app/settings/page.tsx
frontend/src/components/dashboard/analysis-in-progress.tsx
frontend/src/components/dashboard/applicant-analysis.tsx
frontend/src/components/dashboard/create-job-form.tsx
frontend/src/components/dashboard/dashboard-content.tsx
frontend/src/components/dashboard/job-details.tsx
frontend/src/components/dashboard/job-posts-content.tsx
frontend/src/components/dashboard/resume-builder-content.tsx
frontend/src/components/dashboard/resume-uploads-content.tsx
frontend/src/components/dashboard/settings-content.tsx
frontend/src/components/dashboard/upload-resume-dialog.tsx
frontend/src/components/dashboard/use-case-selector.tsx
frontend/src/components/ui/alert.tsx
frontend/src/components/ui/badge.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/card.tsx
frontend/src/components/ui/dialog.tsx
frontend/src/components/ui/dropdown-menu.tsx
frontend/src/components/ui/input.tsx
frontend/src/components/ui/label.tsx
frontend/src/components/ui/progress.tsx
frontend/src/components/ui/radial-progress.tsx
frontend/src/components/ui/separator.tsx
frontend/src/components/ui/sheet.tsx
frontend/src/components/ui/sidebar.tsx
frontend/src/components/ui/skeleton.tsx
frontend/src/components/ui/switch.tsx
frontend/src/components/ui/table.tsx
frontend/src/components/ui/textarea.tsx
frontend/src/components/ui/tooltip.tsx
frontend/src/hooks/use-mobile.ts
frontend/src/lib/script.ts
frontend/src/lib/utils.ts
frontend/tsconfig.json
```

### Dependencies

- backend/requirements.txt: aiohttp@>=3.8.0, fastapi@>=0.104.0, pydantic@>=2.0.0, PyPDF2@>=3.0.0, python-dotenv@>=1.0.0, python-multipart@>=0.0.6, supabase@>=2.0.0, uagents@>=0.4.0, uvicorn@>=0.24.0
- frontend/package.json: @eslint/eslintrc@^3, @radix-ui/react-dialog@^1.1.14, @radix-ui/react-dropdown-menu@^2.1.15, @radix-ui/react-label@^2.1.7, @radix-ui/react-progress@^1.1.7, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slot@^1.2.3, @radix-ui/react-switch@^1.2.5, @radix-ui/react-tooltip@^1.2.7, @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9, eslint-config-next@15.3.4, framer-motion@^12.18.1, lucide-react@^0.522.0, next@15.3.4, react@^19.0.0, react-dom@^19.0.0, react-dropzone@^14.3.8, tailwind-merge@^3.3.1, tailwindcss@^4, tw-animate-css@^1.3.4, typescript@^5

### Recent commits (newest first)

- Merge pull request #8 from mceballos123/new-mod
- Changed model to Gemini
- Merge pull request #4 from mceballos123/top-canidates
- Implement Top Candidates & API Failure Protection
- Merge pull request #3 from mceballos123/use-case-1
- Implement dynamic career field recommendations based on major
- Merge pull request #2 from mceballos123/use-case-1
- feat: Add Use Case 1 - Resume Builder for students
- Merge pull request #1 from mceballos123/terminal-continous
- Fix: Prevent api_server.py from restarting automatically
- Add Docker containerization for all services
- Merge pull request #7 from mceballos123/main
- fix: resolve API server port conflicts and add upload debugging
- Merge branch 'main' of https://github.com/Jaysuun01/AIBerkHack
- Don't ask
- Create README.md
- update on front
- Add real-time multi-agent hiring system with WebSocket visualization
- update front and back end
- Merge pull request #6 from Jaysuun01/supabase-connection

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/requirements.txt

```
# Core web framework and server
fastapi>=0.104.0
uvicorn>=0.24.0
python-multipart>=0.0.6

# Agent framework
uagents>=0.4.0

# Data models and validation
pydantic>=2.0.0

# HTTP client for API calls
aiohttp>=3.8.0

# PDF processing
PyPDF2>=3.0.0

# Database client
supabase>=2.0.0

# Environment variables
python-dotenv>=1.0.0
```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-dropdown-menu": "^2.1.15",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-progress": "^1.1.7",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-switch": "^1.2.5",
    "@radix-ui/react-tooltip": "^1.2.7",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "framer-motion": "^12.18.1",
    "lucide-react": "^0.522.0",
    "next": "15.3.4",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-dropzone": "^14.3.8",
    "tailwind-merge": "^3.3.1"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.3.4",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.3.4",
    "typescript": "^5"
  }
}

```

### backend/main.py

```python
"""
Main Hiring System
=================

Main execution file for the uAgents hiring system.
Coordinates all agents and provides the entry point.
"""

import asyncio
from uagents import Agent, Context, Protocol

# from uagents.setup import fund_agent_if_low
from hiring_agents import (
    JobParserAgent,
    ResumeParserAgent,
    IntersectionAgent,
    ProHireAgent,
    AntiHireAgent,
    DecisionAgent,
)
from hiring_agents.models import (
    JobParseRequest,
    ResumeParseRequest,
    IntersectionRequest,
    DebateRequest,
    DecisionRequest,
    JobParseResponse,
    ResumeParseResponse,
    IntersectionResponse,
    DebateResponse,
    DecisionResponse,
    TranscriptEntry,
    FinalResult,
)


# Main Coordinator
class HiringCoordinator(Agent):
    def __init__(self, event_emitter=None, base_port=8007):
        super().__init__("hiring_coordinator", base_port, "coordinator_seed")
        # fund_agent_if_low(self.wallet.address())  # Disabled for local operation

        # Store event emitter for real-time updates
        self.event_emitter = event_emitter

        # Store agent addresses
        self.job_parser_address = None
        self.resume_parser_address = None
        self.intersection_address = None
        self.pro_hire_address = None
        self.anti_hire_address = None
        self.decision_address = None

        # Store results
        self.job_analysis = None
        self.resume_analysis = None
        self.intersection_analysis = None
        self.pro_arguments = []
        self.anti_arguments = []
        self.final_decision = None

        # Add completion flags
        self.job_complete = False
        self.resume_complete = False
        self.intersection_complete = False
        self.debate_complete = False
        self.decision_complete = False

        # Add flag to prevent duplicate requests
        self.initial_requests_sent = False

        # Store initial data
        self.resume_content = None
        self.job_description = None
        self.candidate_name = None
        self.job_title = None

        self.protocol = Protocol()

        # Timer to send initial requests after startup
        @self.on_interval(period=3.0)
        async def send_initial_requests(ctx: Context):
            if (
                not self.initial_requests_sent
                and self.job_description
                and self.resume_content
            ):
                print("🤖 Starting Hiring Evaluation Process")
                print("=" * 60)
                print("\n📋 STEP 1: Parsing Job and Resume")

                # Send initial requests
                job_request = JobParseRequest(
                    job_description=self.job_description, job_title=self.job_title
                )
                await ctx.send(self.job_parser_address, job_request)

                resume_request = ResumeParseRequest(
                    resume_content=self.resume_content,
                    candidate_name=self.candidate_name,
                )
                await ctx.send(self.resume_parser_address, resume_request)

                # Mark as sent to prevent duplicates
                self.initial_requests_sent = True

        # Handle responses from each agent
        @self.protocol.on_message(model=JobParseResponse)
        async def handle_job_response(ctx: Context, sender: str, msg: JobParseResponse):
            self.job_analysis = msg
            self.job_complete = True
            print(f"Job Analysis: {msg.analysis}")

            # Emit WebSocket event
            if self.event_emitter:
                await self.event_emitter("Job Parser Agent", msg.analysis, "parsing")

            await self._check_and_proceed(ctx)

        @self.protocol.on_message(model=ResumeParseResponse)
        async def handle_resume_response(
            ctx: Context, sender: str, msg: ResumeParseResponse
        ):
            self.resume_analysis = msg
            self.resume_complete = True
            print(f"Resume Analysis: {msg.analysis}")

            # Emit WebSocket event
            if self.event_emitter:
                await self.event_emitter("Resume Parser Agent", msg.analysis, "parsing")

            await self._check_and_proceed(ctx)

        @self.protocol.on_message(model=IntersectionResponse)
        async def handle_intersection_response(
            ctx: Context, sender: str, msg: IntersectionResponse
        ):
            self.intersection_analysis = msg
            self.intersection_complete = True
            print(f"\n🔍 STEP 2: Evaluating Intersection")
            print(f"Intersection Analysis: {msg.analysis}")
            print(f"Overall Compatibility: {msg.overall_compatibility:.2f}")

            # Emit WebSocket event
            if self.event_emitter:
                await self.event_emitter(
                    "Intersection Evaluator", msg.analysis, "evaluation", "evaluation"
                )

            await self._start_debate(ctx)

        @self.protocol.on_message(model=DebateResponse)
        async def handle_debate_response(
            ctx: Context, sender: str, msg: DebateResponse
        ):
            if msg.position == "pro":
                self.pro_arguments.append(msg)
                print(f"Pro-Hire Round {len(self.pro_arguments)}: {msg.argument}")

                # Emit WebSocket event
                if self.event_emitter:
                    await self.event_emitter(
                        "Pro-Hire Advocate", msg.argument, "debate", "pro"
                    )
            else:
                self.anti_arguments.append(msg)
                print(f"Anti-Hire Round {len(self.anti_arguments)}: {msg.argument}")

                # Emit WebSocket event
                if self.event_emitter:
                    await self.event_emitter(
                        "Anti-Hire Advocate", msg.argument, "debate", "anti"
                    )

            await self._continue_debate(ctx)

        @self.protocol.on_message(model=DecisionResponse)

[truncated — 11222 more characters]
```

### backend/api/main.py

```python
from fastapi import FastAPI
from api.job_postings import router as job_postings_router
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Or specify your frontend URL
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.include_router(job_postings_router)
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

```

### frontend/src/app/page.tsx

```typescript
import { DashboardContent } from "@/components/dashboard/dashboard-content"

export default function Dashboard() {
  return <DashboardContent />
}

```

### frontend/src/app/layout.tsx

```typescript
import type React from "react"
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import "./globals.css"
import { AppSidebar } from "@/components/ui/sidebar"

const inter = Inter({ subsets: ["latin"] })

export const metadata: Metadata = {
  title: "AI Recruiter Dashboard",
  description: "AI-powered resume evaluation system for recruiters",
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className="dark">
      <body className="bg-background text-foreground">
        <AppSidebar />
        <main className="ml-[100px] p-8">{children}</main>
      </body>
    </html>
  )
}



```

### frontend/src/app/job-posts/page.tsx

```typescript
import { JobPostsContent } from "@/components/dashboard/job-posts-content"

export default function JobPosts() {
  return <JobPostsContent />
}
```

### frontend/src/app/settings/page.tsx

```typescript
import { SettingsContent } from "@/components/dashboard/settings-content"

export default function SettingsPage() {
  return <SettingsContent />
}
```

### frontend/src/app/resume-uploads/page.tsx

```typescript
// app/resume-uploads/page.tsx
import { ResumeUploadsContent } from "@/components/dashboard/resume-uploads-content"

export default function ResumeUploadsPage() {
  return <ResumeUploadsContent />
}

```

### frontend/src/app/job-posts/[id]/page.tsx

```typescript
import { JobDetails } from "@/components/dashboard/job-details"

export default function JobDetailsPage({
  params,
}: {
  params: { id: string }
}) {
  return <JobDetails id={params.id} />
}







```

[52 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]