# Project export: Chronos Calendar

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: TreeHacks 2025
- Tagline: Ever wished you could just tell your calendar what to do? Chronos is your agentic AI calendar assistant that understands natural language, making scheduling as simple as having a conversation.
- Devpost: https://devpost.com/software/chronos-calendar
- GitHub: https://github.com/sfkunal/Chronos
- Demo: https://www.figma.com/proto/p2P43kxpTnfm2ILd8ZHXff/TreeHacks-2025?page-id=282%3A803&node-id=282-1081&viewport=-681%2C162%2C0.51&t=56PBUtfmvnRULU4y-1&scaling=contain&content-scaling=fixed
- Video: https://www.youtube.com/embed/r8Gvx9pHg90?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Kunal Srivastava (53 commits), Lee Stilwell (48 commits), Neha Washikar (20 commits)

## Devpost submission (written by the team)

### Inspiration

Keeping track of a busy schedule is a frustrating, manual process. Adding, searching, and editing events takes a lot more effort than it should, with the average American spending anywhere between 45-50 minutes planning their day. Most calendar apps require a manual configuration of details (specific dates, times, and names) and don't adapt to how people plan their days. What if your calendar could think the way you do? Instead of forms and dropdowns, imagine just typing what you need — "Block focus time in the mornings" or "Find time for dinner with Sarah next week" — with your schedule update instantly. We're making scheduling effortless with natural language processing and smart event handling.

### What it does

At a high level, Chronos is a set of intelligent agents working together to find the intent behind your prompt and schedule your event for you in real-time. We have combined this agentic workflow with a beautiful UI/UX to create a truly comprehensive AI scheduling assistant. Prompt Chronos to create, modify, or delete simple events like “Lunch with James at 12:45pm tomorrow”, or even complex queries like “I want to go to the gym with kunal@email.com at 8am every other day for the next 3 weeks.” P.S.--adding your university course schedule for the quarter/semester has never been easier! Preferences act as your “user profile.” This is where you put conditions that don’t often change and that Chronos must always keep in mind, i.e. “I don’t want to have any meetings after 6pm.” How We Built This Agent Architecture Our system is built on a sophisticated multi-agent architecture powered by Groq's LLaMA 3.3 70B model. Each agent is specialized for specific calendar operations and works in concert to provide intelligent scheduling: Intent Agent Primary classifier for all user interactions Categorizes requests into CREATE, DELETE, EDIT, or UNKNOWN Uses sophisticated prompt engineering to understand context and implied actions Handles complex queries like "let's move tomorrow's lunch to Friday" Routes requests to appropriate specialized agents Availability Agent Manages calendar availability analysis Processes two-week forward-looking time windows Generates human-readable availability summaries Considers timezone constraints (America/Los_Angeles) Formats events into digestible daily schedules Handles overlapping and adjacent event logic Preferences Agent Converts natural language preferences into structured time-based rules Processes work hours, social time, and recurring patterns Extracts activity types, time constraints, and applicable days Determines if rules are blocking or preferential Maintains preference hierarchy and conflict resolution Integrates with scheduling decisions Search Agent Implements vector-based calendar search using ChromaDB Creates and maintains sentence transformer embeddings Processes calendar events in optimized chunks Enables semantic understanding of event context Provides real-time event lookup and retrieval Handles fuzzy matching and contextual search Mutability Agent Specializes in event modifications and deletions Preserves event integrity during changes Handles recurring event patterns Manages attendee updates and notifications Ensures calendar consistency during modifications Validates changes against user preferences Scheduling Agent Orchestrates all other agents' operations Implements the core scheduling logic Manages Google Calendar API integration Handles contact lookup and attendee validation Processes complex scheduling constraints Provides real-time feedback and confirmation People Agent Grabs relevant contacts over the Google People API Allows users to mention contacts in natural language Events are created with attendee emails by default, without extra effort from the user Backend Pipeline Request Processing Natural Language Input → Intent Classification → Agent Selection → Preference Validation → Calendar Operation → Real-time Response Request Processing Data Flow User Input → Flask Server → Groq LLM → Google Calendar API → ChromaDB → UI Update Data Flow Performance Optimizations Chunked Processing Calendar events are processed in chunks of 10 Reduces memory usage and improves response time Chunked Processing Calendar events are processed in chunks of 10 Reduces memory usage and improves response time Embedding Caching Vector embeddings are cached in ChromaDB Enables fast semantic search without recomputing Embedding Caching Vector embeddings are cached in ChromaDB Enables fast semantic search without recomputing Background Processing Long-running tasks are handled asynchronously Provides immediate user feedback while processing Background Processing Long-running tasks are handled asynchronously Provides immediate user feedback while processing Response Streaming Implemented server-sent events for real-time updates Reduces perceived latency for users Response Streaming Implemented server-sent events for real-time updates Reduces perceived latency for users Preference Optimization Caches processed preference rules Maintains preference hierarchy for quick access Optimizes rule matching during scheduling Preference Optimization Caches processed preference rules Maintains preference hierarchy for quick access Optimizes rule matching during scheduling

### Challenges we ran into

Building Agent Architecture Challenge: Ensuring seven AI agents work seamlessly, especially in coordinating Availability, Preferences, and Scheduling Agents. Solution: Developed a cascading agent system, where the Intent Agent first classifies the request, then routes tasks to specialized agents. Building Agent Architecture Challenge: Ensuring seven AI agents work seamlessly, especially in coordinating Availability, Preferences, and Scheduling Agents. Solution: Developed a cascading agent system, where the Intent Agent first classifies the request, then routes tasks to specialized agents. Natural Language Understanding Challenge: Users phrase scheduling requests ambiguously ("Call in the evening with James"), requiring accurate intent recognition. Solution: Fine-tuned LLM-powered parsing to handle multi-step requests and ensure structured inputs for downstream agents. Natural Language Understanding Challenge: Users phrase scheduling requests ambiguously ("Call in the evening with James"), requiring accurate intent recognition. Solution: Fine-tuned LLM-powered parsing to handle multi-step requests and ensure structured inputs for downstream agents. Multi-Event Scheduling Logic Challenge: Handling recurring commands like "Go to the gym every night" required resolving conflicts, durations, and smart defaults. Solution: Used prompt engineering to generate structured event outputs, enabling the Scheduling Agent to process multiple events accurately. Multi-Event Scheduling Logic Challenge: Handling recurring commands like "Go to the gym every night" required resolving conflicts, durations, and smart defaults. Solution: Used prompt engineering to generate structured event outputs, enabling the Scheduling Agent to process multiple events accurately. Real-time Calendar Updates Challenge: Keeping the UI and Google Calendar synchronized in real-time. Solution: Built a streaming response system using Flask and React's state management to reflect changes instantly. Real-time Calendar Updates Challenge: Keeping the UI and Google Calendar synchronized in real-time. Solution: Built a streaming response system using Flask and React's state management to reflect changes instantly. Preference Management Challenge: Converting natural language preferences into actionable scheduling rules. Solution: Designed a structured JSON schema for preferences and used LLM-powered conversion for consistency. Preference Management Challenge: Converting natural language preferences into actionable scheduling rules. Solution: Designed a structured JSON schema for preferences and used LLM-powered conversion for consistency. Vector Search Optimization Challenge: Running efficient semantic search across thousands of calendar events. Solution: Implemented chunked processing and optimized embeddings to speed up retrieval. Vector Search Optimization Challenge: Running efficient semantic search across thousands of calendar events. Solution: Implemented chunked processing and optimized embeddings to speed up retrieval. Speech-to-Text Challenges Challenge: Adding voice commands required transferring audio from frontend to backend, but encoding and permission issues caused failures. Solution: Pivoted to prioritize text-based interaction, ensuring reliability before revisiting accessibility features. Speech-to-Text Challenges Challenge: Adding voice commands required transferring audio from frontend to backend, but encoding and permission issues caused failures. Solution: Pivoted to prioritize text-based interaction, ensuring reliability before revisiting accessibility features.

### Accomplishments we're proud of

✅ User-Friendly Interface – Our intuitive design makes it effortless to manage schedules, with 35 people already on the waitlist for our Beta launch. ✅ Smart Multi-Event Scheduling – Users can create recurring or multi-event commands in plain English, like: "Start going to the gym every night." Chronos understands and schedules it seamlessly. ✅ Agent Transparency – Users can see how our seven specialized AI agents collaborate in real-time to process and optimize scheduling requests.

### What we learned

Agentic Workflows need to be Robust … User Research is Crucial Speaking with 40+ users helped us uncover real pain points and focus on solving actual problems, not just assumptions. Organization is Key for Success Defining roles and workflows made collaboration across teams smooth and efficient. Structured Workflows Keep Teams Aligned A clear process + ongoing to-do list ensured everyone stayed on the same page and could adapt during hectic moments. Moving Quick Moving quickly while staying focused on goals helped our team deliver results under tight deadlines

### What's next

This weekend, we spoke to fellow hackers to learn from their experiences and better understand their pain points. We now have a growing waitlist of 35 students. In the coming weeks, we want to keep iterating. Here’s what’s next: Speech-to-Text Integration (Groq): Enable users to add, search, and modify events, plus log preferences using voice input. Interactive Chat Functionality: Introduce a conversational interface where users can ask about their schedule, reschedule events, and receive smart recommendations. Keyboard Shortcuts for Efficiency: Implement quick actions for scheduling, searching, and editing events – making navigation even quicker. Understanding Preference-Backed Goals: Offer scheduling suggestions based on user habits. We want Chronos to learn and adapt from previous calendar events.

## README (from the GitHub repository)

# Chronos Calendar

A next-generation calendar application powered by Large Language Models, offering natural language scheduling and intelligent event management. Built for TreeHacks 2025.

## 🌟 Overview

Chronos reimagines calendar management through natural language processing and intelligent agents. Users can schedule, modify, and search events using conversational language, while the system handles complex scheduling logic, preference management, and calendar optimization.

## 🤖 Intelligent Agents

### Intent Agent
- Classifies user requests into CREATE, DELETE, EDIT, or UNKNOWN
- Powered by Groq's LLaMA 3.3 70B model
- Enables context-aware response handling

### Availability Agent
- Analyzes calendar for free/busy times
- Considers timezone constraints (America/Los_Angeles)
- Generates human-readable availability summaries

### Preferences Agent
- Converts natural language preferences into structured rules
- Handles work hours, social time, and recurring patterns
- Ensures scheduling respects user constraints

### Search Agent
- Vector-based calendar search using ChromaDB
- Semantic understanding of event context
- Real-time event lookup and retrieval

### Mutability Agent
- Handles event modifications and deletions
- Preserves event integrity during changes
- Manages recurring event patterns

### Scheduling Agent
- Orchestrates all other agents
- Handles complex scheduling logic
- Manages Google Calendar integration

## 🎯 Key Features

### Natural Language Interface
```
"Schedule lunch with Connor next Wednesday at noon"
"Move my 2pm meeting to tomorrow morning"
"What meetings do I have this week?"
```

### Smart Calendar Views
- Monthly overview with preference highlighting
- Weekly detailed view with real-time updates
- Integrated chat interface for natural interaction

### Preference-Aware Scheduling
- Learning from user patterns
- Time-block restrictions
- Custom scheduling rules

## 🚀 Getting Started

### Prerequisites
- Node.js 16.x+
- Python 3.8+
- Google Cloud Platform account
- Groq API key
- ChromaDB account

### Frontend Setup
```bash
cd chronos-frontend
npm install
npm run dev
```

### Backend Setup
```bash
cd chronos-backend
pip install -r requirements.txt

# Configure environment variables
FLASK_APP=app.py
FLASK_ENV=development
GROQ_API_KEY=your_key
CHROMA_TENANT_KEY=your_key
CHROMA_API_KEY=your_key
OPENAI_API_KEY=your_key

python app.py
```

## 🛠 Technology Stack

### Frontend
- Next.js 13+
- React
- Tailwind CSS
- shadcn/ui
- Google Calendar API

### Backend
- Flask
- Groq LLM
- ChromaDB
- Google Calendar/People APIs
- Sentence Transformers

## 📊 System Architecture

```
User Input (Natural Language)
         ↓
Intent Classification
         ↓
Agent Orchestration ←→ Preference Rules
         ↓
Calendar Operations ←→ Google Calendar
         ↓
Real-time Updates
```

## 🔒 Security & Performance

- OAuth 2.0 authentication
- Rate limiting and request throttling
- Chunked event processing
- Vector search optimization
- Real-time response streaming

## 🤝 Contributing

1. Fork the repository
2. Create feature branch
3. Commit changes
4. Push to branch
5. Create Pull Request

## 📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

## Detected evidence (automated analysis)

Indexed codebase: 47 recognized source files, 192 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 (57 of 57)

```
.gitignore
chronos-backend/.gitignore
chronos-backend/app.py
chronos-backend/groq_engine.py
chronos-backend/README.md
chronos-backend/requirements.txt
chronos-backend/search_engine.py
chronos-frontend/.gitignore
chronos-frontend/components.json
chronos-frontend/jsconfig.json
chronos-frontend/next.config.mjs
chronos-frontend/package.json
chronos-frontend/postcss.config.mjs
chronos-frontend/README.md
chronos-frontend/src/app/dashboard/page.jsx
chronos-frontend/src/app/globals.css
chronos-frontend/src/app/layout.js
chronos-frontend/src/app/lee/page.js
chronos-frontend/src/app/neha/page.js
chronos-frontend/src/app/page.js
chronos-frontend/src/components/app-sidebar.jsx
chronos-frontend/src/components/calendars.jsx
chronos-frontend/src/components/ChatInterface.jsx
chronos-frontend/src/components/date-picker.jsx
chronos-frontend/src/components/DayColumn.jsx
chronos-frontend/src/components/Event.jsx
chronos-frontend/src/components/EventCard.jsx
chronos-frontend/src/components/nav-user.jsx
chronos-frontend/src/components/Preferences.jsx
chronos-frontend/src/components/TimeGrid.jsx
chronos-frontend/src/components/ui/avatar.jsx
chronos-frontend/src/components/ui/breadcrumb.jsx
chronos-frontend/src/components/ui/button.jsx
chronos-frontend/src/components/ui/calendar.jsx
chronos-frontend/src/components/ui/card.jsx
chronos-frontend/src/components/ui/collapsible.jsx
chronos-frontend/src/components/ui/date-picker.jsx
chronos-frontend/src/components/ui/dialog.jsx
chronos-frontend/src/components/ui/dropdown-menu.jsx
chronos-frontend/src/components/ui/input.jsx
chronos-frontend/src/components/ui/label.jsx
chronos-frontend/src/components/ui/popover.jsx
chronos-frontend/src/components/ui/resizable.jsx
chronos-frontend/src/components/ui/scroll-area.jsx
chronos-frontend/src/components/ui/separator.jsx
chronos-frontend/src/components/ui/sheet.jsx
chronos-frontend/src/components/ui/sidebar-nav.jsx
chronos-frontend/src/components/ui/sidebar.jsx
chronos-frontend/src/components/ui/skeleton.jsx
chronos-frontend/src/components/ui/textarea.jsx
chronos-frontend/src/components/ui/time-field.jsx
chronos-frontend/src/components/ui/tooltip.jsx
chronos-frontend/src/components/WeeklyCalendar.jsx
chronos-frontend/src/hooks/use-mobile.jsx
chronos-frontend/src/lib/utils.js
chronos-frontend/tailwind.config.mjs
README.md
```

### Dependencies

- chronos-backend/requirements.txt: chromadb@==0.6.3, datetime, flask@==3.0.2, flask-cors@==4.0.0, google-api-python-client, google-auth, google-auth-httplib2, google-auth-oauthlib, groq, instructor, pydantic, python-dotenv, pytz, sentence-transformers@>=2.2.2
- chronos-frontend/package.json: @radix-ui/react-dialog@^1.1.6, @radix-ui/react-label@^2.1.2, @radix-ui/react-popover@^1.1.6, @radix-ui/react-scroll-area@^1.2.3, @radix-ui/react-slot@^1.1.2, @radix-ui/react-tooltip@^1.1.8, class-variance-authority@^0.7.1, clsx@^2.1.1, date-fns@^3.6.0, lucide-react@^0.475.0, next@15.1.7, postcss@^8, react@^19.0.0, react-day-picker@^8.10.1, react-dom@^19.0.0, react-markdown@^9.0.3, sonner@^1.7.4, tailwind-merge@^3.0.1, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7

### Recent commits (newest first)

- READMEs
- update README.md
- slight ui tweaks
- delete speech stuff
- changed vector search to structure w/ llama 8b instead of 70b for faster speed
- added rubik font for logo + name
- changed pref shadow
- reload on create
- logo
- better text box
- Merge branch 'main' of github.com:sfkunal/Chronos
- updated preferences formatting
- polling http
- Merge branch 'main' of https://github.com/sfkunal/Chronos
- remove mic stuff
- merged selectedMonthDate stuff
- Merge branch 'main' of github.com:sfkunal/Chronos
- textbox consistency
- remove error toast
- show attendees

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

### chronos-backend/requirements.txt

```
flask==3.0.2
flask-cors==4.0.0
python-dotenv==1.0.1
google-auth
google-auth-oauthlib
google-auth-httplib2
google-api-python-client
groq
python-dotenv
instructor
pydantic
pytz
datetime
chromadb==0.6.3
sentence-transformers>=2.2.2
```

### chronos-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.6",
    "@radix-ui/react-label": "^2.1.2",
    "@radix-ui/react-popover": "^1.1.6",
    "@radix-ui/react-scroll-area": "^1.2.3",
    "@radix-ui/react-slot": "^1.1.2",
    "@radix-ui/react-tooltip": "^1.1.8",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "date-fns": "^3.6.0",
    "lucide-react": "^0.475.0",
    "next": "15.1.7",
    "react": "^19.0.0",
    "react-day-picker": "^8.10.1",
    "react-dom": "^19.0.0",
    "sonner": "^1.7.4",
    "react-markdown": "^9.0.3",
    "tailwind-merge": "^3.0.1",
    "tailwindcss-animate": "^1.0.7"
  },
  "devDependencies": {
    "postcss": "^8",
    "tailwindcss": "^3.4.1"
  }
}

```

### chronos-backend/app.py

```python
from flask import Flask, jsonify, session, redirect, request, url_for, Response
from flask_cors import CORS
import os
import pytz
import json
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from datetime import datetime, timedelta
from groq_engine import SchedulingAgent, EditOrDeleteIntentAgent, get_groq_welcome
from search_engine import stringify_event, update_events_in_chroma, search_events
from groq import Groq
import json
import os
import threading
import time

os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'  # Allow HTTP connections in development

app = Flask(__name__)
app.secret_key = 'thisisSECRET1340iu5203u5103'

# Update CORS configuration
CORS(app, 
     origins=["http://localhost:3000"],
     supports_credentials=True,
     allow_headers=["Content-Type", "Authorization"],
     methods=["GET", "POST", "OPTIONS"],
     expose_headers=['Content-Type', 'Content-Length'],
     max_age=3600,
     resources={
         r"/api/*": {
             "origins": ["http://localhost:3000"],
             "allow_headers": ["Content-Type", "Authorization"],
             "methods": ["GET", "POST", "OPTIONS"],
             "supports_credentials": True
         }
     })

SCOPES = [
    'https://www.googleapis.com/auth/calendar',
    'https://www.googleapis.com/auth/calendar.readonly',
    'https://www.googleapis.com/auth/contacts.readonly',
    'https://www.googleapis.com/auth/directory.readonly',
    'https://www.googleapis.com/auth/contacts.other.readonly',
    'https://www.googleapis.com/auth/peopleapi.readonly'
]

class CalendarAPI:
    def __init__(self):
        self.auth_state = None
        self.creds = None
        self.service = None
        self.people_service = None

    def login(self) -> str:
        # Check if we have valid credentials
        if os.path.exists('token.json'):
            try:
                self.creds = Credentials.from_authorized_user_file('token.json', SCOPES)
                if self.creds.valid:
                    self.instantiate()
                    return ''
                    
                # If creds are expired but we have a refresh token
                if self.creds and self.creds.expired and self.creds.refresh_token:
                    try:
                        self.creds.refresh(Request())
                        self.instantiate()
                        return ''
                    except Exception as e:
                        print(f"Error refreshing token: {str(e)}")
                        # Delete invalid token file
                        os.remove('token.json')
            except Exception as e:
                print(f"Error loading credentials: {str(e)}")
                # Delete invalid token file
                os.remove('token.json')

        # Otherwise, need to get new credentials
        flow = Flow.from_client_secrets_file('client_secret.json', SCOPES)
        flow.redirect_uri = url_for('callback', _external=True)
        auth_url, self.auth_state = flow.authorization_url(
            access_type='offline',
            prompt='consent'
        )
        return auth_url

    def login_callback(self, auth_response):
        flow = Flow.from_client_secrets_file(
            'client_secret.json',
            scopes=SCOPES,
            state=self.auth_state
        )
        flow.redirect_uri = url_for('callback', _external=True)
        
        flow.fetch_token(authorization_response=auth_response)
        self.creds = flow.credentials
        
        # Save credentials for future use
        with open('token.json', 'w') as token:
            token.write(self.creds.to_json())
            
        self.instantiate()

    def instantiate(self):
        self.service = build('calendar', 'v3', credentials=self.creds)
        self.people_service = build('people', 'v1', credentials=self.creds)

    def get_events(self):
        if not self.creds or not self.creds.valid:
            return None
            
        ten_days_ago = datetime.utcnow() - timedelta(days=10)
        time_min = ten_days_ago.isoformat() + 'Z'
        
        events_result = self.service.events().list(
            calendarId='primary',
            timeMin=time_min,
            maxResults=200,
            singleEvents=True,
            orderBy='startTime'
        ).execute()
        
        # Filter out all-day events
        events = events_result.get('items', [])
        filtered_events = [
            event for event in events 
            if 'dateTime' in event.get('start', {})
        ]
        
        return filtered_events

    def delete_calendar_event(self, event_id):
        try:
            self.service.events().delete(
                calendarId='primary',
                eventId=event_id
            ).execute()
            return {
                'status': 'success',
                'message': 'Event successfully deleted'
            }
        except Exception as e:
            return {
                'status': 'error',
                'message': f'Failed to delete event: {str(e)}'
            }

    def edit_calendar_event(self, event_id, updates):
        try:
            # First get the existing event
            event = self.service.events().get(
                calendarId='primary',
                eventId=event_id
            ).execute()
            
            # Update the event with new details
            for key, value in updates.items():
                event[key] = value
            
            # Update the event
            updated_event = self.service.events().update(
                calendarId='primary',
                eventId=event_id,
                body=event
            ).execute()
            
            return {
                'status': 'success',
                'message': 'Event successfully updated',
                'event': updated_event
            }
        except Exception
[truncated — 19416 more characters]
```

### chronos-frontend/src/app/layout.js

```javascript
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata = {
  title: "Chronos Calendar",
  description: "Your Calendar, Your Way",
  icons: {
    icon: '/logo.svg',
    shortcut: '/logo.svg',
    apple: '/logo.svg',
    other: {
      rel: 'apple-touch-icon',
      url: '/logo.svg',
    },
  },
};

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

```

### chronos-frontend/src/app/page.js

```javascript
'use client'
import React, { useEffect } from 'react';
import { Manrope } from 'next/font/google';
import { Calendar } from '@/components/ui/calendar';
import Preferences from '@/components/Preferences';
import { useState } from 'react';
import WeeklyCalendar from '@/components/WeeklyCalendar';
import { Button } from '@/components/ui/button';
import ChatInterface from '@/components/ChatInterface';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Loader2 } from "lucide-react";


const manrope = Manrope({ subsets: ['latin'] })


const transformGoogleEvents = (googleEvents) => {
    return googleEvents.map(event => ({
        id: event.id,
        title: event.summary,
        start: new Date(event.start.dateTime || event.start.date),
        end: new Date(event.end.dateTime || event.end.date),
        color: getColorFromId(event.colorId), // We'll define this function
        description: event.description || '',
        attendees: event.attendees || null
    }));
};

// Google Calendar color IDs to hex colors mapping
const getColorFromId = (colorId) => {
    const colorMap = {
        '1': '#7986cb', // Lavender
        '2': '#33b679', // Sage
        '3': '#8e24aa', // Grape
        '4': '#e67c73', // Flamingo
        '5': '#f6c026', // Banana
        '6': '#f5511d', // Tangerine
        '7': '#039be5', // Peacock
        '8': '#616161', // Graphite
        '9': '#3f51b5', // Blueberry
        '10': '#0b8043', // Basil
        'default': '#3b82f6' // Default blue
    };
    return colorMap[colorId] || colorMap.default;
};

function CalendarPage({ selectedMonthDate, events1, setEvents1 }) {
    const [events, setEvents] = useState([]);
    const [selectedEvent, setSelectedEvent] = useState(null);
    const [isLoggedIn, setIsLoggedIn] = React.useState(false);
    const [authToken, setAuthToken] = React.useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [selectedDate, setSelectedDate] = useState(selectedMonthDate ?? new Date())


    useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const token = params.get('token');

        if (token) {
            localStorage.setItem('authToken', token);
            window.history.replaceState({}, '', '/lee');
            setAuthToken(token);
        } else {
            const savedToken = localStorage.getItem('authToken');
            if (savedToken) {
                setAuthToken(savedToken);
            }
        }
    }, []);

    useEffect(() => {
        if (selectedMonthDate) {
            setSelectedDate(selectedMonthDate);
        }
    }, [selectedMonthDate]);

    useEffect(() => {
        const checkAuthAndFetchEvents = async () => {
            try {
                const authResponse = await fetch('http://127.0.0.1:5000/api/auth-status', {
                    credentials: 'include'
                });

                if (authResponse.ok) {
                    const authData = await authResponse.json();

                    if (authData.isAuthenticated) {
                        const eventsResponse = await fetch('http://127.0.0.1:5000/api/events', {
                            credentials: 'include'
                        });

                        if (eventsResponse.ok) {
                            const eventsData = await eventsResponse.json();
                            setEvents1(eventsData.events);
                            setIsLoggedIn(true);
                        } else {
                            window.location.href = 'http://127.0.0.1:5000/login';
                        }
                    } else {
                        window.location.href = 'http://127.0.0.1:5000/login';
                    }
                }
            } catch (error) {
                console.error('Error:', error);
                window.location.href = 'http://127.0.0.1:5000/login';
            }
        };

        if (!isLoggedIn) {
            checkAuthAndFetchEvents();
        }
    }, [isLoggedIn]);

    // Modify this useEffect to remove the artificial delay
    useEffect(() => {
        if (events1.length > 0) {
            setIsLoading(true);
            const transformedEvents = transformGoogleEvents(events1);
            setEvents(transformedEvents);
            setIsLoading(false);
        } else {
            setIsLoading(true);
        }
    }, [events1]);

    const handleEventClick = (event) => {
        setSelectedEvent(event);
    };

    return (
        <div className="container h-full pt-[2%] relative">
            <WeeklyCalendar
                events={isLoading ? [] : events}
                onEventClick={handleEventClick}
                setEvents1={setEvents1}
                selectedDate={selectedDate}
            />

            {isLoading && (
                <div className="absolute inset-0 bg-white/50 backdrop-blur-sm flex items-center justify-center">
                    <Loader2 className="h-8 w-8 animate-spin text-gray-500" />
                </div>
            )}

            {selectedEvent && (
                <Dialog open={!!selectedEvent} onOpenChange={(open) => !open && setSelectedEvent(null)}>
                    <DialogContent>
                        <DialogHeader>
                            <DialogTitle>{selectedEvent.title}</DialogTitle>
                        </DialogHeader>
                        <div className="py-4">
                            <p><strong>Time:</strong> {selectedEvent.start.toLocaleString()} - {selectedEvent.end.toLocaleString()}</p>
                            {selectedEvent.description && (
                                <p className="mt-2">{selectedEvent.description}</p>
                            )}
                        </div>
                        <div className="flex justify-end gap-2">
                            <Button variant="outline" onClick={() => setSelectedEvent(null)}>Close</Button>
                            <B
[truncated — 4867 more characters]
```

### chronos-frontend/src/app/dashboard/page.jsx

```javascript
import { AppSidebar } from "@/components/app-sidebar"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbList,
  BreadcrumbPage,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import {
  SidebarInset,
  SidebarProvider,
  SidebarTrigger,
} from "@/components/ui/sidebar"

export default function Page() {
  return (
    (<SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        <header
          className="sticky top-0 flex h-16 shrink-0 items-center gap-2 border-b bg-background px-4">
          <SidebarTrigger className="-ml-1" />
          <Separator orientation="vertical" className="mr-2 h-4" />
          <Breadcrumb>
            <BreadcrumbList>
              <BreadcrumbItem>
                <BreadcrumbPage>October 2024</BreadcrumbPage>
              </BreadcrumbItem>
            </BreadcrumbList>
          </Breadcrumb>
        </header>
        <div className="flex flex-1 flex-col gap-4 p-4">
          <div className="grid auto-rows-min gap-4 md:grid-cols-5">
            {Array.from({ length: 20 }).map((_, i) => (
              <div key={i} className="aspect-square rounded-xl bg-muted/50" />
            ))}
          </div>
        </div>
      </SidebarInset>
    </SidebarProvider>)
  );
}

```

### chronos-frontend/src/app/lee/page.js

```javascript
'use client'
import React, { useEffect } from 'react';
import { Manrope } from 'next/font/google';
import { Calendar } from '@/components/ui/calendar';
import Preferences from '@/components/Preferences';
import { useState } from 'react';
import WeeklyCalendar from '@/components/WeeklyCalendar';
import { Button } from '@/components/ui/button';
import ChatInterface from '@/components/ChatInterface';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Loader2 } from "lucide-react";


const manrope = Manrope({ subsets: ['latin'] })


const transformGoogleEvents = (googleEvents) => {
    return googleEvents.map(event => ({
        id: event.id,
        title: event.summary,
        start: new Date(event.start.dateTime || event.start.date),
        end: new Date(event.end.dateTime || event.end.date),
        color: getColorFromId(event.colorId), // We'll define this function
        description: event.description || ''
    }));
};

// Google Calendar color IDs to hex colors mapping
const getColorFromId = (colorId) => {
    const colorMap = {
        '1': '#7986cb', // Lavender
        '2': '#33b679', // Sage
        '3': '#8e24aa', // Grape
        '4': '#e67c73', // Flamingo
        '5': '#f6c026', // Banana
        '6': '#f5511d', // Tangerine
        '7': '#039be5', // Peacock
        '8': '#616161', // Graphite
        '9': '#3f51b5', // Blueberry
        '10': '#0b8043', // Basil
        'default': '#3b82f6' // Default blue
    };
    return colorMap[colorId] || colorMap.default;
};

function CalendarPage({ selectedMonthDate, events1, setEvents1 }) {
    const [events, setEvents] = useState([]);
    const [selectedEvent, setSelectedEvent] = useState(null);
    const [isLoggedIn, setIsLoggedIn] = React.useState(false);
    const [authToken, setAuthToken] = React.useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [selectedDate, setSelectedDate] = useState(selectedMonthDate ?? new Date())


    useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const token = params.get('token');

        if (token) {
            localStorage.setItem('authToken', token);
            window.history.replaceState({}, '', '/lee');
            setAuthToken(token);
        } else {
            const savedToken = localStorage.getItem('authToken');
            if (savedToken) {
                setAuthToken(savedToken);
            }
        }
    }, []);

    useEffect(() => {
        if (selectedMonthDate) {
            setSelectedDate(selectedMonthDate);
        }
    }, [selectedMonthDate]);

    useEffect(() => {
        const checkAuthAndFetchEvents = async () => {
            try {
                const authResponse = await fetch('http://127.0.0.1:5000/api/auth-status', {
                    credentials: 'include'
                });

                if (authResponse.ok) {
                    const authData = await authResponse.json();

                    if (authData.isAuthenticated) {
                        const eventsResponse = await fetch('http://127.0.0.1:5000/api/events', {
                            credentials: 'include'
                        });

                        if (eventsResponse.ok) {
                            const eventsData = await eventsResponse.json();
                            setEvents1(eventsData.events);
                            setIsLoggedIn(true);
                        } else {
                            window.location.href = 'http://127.0.0.1:5000/login';
                        }
                    } else {
                        window.location.href = 'http://127.0.0.1:5000/login';
                    }
                }
            } catch (error) {
                console.error('Error:', error);
                window.location.href = 'http://127.0.0.1:5000/login';
            }
        };

        if (!isLoggedIn) {
            checkAuthAndFetchEvents();
        }
    }, [isLoggedIn]);

    // Modify this useEffect to remove the artificial delay
    useEffect(() => {
        if (events1.length > 0) {
            setIsLoading(true);
            const transformedEvents = transformGoogleEvents(events1);
            setEvents(transformedEvents);
            setIsLoading(false);
        } else {
            setIsLoading(true);
        }
    }, [events1]);

    const handleEventClick = (event) => {
        setSelectedEvent(event);
    };

    return (
        <div className="container h-full pt-[2%] relative">
            <WeeklyCalendar
                events={isLoading ? [] : events}
                onEventClick={handleEventClick}
                setEvents1={setEvents1}
                selectedDate={selectedDate}
            />

            {isLoading && (
                <div className="absolute inset-0 bg-white/50 backdrop-blur-sm flex items-center justify-center">
                    <Loader2 className="h-8 w-8 animate-spin text-gray-500" />
                </div>
            )}

            {selectedEvent && (
                <Dialog open={!!selectedEvent} onOpenChange={(open) => !open && setSelectedEvent(null)}>
                    <DialogContent>
                        <DialogHeader>
                            <DialogTitle>{selectedEvent.title}</DialogTitle>
                        </DialogHeader>
                        <div className="py-4">
                            <p><strong>Time:</strong> {selectedEvent.start.toLocaleString()} - {selectedEvent.end.toLocaleString()}</p>
                            {selectedEvent.description && (
                                <p className="mt-2">{selectedEvent.description}</p>
                            )}
                        </div>
                        <div className="flex justify-end gap-2">
                            <Button variant="outline" onClick={() => setSelectedEvent(null)}>Close</Button>
                            <Button variant="destructive">Delete Event</Bu
[truncated — 4669 more characters]
```

### chronos-frontend/src/app/neha/page.js

```javascript
'use client'
import React, { useEffect } from 'react';
import { Manrope } from 'next/font/google';
import { Calendar } from '@/components/ui/calendar';
import Preferences from '@/components/Preferences';
import { useState } from 'react';
import WeeklyCalendar from '@/components/WeeklyCalendar';
import { Button } from '@/components/ui/button';
import ChatInterface from '@/components/ChatInterface';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Loader2 } from "lucide-react";


const manrope = Manrope({ subsets: ['latin'] })

const DemoContent = ({ className, title }) => (
    <div className={`p-4 border border-gray-200 rounded-lg bg-white shadow-sm ${className}`}>
        <h1 className="text-2xl font-semibold text-gray-700 mb-2">{title}</h1>
        <div className="w-full h-full min-h-[100px rounded flex items-center justify-center">
            <span className="text-gray-500">{title} Content</span>
        </div>
    </div>
);

// For demonstration purposes, we'll create some sample events
const generateSampleEvents = () => {
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

    return [
        {
            id: '1',
            title: 'Team Meeting',
            start: new Date(today.getTime() + 10 * 60 * 60 * 1000), // 10:00 AM
            end: new Date(today.getTime() + 11 * 60 * 60 * 1000),   // 11:00 AM
            color: '#3b82f6',
            description: 'Weekly team sync'
        },
        {
            id: '2',
            title: 'Lunch Break',
            start: new Date(today.getTime() + 12 * 60 * 60 * 1000), // 12:00 PM
            end: new Date(today.getTime() + 13 * 60 * 60 * 1000),   // 1:00 PM
            color: '#10b981',
            description: 'Take a break!'
        },
        {
            id: '3',
            title: 'Client Call',
            start: new Date(today.getTime() + 24 * 60 * 60 * 1000 + 14 * 60 * 60 * 1000), // Tomorrow 2:00 PM
            end: new Date(today.getTime() + 24 * 60 * 60 * 1000 + 15 * 60 * 60 * 1000),   // Tomorrow 3:00 PM
            color: '#f59e0b',
            description: 'Discuss new project requirements'
        }
    ];
};

const transformGoogleEvents = (googleEvents) => {
    return googleEvents.map(event => ({
        id: event.id,
        title: event.summary,
        start: new Date(event.start.dateTime || event.start.date),
        end: new Date(event.end.dateTime || event.end.date),
        color: getColorFromId(event.colorId), // We'll define this function
        description: event.description || ''
    }));
};

// Google Calendar color IDs to hex colors mapping
const getColorFromId = (colorId) => {
    const colorMap = {
        '1': '#7986cb', // Lavender
        '2': '#33b679', // Sage
        '3': '#8e24aa', // Grape
        '4': '#e67c73', // Flamingo
        '5': '#f6c026', // Banana
        '6': '#f5511d', // Tangerine
        '7': '#039be5', // Peacock
        '8': '#616161', // Graphite
        '9': '#3f51b5', // Blueberry
        '10': '#0b8043', // Basil
        'default': '#3b82f6' // Default blue
    };
    return colorMap[colorId] || colorMap.default;
};

function CalendarPage({ selectedMonthDate }) {
    const [events, setEvents] = useState([]);
    const [selectedEvent, setSelectedEvent] = useState(null);
    const [isLoggedIn, setIsLoggedIn] = React.useState(false);
    const [events1, setEvents1] = React.useState([]);
    const [authToken, setAuthToken] = React.useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [selectedDate, setSelectedDate] = useState(selectedMonthDate) || new Date();

    useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const token = params.get('token');

        if (token) {
            localStorage.setItem('authToken', token);
            window.history.replaceState({}, '', '/lee');
            setAuthToken(token);
        } else {
            const savedToken = localStorage.getItem('authToken');
            if (savedToken) {
                setAuthToken(savedToken);
            }
        }
    }, []);

    useEffect(() => {
        if (selectedMonthDate) {
            setSelectedDate(selectedMonthDate);
        }
    }, [selectedMonthDate]);

    useEffect(() => {
        const checkAuthAndFetchEvents = async () => {
            try {
                const authResponse = await fetch('http://127.0.0.1:5000/api/auth-status', {
                    credentials: 'include'
                });

                if (authResponse.ok) {
                    const authData = await authResponse.json();

                    if (authData.isAuthenticated) {
                        const eventsResponse = await fetch('http://127.0.0.1:5000/api/events', {
                            credentials: 'include'
                        });

                        if (eventsResponse.ok) {
                            const eventsData = await eventsResponse.json();
                            setEvents1(eventsData.events);
                            setIsLoggedIn(true);
                        } else {
                            window.location.href = 'http://127.0.0.1:5000/login';
                        }
                    } else {
                        window.location.href = 'http://127.0.0.1:5000/login';
                    }
                }
            } catch (error) {
                console.error('Error:', error);
                window.location.href = 'http://127.0.0.1:5000/login';
            }
        };

        if (!isLoggedIn) {
            checkAuthAndFetchEvents();
        }
    }, [isLoggedIn]);

    // Transform and combine events when events1 changes
    useEffect(() => {
        let timeoutId;

        if (events1.length > 0) {
            setIsLoading(true);
            timeoutId = setTimeout(() => {
                const transformedEvents = transformGoogleEvents(events1);
              
[truncated — 5515 more characters]
```

### chronos-backend/search_engine.py

```python
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction
from datetime import datetime
import dotenv
import uuid
import os
import logging

logging.basicConfig(level=logging.INFO)

dotenv.load_dotenv()

class SearchEngine:
    def __init__(self):
        self.client = None
        self.embedder = None
        self.collection = None
        self.initialize()

    def initialize(self):
        try:
            logging.info(f"Initializing ChromaDB version: {chromadb.__version__}")
            self.client = chromadb.HttpClient(
                ssl=True,
                host='api.trychroma.com',
                tenant=os.getenv('CHROMA_TENANT_KEY'),
                database='Chronos',
                headers={
                    'x-chroma-token': os.getenv('CHROMA_API_KEY')
                }
            )
            
            # Test the connection and log server version
            heartbeat = self.client.heartbeat()
            logging.info(f"Connected to ChromaDB server. Heartbeat: {heartbeat}")
            
            self.embedder = SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")
            self.collection = self.client.get_or_create_collection(
                name="calendar_events",
                embedding_function=self.embedder
            )
            logging.info("Successfully initialized ChromaDB connection")
        except Exception as e:
            logging.error(f"Failed to initialize ChromaDB: {str(e)}")
            raise

    def update_events_in_chroma(self, events):
        try:
            existing = self.collection.get()
            if existing and existing['ids']:
                self.collection.delete(ids=existing['ids'])
                logging.info(f"Cleared {len(existing['ids'])} existing events from collection")

            CHUNK_SIZE = 10
            documents = []
            metadatas = []
            ids = []
            
            for i in range(0, len(events), CHUNK_SIZE):
                chunk = events[i:i + CHUNK_SIZE]
                chunk_text = ""
                
                for event in chunk:
                    try:
                        event_text = stringify_event(event)
                        chunk_text += event_text + " "
                    except Exception as e:
                        logging.error(f"Error processing event: {str(e)}")
                        continue
                
                if chunk_text:
                    chunk_id = f"chunk_{i//CHUNK_SIZE}"
                    documents.append(chunk_text)
                    metadatas.append({
                        "idx": i//CHUNK_SIZE,
                        "size": len(chunk),
                        "ts": datetime.now().strftime("%Y%m%d")
                    })
                    ids.append(chunk_id)

            if documents:
                self.collection.add(
                    documents=documents,
                    metadatas=metadatas,
                    ids=ids
                )
                logging.info(f"Added {len(documents)} chunks containing {len(events)} events to collection")

            return True

        except Exception as e:
            logging.error(f"Error in update_events_in_chroma: {str(e)}")
            return False

    def search_events(self, query_text, n_results=5):
        try:
            results = self.collection.query(
                query_texts=[query_text],
                n_results=n_results
            )
            return results
        except Exception as e:
            logging.error(f"Error in search_events: {str(e)}")
            return None

# Create a singleton instance
search_engine = SearchEngine()

# Export the methods to maintain backwards compatibility
def update_events_in_chroma(events):
    return search_engine.update_events_in_chroma(events)

def search_events(query_text, n_results=5):
    return search_engine.search_events(query_text, n_results)

def stringify_event(event):
    parts = []
    if 'summary' in event:
        parts.append(f"Event '{event['summary']}'")
    else:
        parts.append("Untitled event")
    
    if 'start' in event:
        start_time = event['start'].get('dateTime', event['start'].get('date'))
        if start_time:
            if 'T' in start_time:
                start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
                start_str = start_dt.strftime("%B %d, %Y at %I:%M %p")
            else:
                start_str = datetime.fromisoformat(start_time).strftime("%B %d, %Y")
            parts.append(f"starts on {start_str}")
    
    if 'end' in event:
        end_time = event['end'].get('dateTime', event['end'].get('date'))
        if end_time:
            if 'T' in end_time:
                end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00'))
                end_str = end_dt.strftime("%I:%M %p")
                parts.append(f"ends at {end_str}")
            else:
                end_str = datetime.fromisoformat(end_time).strftime("%B %d, %Y")
                parts.append(f"ends on {end_str}")
    
    if 'attendees' in event:
        attendee_emails = [attendee['displayName'] if 'displayName' in attendee else attendee['email'] for attendee in event['attendees']]
        if len(attendee_emails) > 0:
            parts.append(f"with attendees {', '.join(attendee_emails)}")
    
    if 'description' in event and event['description']:
        parts.append(f"with description: {event['description']}")

    if 'recurrence' in event:
        parts.append(f" and recurs every {event['recurrence']}")
    
    return ' '.join(parts) + ". "

```

### chronos-frontend/src/lib/utils.js

```javascript
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
  return twMerge(clsx(inputs));
}

```

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