# Project export: Project Lend

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 2026
- Tagline: An autonomous food bank run entirely by AI + robotics, sourcing and distributing over 50lbs of food during TreeHacks to Palo Alto shelters in 1 weekend!
- Devpost: https://devpost.com/software/project-lend
- GitHub: https://github.com/PranavViswanath/project-lend
- Video: https://www.youtube.com/embed/hBcCNg_U3t4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner ([Anthropic] Human Flourishing Track (1st Place: 4 tungsten cubes 2nd Place: 1 year of Claude Pro 3rd Place: 6 months of Claude Pro))
- Team: 4 GitHub contributor(s) — sathvik vang (5 commits), Claude Opus 4.6 (2 commits), PranavViswanath (2 commits), Warp (2 commits)

## Devpost submission (written by the team)

### Overview

AI-powered autonomous food rescue, because giving should be as smart as taking

### Inspiration

We've spent years volunteering at food banks and shelters, experiencing firsthand the challenges of both front-office admin and back-breaking logistics. When Anthropic released Project Vend showing Claude could run a business autonomously, we wondered- what if we applied that same capability to helping people give instead of take? With agents now able to reason, communicate, and operate in the physical world, Project Lend was born at the intersection of technical possibility and human need.

### What it does

Project Lend is an autonomous food bank run entirely by AI. Over TreeHacks weekend, our agent system sorted donations from fellow builders using a robotic arm with computer vision, coordinated with 3+ shelters in Palo Alto, and delivered over 100 lbs of food in one weekend. The system handles everything—intake, sorting, inventory, shelter coordination, scheduling, and delivery logistics—completely autonomously. Donors text to contribute, the robotic arm sorts items into shelter-specific boxes, and our agents coordinate pickup times with actual shelters. No human intervention required.

### How we built it

We basically replicated the entire front and back office of a traditional food rescue operation using the Claude Agent SDK as the brain: Physical Operations: Robotic arm sorted donations using Claude Vision API (lightning fast on Haiku 3.5!), reasoning over resource allocation from our inventory database Computer vision pipeline identified and categorized food in real-time Data streamed to our frontend dashboard for live visibility Coordination Layer: Orchestrator agent (inspired by Project Vend's "boss" architecture) coordinated with local shelters using Fetch.ai email agents Multi-agent system with specialized sub-agents for sorting, scheduling, donor engagement, and allocation Donor Engagement: Text-based agent powered by Interaction Company's Poke sent personalized messages to TreeHacks participants, raising over $100 for our GoFundMe Development Stack: Claude Code and Warp IDE agents for rapid iteration React frontend with real-time database syncing Python hardware integration with custom MCP tools Challenges Network outage at 3 AM forced a complete hardware stack pivot. Robot calibration needed constant fine-tuning. Agent concurrency issues when multiple sub-agents accessed shared resources. Real-world shelter schedules (nonprofits don't always respond to AI emails immediately!). Computer vision reliability under hackathon lighting. The usual chaos. Accomplishments Sourced over 100 lbs of food organically through the hackathon community Autonomous coordination with actual shelters—REAL coordination, not mocked, resulting in scheduled Monday deliveries Working computer vision/robotic sorting pipeline $100 raised through AI agent interactions Full multi-agent orchestration handling intake through delivery Proved AI agents can run real-world service operations, not just digital workflows

### What we learned

AI in the real world is messy, but it's real. We learned how to handle 5 AM networking disasters, make agents resilient to unpredictability, and ship something that genuinely helps people in 36 hours. When you build something that actually matters, the energy to push through just appears. Most importantly—the gap between "AI demo" and "AI doing real work in the physical world" is closable right now with today's tools. It's not science fiction anymore.

### What's next

We're delivering to 2 shelters Monday morning! Next steps: scaling robotic sorting for higher volumes, integrating more Bay Area shelters, building partnerships with grocery stores and restaurants for regular donations, creating a network of autonomous food banks that share resources, and potentially open-sourcing our orchestration framework so other communities can deploy their own AI-powered food rescue. The future where AI helps humans give, not just take, starts here. 🌲❤️

## README (from the GitHub repository)

# Project Lend

Autonomous food bank built at TreeHacks 2026! xArm 1S sorts donated food, Claude agents coordinate with donors + shelters.

## Hardware

- Hiwonder xArm 1S over USB (connected to laptop)
- Laptop webcam for vision classification

## Setup (Laptop)

```powershell
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install -r requirements.txt
```

Set your API key before running vision features:

```powershell
$env:ANTHROPIC_API_KEY="YOUR_API_KEY"
```

## Calibrate Arm Positions

Physically move the arm to each position and record servo values:

```powershell
python calibrate.py
```

Copy the output into `positions.py`.

## Run the Full System

### 1. Start the API (terminal 1)

```powershell
python api.py
```

Runs on http://localhost:5000

### 2. Open the Dashboard

Open `dashboard.html` in your browser. It will show:
- Live stats (items, weight, donors)
- Category breakdown (fruit/snack/drink)
- Real-time donation feed with Claude's detailed classifications

### 3. Run the Pipeline (terminal 2)

**Test mode (manual capture):**
```powershell
python test_pipeline.py
```
Press SPACE to capture and classify.

**Auto mode (motion detection):**
```powershell
python main.py
```
Watches for items placed in front of camera, auto-classifies and sorts.

**Vision-only (no arm):**
```powershell
python main.py --no-arm
```

## Quick Tests

**Test arm:**
```powershell
python test_arm.py
```

**Test camera + vision:**
```powershell
python test_vision.py
```

**Test camera demo:**
```powershell
python camera_demo.py --camera 0
```

## Data

- Donations logged to: `donations.json`
- Captured images saved to: `images/`
- API endpoints:
  - GET `/donations` - all records
  - GET `/donations/recent?limit=10` - latest N
  - GET `/stats` - summary stats


## Detected evidence (automated analysis)

Indexed codebase: 121 recognized source files, 422 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- HTML (language) — 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
- TypeScript (language) — detected in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 161)

```
.agents/skills/ui-ux-pro-max/data/charts.csv
.agents/skills/ui-ux-pro-max/data/colors.csv
.agents/skills/ui-ux-pro-max/data/icons.csv
.agents/skills/ui-ux-pro-max/data/landing.csv
.agents/skills/ui-ux-pro-max/data/products.csv
.agents/skills/ui-ux-pro-max/data/react-performance.csv
.agents/skills/ui-ux-pro-max/data/stacks/astro.csv
.agents/skills/ui-ux-pro-max/data/stacks/flutter.csv
.agents/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv
.agents/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv
.agents/skills/ui-ux-pro-max/data/stacks/nextjs.csv
.agents/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv
.agents/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv
.agents/skills/ui-ux-pro-max/data/stacks/react-native.csv
.agents/skills/ui-ux-pro-max/data/stacks/react.csv
.agents/skills/ui-ux-pro-max/data/stacks/shadcn.csv
.agents/skills/ui-ux-pro-max/data/stacks/svelte.csv
.agents/skills/ui-ux-pro-max/data/stacks/swiftui.csv
.agents/skills/ui-ux-pro-max/data/stacks/vue.csv
.agents/skills/ui-ux-pro-max/data/styles.csv
.agents/skills/ui-ux-pro-max/data/typography.csv
.agents/skills/ui-ux-pro-max/data/ui-reasoning.csv
.agents/skills/ui-ux-pro-max/data/ux-guidelines.csv
.agents/skills/ui-ux-pro-max/data/web-interface.csv
.agents/skills/ui-ux-pro-max/scripts/core.py
.agents/skills/ui-ux-pro-max/scripts/design_system.py
.agents/skills/ui-ux-pro-max/scripts/search.py
.agents/skills/ui-ux-pro-max/SKILL.md
.agents/skills/vercel-react-best-practices/AGENTS.md
.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md
.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md
.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md
.agents/skills/vercel-react-best-practices/rules/async-api-routes.md
.agents/skills/vercel-react-best-practices/rules/async-defer-await.md
.agents/skills/vercel-react-best-practices/rules/async-dependencies.md
.agents/skills/vercel-react-best-practices/rules/async-parallel.md
.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md
.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md
.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md
.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md
.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md
.agents/skills/vercel-react-best-practices/rules/bundle-preload.md
.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md
.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md
.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md
.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md
.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md
.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md
.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md
.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md
.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md
.agents/skills/vercel-react-best-practices/rules/js-early-exit.md
.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md
.agents/skills/vercel-react-best-practices/rules/js-index-maps.md
.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md
.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md
.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md
.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md
.agents/skills/vercel-react-best-practices/rules/rendering-activity.md
.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md
.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md
.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md
.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md
.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md
.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md
.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md
.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md
.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md
.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md
.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md
.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md
.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md
.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md
.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md
.agents/skills/vercel-react-best-practices/rules/rerender-memo.md
.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md
.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md
.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md
.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md
.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md
.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md
.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md
.agents/skills/vercel-react-best-practices/rules/server-cache-react.md
.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md
.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md
.agents/skills/vercel-react-best-practices/rules/server-serialization.md
.agents/skills/vercel-react-best-practices/SKILL.md
.claude/skills/ui-ux-pro-max
.claude/skills/vercel-react-best-practices
.gitignore
CLAUDE.md
dashboard/.gitignore
dashboard/app/globals.css
dashboard/app/layout.tsx
dashboard/app/page.tsx
dashboard/components/CameraFeed.tsx
dashboard/components/CategoryBreakdown.tsx
dashboard/components/DonationFeed.tsx
dashboard/components/LiveBadge.tsx
dashboard/components/StatsCards.tsx
dashboard/eslint.config.mjs
dashboard/hooks/useApi.ts
dashboard/lib/api.ts
dashboard/lib/types.ts
dashboard/next.config.ts
dashboard/package.json
dashboard/postcss.config.mjs
dashboard/README.md
dashboard/tsconfig.json
docs/AGENTS.md
docs/DASHBOARD.md
docs/QUICKSTART.md
docs/VERIFICATION.md
frontend/.gitignore
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
[41 more files omitted for size]
```

### Dependencies

- dashboard/package.json: @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, eslint@^9, eslint-config-next@16.1.6, framer-motion@^12.34.0, next@16.1.6, react@19.2.3, react-dom@19.2.3, swr@^2.4.0, tailwindcss@^4, typescript@^5
- frontend/package.json: @eslint/js@^9.39.1, @types/node@^24.10.1, @types/react@^19.2.7, @types/react-dom@^19.2.3, @vitejs/plugin-react@^5.1.1, eslint@^9.39.1, eslint-plugin-react-hooks@^7.0.1, eslint-plugin-react-refresh@^0.4.24, globals@^16.5.0, react@^19.2.0, react-dom@^19.2.0, typescript@~5.9.3, typescript-eslint@^8.48.0, vite@^7.3.1
- requirements.txt: anthropic@>=0.40.0, claude-agent-sdk@>=0.1.30, flask@>=3.0.0, flask-cors@>=4.0.0, mcp@>=1.0.0, opencv-python@>=4.8.0, uagents@>=0.17.0, xarm@==0.0.4

### Recent commits (newest first)

- Update project description in README.md
- Add auto motion detection, new arm sort flow, recalibrated positions
- Refactor: organize project into lend/ package structure
- Merge remote-tracking branch 'origin/main'
- Local changes to api.py and skills
- live frontend
- arducam, haiku
- fu
- vision
- arm control
- servo off
- Add calibration + positions, resolve merge conflict
- updated code to test all the servos
- Initial commit: xArm 1S test setup

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

### CLAUDE.md

```markdown
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**Project Lend** is an autonomous food bank system for TreeHacks 2026 that combines:
- Hiwonder xArm 1S robotic arm (USB-connected)
- Camera-based vision classification using Claude API
- State machine pipeline with motion detection and automated sorting

The system detects donated items via motion detection, classifies them into categories (fruit/snack/drink), logs donation data, and sorts items into appropriate bins.

## Environment Setup

```bash
python -m venv venv
source venv/bin/activate  # or .\venv\Scripts\Activate.ps1 on Windows
pip install -r requirements.txt
pip install -e .           # install lend package in editable mode
```

Required environment variable:
```bash
export ANTHROPIC_API_KEY="sk-..."
```

Optional override for Claude model (defaults to `claude-3-5-haiku-latest`):
```bash
export CLAUDE_VISION_MODEL="claude-3-5-haiku-latest"
```

## Common Commands

### Hardware Calibration
Run FIRST when setting up new positions or after hardware changes:
```bash
python tools/calibrate.py
```
Physically move the arm to each position, press Enter to record servo values, then copy output into `lend/hardware/positions.py`.

### Testing Individual Components
```bash
# Detect available cameras
python tools/detect_cameras.py

# Test arm only (no vision)
python tools/test_arm.py

# Test vision only (requires ANTHROPIC_API_KEY)
python tools/test_vision.py

# Test camera preview
python tools/camera_demo.py --camera 0  # or --camera 1 for ArduCam
```

### Running the Full System

**Terminal 1 - Start API server:**
```bash
python run_api.py                    # runs on http://localhost:5000
python run_api.py --port 8080        # custom port
```

**Browser - Open dashboard:**
Open `static/dashboard.html` to view live stats, category breakdown, and donation feed.

**Terminal 2 - Run pipeline:**
```bash
# Manual test mode (press SPACE to capture)
python tools/test_pipeline.py --camera 1

# Auto mode (motion detection)
python run_pipeline.py --camera 1

# Vision-only mode (no arm control)
python run_pipeline.py --no-arm
```

Default camera is 1 (ArduCam). Use `--camera 0` for laptop webcam.

## Architecture

### Package Layout

```
lend/                        # Main Python package
├── hardware/                # xArm control + calibrated positions
│   ├── arm_control.py
│   └── positions.py
├── vision/
│   └── classifier.py        # Claude-based image classification
├── data/
│   ├── donations.py          # JSON-backed donation log
│   ├── pipeline_state.py     # Thread-safe in-memory state
│   └── runtime_state.py      # File-based state for frontend sync
├── pipeline/
│   └── main.py               # 5-state detection/sorting pipeline
├── api/
│   └── server.py             # Flask REST API
└── agents/                   # Claude Agent SDK orchestration
    ├── orchestrator.py
    ├── email_agent.py
    ├── shelter_r
[truncated — 5152 more characters]
```

### docs/QUICKSTART.md

```markdown
# Quick Start Guide - Project Lend Dashboard

## One-Time Setup

```bash
cd /Users/vikvang/Projects/project-vend/dashboard
npm install  # Already done, but run if needed
```

## Running the Dashboard

### Option 1: Dashboard Only (No Live Camera)

**Terminal 1 - Start Flask API:**
```bash
cd /Users/vikvang/Projects/project-vend
python api.py --camera 1
```

**Terminal 2 - Start Dashboard:**
```bash
cd dashboard
npm run dev
```

**Browser:**
Open `http://localhost:3000`

**What you'll see:**
- "Camera Offline" placeholder
- Stats (if donations exist in `donations.json`)
- Category breakdown
- Recent donations

---

### Option 2: Full System (Live Camera + Pipeline)

**Terminal 1 - Start Flask API:**
```bash
cd /Users/vikvang/Projects/project-vend
python api.py --camera 1
```

**Terminal 2 - Start Main Pipeline:**
```bash
cd /Users/vikvang/Projects/project-vend
python main.py --camera 1
```

**Terminal 3 - Start Dashboard:**
```bash
cd dashboard
npm run dev
```

**Browser:**
Open `http://localhost:3000`

**What you'll see:**
- Live camera feed from ArduCam
- Real-time state indicator (WARMUP → WATCHING → SETTLING → CLASSIFYING → COOLDOWN)
- Motion area when items detected
- Stats updating every 2 seconds
- New donations appearing in feed

---

## Testing Without Hardware

**Use laptop webcam:**
```bash
# Terminal 1
python api.py --camera 0  # Note: --camera 0 for built-in webcam

# Terminal 2
python main.py --camera 0  # Note: --camera 0

# Terminal 3
cd dashboard && npm run dev
```

**Vision-only mode (no arm):**
```bash
python main.py --camera 1 --no-arm
```

---

## Stopping Everything

Press `Ctrl+C` in each terminal:
1. Stop dashboard (Terminal 3)
2. Stop main pipeline (Terminal 2)
3. Stop API (Terminal 1)

---

## Troubleshooting

**Camera not opening:**
```bash
# Check available cameras
python detect_cameras.py

# Try different index
python api.py --camera 0  # Built-in webcam
```

**Dashboard shows "Camera Offline":**
- Make sure `main.py` is running (not just API)
- Check `main.py` terminal for camera errors

**No donations showing:**
- Run `main.py` to classify items
- Check `donations.json` has data

**Port already in use:**
```bash
# Use different port
python api.py --port 8080

# Update dashboard/.env.local
# NEXT_PUBLIC_API_URL=http://localhost:8080
```

---

## File Locations

- **API**: `/Users/vikvang/Projects/project-vend/api.py`
- **Pipeline**: `/Users/vikvang/Projects/project-vend/main.py`
- **Dashboard**: `/Users/vikvang/Projects/project-vend/dashboard/`
- **Config**: `/Users/vikvang/Projects/project-vend/dashboard/.env.local`

---

## Common Workflows

### Demo Mode (for presentations)
```bash
# Terminal 1: API + Camera stream
python api.py --camera 1

# Terminal 2: Dashboard
cd dashboard && npm run dev
```

### Full Pipeline Test
```bash
# Terminal 1: API
python api.py --camera 1

# Terminal 2: Main pipeline
python main.py --camera 1

# Terminal 3: Dashboard
cd dashboard && npm run dev

# Place an item in front of camera and 
[truncated — 1442 more characters]
```

### requirements.txt

```
xarm==0.0.4
anthropic>=0.40.0
opencv-python>=4.8.0
flask>=3.0.0
flask-cors>=4.0.0
uagents>=0.17.0
claude-agent-sdk>=0.1.30
mcp>=1.0.0

```

### pyproject.toml

```
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "project-lend"
version = "0.1.0"
description = "Autonomous food bank system with robotic sorting"
requires-python = ">=3.10"

[tool.setuptools.packages.find]
include = ["lend*"]

```

### dashboard/package.json

```
{
  "name": "dashboard",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "framer-motion": "^12.34.0",
    "next": "16.1.6",
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "swr": "^2.4.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.1.6",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.2.0",
    "react-dom": "^19.2.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.39.1",
    "@types/node": "^24.10.1",
    "@types/react": "^19.2.7",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^5.1.1",
    "eslint": "^9.39.1",
    "eslint-plugin-react-hooks": "^7.0.1",
    "eslint-plugin-react-refresh": "^0.4.24",
    "globals": "^16.5.0",
    "typescript": "~5.9.3",
    "typescript-eslint": "^8.48.0",
    "vite": "^7.3.1"
  }
}

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### dashboard/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Space_Grotesk, DM_Sans } from "next/font/google";
import "./globals.css";

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

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

export const metadata: Metadata = {
  title: "Project Lend — Autonomous Food Bank",
  description: "Real-time monitoring dashboard for the autonomous food bank system. TreeHacks 2026.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${spaceGrotesk.variable} ${dmSans.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}

```

### dashboard/app/page.tsx

```typescript
/**
 * Project Lend - Main Dashboard Page
 * Real-time monitoring of autonomous food bank system
 */

import CameraFeed from '@/components/CameraFeed';
import StatsCards from '@/components/StatsCards';
import CategoryBreakdown from '@/components/CategoryBreakdown';
import DonationFeed from '@/components/DonationFeed';
import LiveBadge from '@/components/LiveBadge';

export default function Home() {
  return (
    <main className="min-h-screen bg-[#0F172A] p-4 md:p-6 lg:p-8">
      {/* Subtle gradient overlay */}
      <div className="fixed inset-0 bg-gradient-to-br from-emerald-950/20 via-transparent to-blue-950/20 pointer-events-none" />

      <div className="relative max-w-7xl mx-auto space-y-6">
        {/* Header */}
        <header className="flex items-center justify-between py-4">
          <div className="flex items-center gap-4">
            {/* Logo mark */}
            <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-emerald-500 to-emerald-600 flex items-center justify-center shadow-lg shadow-emerald-500/20">
              <svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" d="M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z" />
              </svg>
            </div>
            <div>
              <h1 className="text-2xl font-bold text-white" style={{ fontFamily: 'var(--font-heading)' }}>
                Project Lend
              </h1>
              <p className="text-slate-400 text-sm">
                Autonomous Food Bank System
              </p>
            </div>
          </div>

          <LiveBadge />
        </header>

        {/* Stats Cards */}
        <StatsCards />

        {/* Main Content Grid */}
        <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
          {/* Camera Feed - Spans 2 columns */}
          <div className="lg:col-span-2">
            <CameraFeed />
          </div>

          {/* Category Breakdown */}
          <div>
            <CategoryBreakdown />
          </div>
        </div>

        {/* Donation Feed - Full Width */}
        <DonationFeed />

        {/* Footer */}
        <footer className="text-center pt-6 pb-4">
          <p className="text-slate-500 text-xs">
            Built for TreeHacks 2026 with Next.js, Claude Vision, and xArm 1S
          </p>
        </footer>
      </div>
    </main>
  );
}

```

### lend/api/server.py

```python
"""REST API for Project Lend donation data.

Endpoints:
    GET /donations          — all donation records
    GET /donations/recent   — last N records (default 10, ?limit=N)
    GET /stats              — summary stats for frontend dashboard
    GET /state              — current pipeline state (motion, classification, cooldown)
    GET /video_feed         — MJPEG camera stream

Usage:
    python api.py                   # runs on port 5000, camera 1
    python api.py --port 8080       # custom port
    python api.py --camera 0        # use laptop webcam
"""

import argparse
import os
import threading
import time
from flask import Flask, jsonify, request, send_file, Response
from flask_cors import CORS
from lend import PROJECT_ROOT
from lend.data import donations
from lend.data.runtime_state import read_pipeline_state, write_pipeline_state, LATEST_FRAME_PATH
from lend.vision.classifier import classify_frame_detailed

IMAGES_DIR = os.path.join(PROJECT_ROOT, "images")
os.makedirs(IMAGES_DIR, exist_ok=True)

_capture_lock = threading.Lock()

app = Flask(__name__)
CORS(app)  # allow frontend to call from any origin


@app.route("/donations", methods=["GET"])
def all_donations():
    return jsonify(donations.get_all())


@app.route("/donations/recent", methods=["GET"])
def recent_donations():
    limit = request.args.get("limit", 10, type=int)
    records = donations.get_all()
    return jsonify(records[-limit:])


@app.route("/stats", methods=["GET"])
def stats():
    return jsonify(donations.get_stats())


@app.route("/pipeline/state", methods=["GET"])
def pipeline_state():
    return jsonify(read_pipeline_state())


@app.route("/pipeline/frame", methods=["GET"])
def pipeline_frame():
    if not os.path.exists(LATEST_FRAME_PATH):
        return jsonify({"error": "no frame available"}), 404
    return send_file(LATEST_FRAME_PATH, mimetype="image/jpeg")


def _generate_mjpeg():
    """Yield MJPEG frames for smooth browser video."""
    last_data = None
    while True:
        if os.path.exists(LATEST_FRAME_PATH):
            try:
                with open(LATEST_FRAME_PATH, "rb") as f:
                    frame = f.read()
                # Only send valid JPEGs (starts with FFD8, ends with FFD9)
                if frame and frame[:2] == b'\xff\xd8' and frame[-2:] == b'\xff\xd9':
                    last_data = frame
            except Exception:
                pass
        if last_data:
            yield (b"--frame\r\n"
                   b"Content-Type: image/jpeg\r\n\r\n" + last_data + b"\r\n")
        time.sleep(0.033)  # ~30 FPS


@app.route("/pipeline/stream")
def pipeline_stream():
    """MJPEG stream for smooth live camera feed."""
    return Response(_generate_mjpeg(),
                    mimetype="multipart/x-mixed-replace; boundary=frame")


@app.route("/pipeline/capture", methods=["POST"])
def pipeline_capture():
    """Capture the current frame, classify with Claude, and log the donation."""
    if not _capture_lock.acquire(blocking=False):
        return jsonify({"error": "Capture already in progress"}), 409

    try:
        write_pipeline_state({
            "mode": "processing",
            "status_text": "Classifying with Claude...",
            "last_result": None,
        })

        if not os.path.exists(LATEST_FRAME_PATH):
            write_pipeline_state({
                "mode": "error",
                "status_text": "No camera frame available",
            })
            return jsonify({"error": "No frame available"}), 404

        with open(LATEST_FRAME_PATH, "rb") as f:
            frame_bytes = f.read()

        info = classify_frame_detailed(frame_bytes)
        category = info["category"]

        img_name = f"donation_{len(donations.get_all()) + 1}.jpg"
        img_path = os.path.join(IMAGES_DIR, img_name)
        with open(img_path, "wb") as f:
            f.write(frame_bytes)

        record = donations.log_donation(
            category=category,
            item_name=info.get("item_name", "unknown"),
            estimated_weight_lbs=info.get("estimated_weight_lbs"),
            estimated_expiry=info.get("estimated_expiry"),
            image_path=img_path,
        )

        result = {
            "donation_id": record["id"],
            "category": category,
            "item_name": info.get("item_name", "unknown"),
            "estimated_weight_lbs": info.get("estimated_weight_lbs"),
            "estimated_expiry": info.get("estimated_expiry"),
            "image_path": img_path,
        }

        write_pipeline_state({
            "mode": "classified",
            "status_text": f"Classified: {info.get('item_name', 'unknown')} ({category})",
            "last_result": result,
        })

        return jsonify(result)

    except Exception as e:
        write_pipeline_state({
            "mode": "error",
            "status_text": f"Classification error: {e}",
        })
        return jsonify({"error": str(e)}), 500

    finally:
        _capture_lock.release()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Project Lend API")
    parser.add_argument("--port", type=int, default=5000)
    args = parser.parse_args()

    print(f"Starting API on http://localhost:{args.port}")
    app.run(host="0.0.0.0", port=args.port, debug=True)

```

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