# Project export: Synergy

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: Cal Hacks 12.0
- Tagline: Find your next co-founder.
- Devpost: https://devpost.com/software/synergy-07vh3t
- GitHub: https://github.com/tanmaygarg901/Synergy/
- Team: 1 GitHub contributor(s) — tanmaygarg901 (11 commits)

## Devpost submission (written by the team)

### Overview

Finding the right co-founder is one of the hardest challenges in entrepreneurship. We wanted to build a platform that could understand what founders are looking for through natural conversation, then intelligently match them with complementary teammates based on skills, values, and needs rather than just surface-level criteria. What: Synergy is an AI co-founder matching platform that engages founders through guided conversation and extracts structured profiles to find their perfect teammates. Users interact through a dual-interface approach: a freeform prompt or a persistent chatbot that naturally collects information about background, skills, and collaboration preferences. The platform then uses semantic search and intelligent matching to recommend complementary co-founders, with the UI visualizing compatibility scores and team suggestions. Build/Tech Framework: We paired a polished Next.js 14 frontend with a Flask-based AI backend. The frontend uses shadcn/ui, Tailwind CSS, react-chatbot-kit, and Lucide icons for a high-end conversational interface. The Flask backend leverages Groq's Llama-3 models: an 8B instant model for rapid chat and a 70B JSON-mode model for profile extraction. We generate embeddings locally with SentenceTransformers and persist them into ChromaDB, creating a semantic knowledge base that powers our matching algorithm. Project Challenges: Building intelligent matchmaking proved complex. We had to guide users through structured conversation without feeling robotic, extract clean JSON profiles from freeform dialogue, and move beyond simple vector similarity to recommend genuinely complementary teams. Balancing speed with quality by using the faster 8B model for chat and the 70B model for profile extraction also required careful engineering. Accomplishments: We built a system that feels genuinely intelligent rather than a generic GPT wrapper. Our matchmaking layer normalizes roles, applies semantic filters, and re-ranks candidates based on team composition. The adaptive questioning naturally collects needed information, and watching conversational data transform into structured profiles and meaningful team recommendations is satisfying. The UI successfully makes the entire process transparent and easy to understand.

### What we learned

: We learned that AI systems need discipline and structure to be useful. A good language model isn't enough; you need conversational policies, validation, and careful post-processing of outputs. Building a good UX around AI is hard, managing state across multiple entry points requires careful thought, and combining multiple models with different strengths creates better outcomes than relying on a single approach.

### What's next

? Well, we're exploring richer founder profiles capturing working styles and values, real-time availability filtering, feedback loops to improve matches over time, and team formation workflows for newly matched founders. We're also investigating explainability features so founders understand not just who they're matched with, but why.

## README (from the GitHub repository)

# Synergy - AI-Powered Co-Founder Matching Platform

**CalHacks 12.0 Project**

Synergy is an intelligent co-founder matching platform that uses conversational AI to understand your skills, interests, and needs, then matches you with complementary team members. Through natural conversation, Synergy learns about you and suggests both individual collaborators and complete team combinations.

## ✨ Key Features

- 🤖 **Conversational AI** - Natural chat interface powered by Groq's Llama 3.1
- 🎯 **Smart Matching** - Vector similarity search with semantic understanding
- 👥 **Team Suggestions** - AI-generated team combinations, not just individual matches
- 📊 **Compatibility Scores** - See how well each match fits your needs (85-100%)
- ✨ **Beautiful UI** - Modern, animated interface with smooth transitions
- 🚀 **Fast & Real-time** - Instant responses with sub-second inference
- 🔄 **Adaptive Conversation** - AI asks follow-up questions only when needed

## Tech Stack

### Frontend
- **Next.js 14** - React framework
- **shadcn/ui** - Modern UI components
- **Tailwind CSS** - Styling
- **react-chatbot-kit** - Chat interface
- **Lucide Icons** - Icon system

### Backend
- **Flask** - Python web framework with CORS support
- **Groq AI** - Ultra-fast LLM inference
  - `llama-3.1-8b-instant` for real-time chat (200ms response time)
  - `llama-3.1-70b-versatile` for profile extraction & team reasoning
- **ChromaDB** - Vector database for semantic search
- **sentence-transformers** - Local embeddings (all-MiniLM-L6-v2)
- **Python 3.9+** - Modern Python with type hints

## Project Structure

```
calhacks 12.0/
├── frontend/              # Next.js application
│   ├── app/              # App router pages
│   ├── components/       # React components
│   │   ├── ui/          # shadcn/ui components
│   │   └── chatbot/     # Chatbot configuration
│   └── lib/             # Utilities
└── backend/              # Flask API
    ├── app.py           # Flask routes
    ├── ai_core.py       # AI logic & ChromaDB
    ├── seed_db.py       # Database seeding
    └── requirements.txt  # Python dependencies
```

## 🚀 Quick Start

### Prerequisites
- **Node.js 18+** and npm
- **Python 3.9+** (3.8+ works but 3.9+ recommended)
- **Groq API Key** - [Get one free here](https://console.groq.com)
  - Sign up for Groq Cloud
  - Navigate to API Keys section
  - Create a new API key
  - Copy the key (you'll need it in step 5)

---

## 📦 Installation

### Backend Setup

**1. Navigate to backend directory:**
```bash
cd backend
```

**2. Create and activate virtual environment:**
```bash
python3 -m venv venv

# On macOS/Linux:
source venv/bin/activate

# On Windows:
venv\Scripts\activate
```

**3. Install Python dependencies:**
```bash
pip install -r requirements.txt
```

This installs:
- Flask & Flask-CORS
- Groq SDK
- ChromaDB
- sentence-transformers
- python-dotenv

**4. Create environment file:**
```bash
cp .env.example .env
```

**5. Add your Groq API key to `.env`:**
```env
GROQ_API_KEY=gsk_your_actual_api_key_here
```

**6. Seed the database with sample profiles:**
```bash
python seed_db.py
```

This creates 20+ diverse collaborator profiles in ChromaDB with:
- Software Engineers
- Designers
- Product Managers
- Data Scientists
- And more!

**7. Start the Flask backend server:**

**Option A - Using the start script (recommended):**
```bash
./start.sh
```

**Option B - Direct Python:**
```bash
python app.py
```

✅ Backend will run on `http://localhost:5001`

You should see:
```
================================================================================
🚀 Starting Synergy Backend Server
   Port: 5001
   Debug Mode: True
   Endpoints:
      GET  /health
      POST /chat
      POST /find-collaborators
      ...
================================================================================
```

---

### Frontend Setup

**1. Navigate to frontend directory:**
```bash
cd frontend
```

**2. Install Node dependencies:**
```bash
npm install
```

This installs:
- Next.js 14
- React 18
- Tailwind CSS
- shadcn/ui components
- react-chatbot-kit
- Lucide icons

**3. Start the Next.js development server:**
```bash
npm run dev
```

✅ Frontend will run on `http://localhost:3000`

You should see:
```
▲ Next.js 14.2.33
- Local:        http://localhost:3000
✓ Ready in 800ms
```

---

## 🎯 Running the Application

**1. Make sure both servers are running:**
- Backend: `http://localhost:5001` ✅
- Frontend: `http://localhost:3000` ✅

**2. Open your browser and go to:**
```
http://localhost:3000
```

**3. You should see:**
- Beautiful landing page with gradient background
- Large input box at the top
- "Connected" status indicator (green dot) in navbar

**4. Try the demo:**

**Option A - Complete prompt (instant matching):**
```
Product designer with fintech experience. Want to build a B2B payments 
platform, need an engineer who knows backend systems.
```

**Option B - Conversational flow:**
```
User: "Need a technical co-founder for my startup idea."
Bot: "What skills do you have?"
User: "Python and Java"
Bot: "What domains interest you most?"
User: "Finance and healthcare"
Bot: "Great, I have everything I need!"
```

**5. Watch the magic:**
- ⏳ Loading indicator appears (1.5 seconds)
- 👥 5-6 match cards appear with compatibility scores (85-100%)
- 🎯 2-3 team suggestions appear below
- ✨ Smooth animations throughout

---

## 🔧 How It Works

### 1. **Conversational Input** 
- User types their information in the main input box OR uses the chatbot
- Can provide complete information upfront or answer follow-up questions
- AI adapts to the level of detail provided

### 2. **Smart Trigger Detection**
The backend uses dual-trigger logic:
- **Phrase Detection**: AI says "Great, I have everything I need!"
- **Auto-Extraction**: Analyzes transcript for skills AND interests
- **Combined Logic**: Triggers when BOTH conditions are met

```python
# Backend checks:
skills_ok = has_valid_skills(profile)  # Python, React, etc.
interests_ok = has_valid_interests(profile)  # FinTech, Healthcare, etc.
is_trigger = phrase_trigger OR (skills_ok AND interests_ok)
```

### 3. **Profile Extraction**
Using Groq's `llama-3.1-70b-versatile`:
```python
{
  "name": "User",
  "skills": ["Product Design", "Fintech", "UI/UX"],
  "interests": ["B2B Payments", "Financial Technology"],
  "role": "Designer",
  "looking_for": "Software Engineer"
}
```

### 4. **Vector Embedding & Semantic Search**
- Profile → text → embedding using `sentence-transformers`
- Query ChromaDB for similar profiles
- Returns top 5-7 matches based on cosine similarity

### 5. **Re-ranking Algorithm**
Matches are scored based on:
```python
score = 0.0

# Role complementarity (highest weight)
if candidate_role in target_roles:
    score += 10.0  # Explicitly requested role
elif candidate_role in complement_roles:
    score += 6.0   # Complementary role
    
# Domain overlap
shared_interests = user_interests ∩ candidate_interests
score += 3.0 * (overlap_ratio)

# Final sorting by score
```

### 6. **Team Building**
AI generates 2-3 different team suggestions:
- **Balanced Team**: Designer + PM (early-stage focus)
- **Technical Powerhouse**: 2 technical roles (infrastructure focus)
- **Product-Led Team**: PM + executor (user-centric focus)

Each team gets AI-generated reasoning using Groq.

### 7. **Results Display**
- **Match Cards**: 5-6 individuals with compatibility scores
- **Team Suggestions**: 2-3 pre-formed teams with reasoning
- **Animations**: Staggered slide-up animations (100ms delays)
- **Auto-scroll**: Smooth scroll to results section

---

## 📡 API Endpoints

### `GET /health`
Health check endpoint
```json
{
  "status": "healthy",
  "timestamp": "2025-10-26T05:00:00Z"
}
```

### `POST /chat`
Send a chat message, get AI response with trigger detection

**Request:**
```json
{
  "session_id": "session_123",
  "message": "I'm a Python developer interested in healthcare"
}
```

**Response:**
```json
{
  "response": "What domains interest you most

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 27 recognized source files, 203 KB.
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code

## Codebase structure (from repository index)

### Files (34 of 34)

```
backend/.env.example
backend/.gitignore
backend/ai_core.py
backend/app.py
backend/generate_profiles.py
backend/README.md
backend/requirements.txt
backend/reset_db.sh
backend/seed_db.py
backend/setup.sh
backend/start.sh
backend/test_api.py
backend/wipe_db.py
frontend/.eslintrc.json
frontend/.gitignore
frontend/app/chatbot.css
frontend/app/globals.css
frontend/app/layout.js
frontend/app/page.js
frontend/components/chatbot/ActionProvider.js
frontend/components/chatbot/config.js
frontend/components/chatbot/MessageParser.js
frontend/components/ui/badge.jsx
frontend/components/ui/button.jsx
frontend/components/ui/card.jsx
frontend/components/UserCard.jsx
frontend/jsconfig.json
frontend/lib/api.js
frontend/lib/utils.js
frontend/next.config.js
frontend/package.json
frontend/postcss.config.js
frontend/tailwind.config.js
README.md
```

### Dependencies

- backend/requirements.txt: chromadb@==0.4.22, flask@==3.0.0, flask-cors@==4.0.0, groq@==0.4.2, numpy@<2.0, python-dotenv@==1.0.0, sentence-transformers@==2.3.1
- frontend/package.json: autoprefixer@^10.4.19, class-variance-authority@^0.7.0, clsx@^2.1.1, eslint@^8.57.0, eslint-config-next@14.2.3, lucide-react@^0.378.0, next@^14.2.33, postcss@^8.4.38, react@^18.3.1, react-chatbot-kit@^2.2.2, react-dom@^18.3.1, tailwind-merge@^2.3.0, tailwindcss@^3.4.3, typescript@^5.4.5

### Recent commits (newest first)

- Implemented slack integration, added features for db reset
- 5:27 am typa mvp
- updated backend 2.0
- backend 2.0
- backend v1.0
- database task 1
- updated ai core prompt
- updated app.py
- added scripts and logging
- added logging
- Updated requirements
- Initial commit

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

### backend/requirements.txt

```
flask==3.0.0
flask-cors==4.0.0
groq==0.4.2
chromadb==0.4.22
numpy<2.0
sentence-transformers==2.3.1
python-dotenv==1.0.0
```

### frontend/package.json

```
{
  "name": "synergy-frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "lucide-react": "^0.378.0",
    "next": "^14.2.33",
    "react": "^18.3.1",
    "react-chatbot-kit": "^2.2.2",
    "react-dom": "^18.3.1",
    "tailwind-merge": "^2.3.0"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.19",
    "eslint": "^8.57.0",
    "eslint-config-next": "14.2.3",
    "postcss": "^8.4.38",
    "tailwindcss": "^3.4.3",
    "typescript": "^5.4.5"
  }
}

```

### frontend/app/layout.js

```javascript
import { Plus_Jakarta_Sans, Space_Grotesk } from "next/font/google";
import "./globals.css";
import "react-chatbot-kit/build/main.css";

const bodyFont = Plus_Jakarta_Sans({
  subsets: ["latin"],
  weight: ["400", "500", "600", "700"],
  variable: "--font-body",
});

const headingFont = Space_Grotesk({
  subsets: ["latin"],
  weight: ["500", "600", "700"],
  variable: "--font-heading",
});

export const metadata = {
  title: "Synergy - AI Collaborator Finder",
  description: "Find your perfect co-founder with AI",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={`${bodyFont.variable} ${headingFont.variable}`}>
        {children}
      </body>
    </html>
  );
}

```

### backend/app.py

```python
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv
import logging
import time
import traceback
import os
import httpx
import ai_core

# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)

app = Flask(__name__)
# Configure CORS to allow frontend communication
CORS(app, resources={
    r"/*": {
        "origins": ["http://localhost:3000", "http://127.0.0.1:3000"],
        "methods": ["GET", "POST", "OPTIONS"],
        "allow_headers": ["Content-Type"],
        "expose_headers": ["Content-Type"],
        "supports_credentials": False
    }
})

# Request logging middleware
@app.before_request
def log_request_info():
    """Log incoming request details."""
    request.start_time = time.time()
    logger.info(f"📥 {request.method} {request.path}")

@app.after_request
def log_response_info(response):
    """Log response details and request duration."""
    duration = time.time() - request.start_time
    logger.info(f"📤 {response.status_code} - {duration:.3f}s\n")
    return response

# Store chat history per session (in production, use proper session management)
chat_sessions = {}
processed_slack_messages = set()  # (channel_id, message_ts)
recent_text_cache = {}  # {(channel_id, normalized_text): last_ts}


def _post_slack_thread_message(channel_id: str, thread_ts: str, text: str) -> bool:
    """Post a message to a Slack thread if SLACK_BOT_TOKEN is configured.
    Returns True on success, False otherwise. Never raises to avoid impacting MVP.
    """
    token = os.getenv('SLACK_BOT_TOKEN', '').strip()
    if not token:
        return False
    try:
        resp = httpx.post(
            'https://slack.com/api/chat.postMessage',
            headers={
                'Authorization': f'Bearer {token}',
                'Content-Type': 'application/json'
            },
            json={
                'channel': channel_id,
                'thread_ts': thread_ts,
                'text': text
            },
            timeout=8.0
        )
        ok = resp.json().get('ok', False)
        if not ok:
            logging.warning(f"Slack postMessage failed: {resp.text}")
        return bool(ok)
    except Exception as e:
        logging.warning(f"Slack postMessage error: {e}")
        return False


# Validation helpers
def validate_chat_request(data):
    """Validate /chat endpoint request data."""
    if not data:
        return "Request body is required", 400
    
    message = data.get('message', '').strip()
    session_id = data.get('session_id', '').strip()
    
    if not message:
        return "Message is required and cannot be empty", 400
    
    if len(message) > 1000:
        return "Message is too long (max 1000 characters)", 400
    
    if not session_id:
        return "Session ID is required", 400
    
    if len(session_id) > 100:
        return "Session ID is too long (max 100 characters)", 400
    
    return None, None


def validate_find_collaborators_request(data):
    """Validate /find-collaborators endpoint request data."""
    if not data:
        return "Request body is required", 400
    
    chat_transcript = data.get('chat_transcript', '').strip()
    session_id = data.get('session_id', '').strip()
    
    if not chat_transcript:
        return "Chat transcript is required and cannot be empty", 400
    
    if len(chat_transcript) < 50:
        return "Chat transcript is too short (minimum 50 characters)", 400
    
    if len(chat_transcript) > 10000:
        return "Chat transcript is too long (max 10000 characters)", 400
    
    if not session_id:
        return "Session ID is required", 400
    
    return None, None


@app.route('/health', methods=['GET'])
def health():
    """Health check endpoint."""
    logger.info("✅ Health check requested")
    return jsonify({"status": "ok"})


@app.route('/chat', methods=['POST'])
def chat():
    """
    Real-time chat endpoint using Groq llama3-8b.
    """
    try:
        logger.info("💬 Processing chat request")
        
        # Validate request
        data = request.get_json(silent=True)
        error_msg, status_code = validate_chat_request(data)
        if error_msg:
            logger.warning(f"   ⚠️  Validation failed: {error_msg}")
            return jsonify({"error": error_msg}), status_code
        
        message = data.get('message', '').strip()
        session_id = data.get('session_id', 'default')
        
        logger.info(f"   Session ID: {session_id}")
        logger.info(f"   User Message: {message[:100]}..." if len(message) > 100 else f"   User Message: {message}")
        
        # Get or create chat history for this session
        if session_id not in chat_sessions:
            logger.info(f"   Creating new session: {session_id}")
            chat_sessions[session_id] = []
        else:
            logger.info(f"   Existing session with {len(chat_sessions[session_id])} messages")
        
        chat_history = chat_sessions[session_id]
        
        # Get AI response
        logger.info("   🤖 Calling Groq API for chat response...")
        ai_start = time.time()
        response = ai_core.get_chat_response(message, chat_history)
        ai_duration = time.time() - ai_start
        logger.info(f"   ✅ Groq API response received in {ai_duration:.3f}s")
        logger.info(f"   AI Response: {response[:100]}..." if len(response) > 100 else f"   AI Response: {response}")
        
        # Determine trigger: exact phrase OR server-side extraction shows enough info (skills+interests)
        phrase_trigger = "Great, I have everything I need!" in response
        extracted_trigger = False
        try:
            augmented_history = chat_history + [
                {"role": "user", "content": message},
                {"role": "assistant", "content": re
[truncated — 19387 more characters]
```

### frontend/app/page.js

```javascript
'use client';

import { useRef, useState, useEffect, useMemo } from 'react';
import dynamic from 'next/dynamic';
import { ArrowUpRight, Sparkles } from 'lucide-react';
import config from '@/components/chatbot/config';
import MessageParser from '@/components/chatbot/MessageParser';
import ActionProvider from '@/components/chatbot/ActionProvider';
import { sendChatMessage, findCollaborators, generateSessionId, checkBackendHealth } from '@/lib/api';
import UserCard from '@/components/UserCard';
import './chatbot.css';

const Chatbot = dynamic(() => import('react-chatbot-kit').then(mod => mod.default), {
  ssr: false,
});

const suggestedPrompts = [
  // Full info - best case demo
  "I'm a software engineer with Python and React skills, building a healthcare AI tool for doctors. Based in SF, looking for a designer co-founder to join full-time.",
  
  // Partial/ambiguous - shows AI asking clarifying questions
  "I'm interested in climate tech and renewable energy. Looking for a co-founder.",
  
  // Missing info - demonstrates conversation flow
  "Need a technical co-founder for my startup idea.",
  
  // Good balance - realistic scenario
  "Product designer with fintech experience. Want to build a B2B payments platform, need an engineer who knows backend systems.",
];

export default function Home() {
  const [focused, setFocused] = useState(false);
  const [inputValue, setInputValue] = useState('');
  const [sessionId] = useState(() => generateSessionId());
  const [chatHistory, setChatHistory] = useState([]);
  const [matches, setMatches] = useState([]);
  const [teamSuggestions, setTeamSuggestions] = useState([]);
  const [userProfile, setUserProfile] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [backendConnected, setBackendConnected] = useState(false);
  const [error, setError] = useState(null);
  const [chatbotKey, setChatbotKey] = useState(0);
  const [isMatching, setIsMatching] = useState(false);
  const inputRef = useRef(null);
  const chatbotRef = useRef(null);

  // Check backend connection on mount
  useEffect(() => {
    checkBackendHealth().then(setBackendConnected);
  }, []);

  // Debug: Monitor matches state changes
  useEffect(() => {
    console.log('🔄 Matches state changed:', matches.length, 'matches');
    console.log('🔄 Team suggestions state changed:', teamSuggestions.length, 'teams');
  }, [matches, teamSuggestions]);

  const handlePromptClick = (prompt) => {
    setInputValue(prompt);
    inputRef.current?.focus();
  };

  const handleSendMessage = async () => {
    const trimmed = inputValue.trim();
    if (!trimmed || isLoading) return;

    setIsLoading(true);
    const userMessage = trimmed;
    setInputValue('');

    try {
      // Send initial message to backend
      const response = await sendChatMessage(sessionId, userMessage);
      
      console.log('📨 TOP BOX - Backend response:', response);
      console.log('   is_trigger:', response.is_trigger);
      
      // Update chat history  
      setChatHistory(prev => {
        const newHistory = [
          ...prev,
          { role: 'user', content: userMessage },
          { role: 'assistant', content: response.response },
        ];
        
        // Check if backend triggered matching
        if (response.is_trigger === true || response.is_trigger === 'true') {
          console.log('🎯 TOP BOX - Trigger detected! Calling findCollaborators');
          setIsMatching(true);
          
          const matchingStartTime = Date.now();
          const transcript = newHistory
            .map(msg => `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`)
            .join('\n');
          
          findCollaborators(sessionId, transcript)
            .then(matchData => {
              const newMatches = matchData.matches || [];
              const newTeams = matchData.team_suggestions || [];
              const matchingDuration = Date.now() - matchingStartTime;
              const remainingTime = Math.max(0, 1500 - matchingDuration);
              
              setTimeout(() => {
                setMatches(newMatches);
                setTeamSuggestions(newTeams);
                setUserProfile(matchData.your_profile);
                setIsMatching(false);
                
                setTimeout(() => {
                  const matchesHeading = Array.from(document.querySelectorAll('h2'))
                    .find(h2 => h2.textContent.includes('Your Perfect Matches'));
                  if (matchesHeading) {
                    matchesHeading.parentElement.scrollIntoView({ 
                      behavior: 'smooth', 
                      block: 'start' 
                    });
                  }
                }, 800);
              }, remainingTime);
            })
            .catch(err => {
              console.error('❌ Error finding collaborators:', err);
              setError('Failed to find matches');
              setIsMatching(false);
            });
        }
        
        return newHistory;
      });

      // Increment chatbot key to force re-render with new history
      setChatbotKey(prev => prev + 1);

      // Scroll to chatbot section to continue conversation
      setTimeout(() => {
        const chatSection = document.getElementById('synergy-chat');
        if (chatSection) {
          // Get the section's position
          const rect = chatSection.getBoundingClientRect();
          const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
          // Scroll with offset to account for fixed navbar (100px)
          window.scrollTo({
            top: scrollTop + rect.top - 100,
            behavior: 'smooth'
          });
        }
        
        // Focus on chatbot input after scroll
        setTimeout(() => {
          const chatInput = document.querySelector('.react-chatbot-kit-chat-input');
          if (chatInput) {
            chatInput.focus();
          }
        }, 600);
      }, 300);

    } catch (er
[truncated — 19715 more characters]
```

### frontend/postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### frontend/next.config.js

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
}

module.exports = nextConfig

```

### backend/reset_db.sh

```shell
#!/bin/bash

# Quick script to wipe and re-seed the database

echo "🔄 Resetting ChromaDB..."
echo ""

echo "🗑️  Step 1/2: Wiping database..."
python3 wipe_db.py --force

echo ""
echo "🌱 Step 2/2: Re-seeding with fresh profiles..."
python3 seed_db.py

echo ""
echo "✅ Database reset complete!"
echo "   - 92 diverse collaborators ready"
echo "   - No duplicate 'User' entries"
echo ""
echo "💡 To add Slack intros manually:"
echo "   1. Go to http://localhost:3000"
echo "   2. Paste the Slack intro in the chat"
echo "   3. Answer any follow-up questions"
echo "   4. They'll be added to the database automatically!"
echo ""
echo "Or use: python add_slack_intro.py \"intro text here\""

```

### backend/start.sh

```shell
#!/bin/bash

# Synergy Backend Startup Script
# This script activates the virtual environment, loads the API key, and starts the server

echo "🚀 Starting Synergy Backend..."

# Check if venv exists
if [ ! -d "venv" ]; then
    echo "❌ Virtual environment not found!"
    echo "Run: python3 -m venv venv"
    exit 1
fi

# Check if .env exists
if [ ! -f ".env" ]; then
    echo "❌ .env file not found!"
    echo "Create it with: echo 'GROQ_API_KEY=your_key_here' > .env"
    exit 1
fi

# Activate virtual environment
echo "📦 Activating virtual environment..."
source venv/bin/activate

# Load .env file and export GROQ_API_KEY
echo "🔑 Loading API key from .env..."
export $(cat .env | grep -v '^#' | xargs)

# Check if API key is loaded
if [ -z "$GROQ_API_KEY" ]; then
    echo "❌ GROQ_API_KEY not found in .env file!"
    exit 1
fi

echo "✅ Environment ready!"
echo ""

# Start Flask server
python app.py

```

### backend/wipe_db.py

```python
#!/usr/bin/env python3
"""
Wipe ChromaDB completely and start fresh.
Run this before re-seeding with seed_db.py
"""

import chromadb
import os
import shutil
import sys

def wipe_chromadb():
    """Delete the entire ChromaDB directory."""
    db_path = "./chroma_db"
    
    if os.path.exists(db_path):
        print(f"🗑️  Deleting ChromaDB at {db_path}...")
        shutil.rmtree(db_path)
        print("✅ ChromaDB wiped successfully!")
    else:
        print("ℹ️  No ChromaDB found at ./chroma_db")
    
    print("\n📝 Next steps:")
    print("   1. Run: python seed_db.py")
    print("   2. Or add manual entries from Slack via the frontend")

if __name__ == "__main__":
    # Check if --force flag is passed (for scripting)
    if "--force" in sys.argv:
        wipe_chromadb()
    else:
        confirm = input("⚠️  This will DELETE all data in ChromaDB. Continue? (yes/no): ")
        
        if confirm.lower() in ['yes', 'y']:
            wipe_chromadb()
        else:
            print("❌ Aborted.")

```

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