AGENTS.md — ScrapYard (Hack the Globe 2026)
Construction surplus materials marketplace with AI-powered matching and carbon accounting. Hackathon project for Hack the Globe 2026 (Sustainable Infrastructure theme).
Project Structure
hack-the-globe/
web/ # PRIMARY APP — Next.js 16 + Convex (TypeScript)
src/
components/ # Shared components (Layout, Logo, AuthContext, ui/)
pages/ # Next.js Pages Router routes
styles/ # Global CSS (Tailwind)
lib/ # Utilities (cn helper)
convex/ # Convex backend (schema, queries, mutations)
public/ # Static assets (logos, icons)
package.json
components.json # shadcn/ui config (radix-nova style)
scrapyard/ # OLD MOCKUP — React + Vite prototype (JavaScript, do NOT develop here)
pitch/ # Pitch plan, feedback notes
docs/ # Hackathon guides, brainstorming, research
.agents/skills/ # Convex agent skill definitions
All active development happens in web/. The scrapyard/ directory is an old mockup prototype.
Build / Lint / Dev Commands
Package manager: Bun
# All commands run from web/
cd web
# Install dependencies
bun install
# Development server (Next.js + Convex)
bun run dev
# Production build
bun run build
# Lint (ESLint 9 + next core-web-vitals + typescript)
bun run lint
There is no test framework configured.
Tech Stack
| Layer | Technology |
|---|---|
| Language | TypeScript (.tsx / .ts) |
| Framework | Next.js 16 (Pages Router) |
| React | 19.2 |
| Backend / DB | Convex (real-time cloud database) |
| AI / LLM | OpenRouter AI SDK + Gemini (google/gemini-3.1-flash-lite-preview) |
| Styling | Tailwind CSS 4 (@tailwindcss/postcss) |
| UI Components | shadcn/ui (radix-nova style, Radix UI primitives) |
| Icons | lucide-react |
| Charts | Recharts (via shadcn chart wrapper) |
| Maps | Leaflet + react-leaflet (dynamic import, no SSR) |
| Validation | Zod 4 |
| Font | Outfit (Google Fonts) |
| Auth | Custom localStorage-based username (no real auth) |
| Pkg manager | Bun |
Convex Data Model
Three tables in convex/schema.ts:
listings
title, category, quantity, unit, price, originalPrice, seller, sellerRating, sellerVerified, sellerTransactions, carbonSaved, image (optional string), images (optional array of storage IDs), condition, description, location, lat, lng
Search index: by_category (searchField: title, filterFields: ["category"])
requests
title, category, budget, urgency, requester
messages
listingId (Id<"listings">), sender, body
Index: by_listingId on ["listingId"]
Convex Backend Functions
| File | Functions |
|---|---|
listings.ts | list, getById, listByCategory, create, remove, generateUploadUrl |
messages.ts | list, send |
requests.ts | list, create, remove |
seed.ts | seedAll, resetAndSeed |
Routes (Pages Router)
| Path | Page File | Description |
|---|---|---|
/ | index.tsx | Home — tabs: Open Requests + My Listings |
/browse/[category] | browse/[category].tsx | Filtered listing grid with search |
/listing/[id] | listing/[id].tsx | Single listing detail + seller info |
/post | post.tsx | Batch AI scanner — multi-item detection + bulk post |
/scan | scan.tsx | Redirects to /post?scan=1 |
/map | map.tsx | Leaflet map with listing pins (not in main nav) |
/carbon | carbon.tsx | Carbon impact dashboard (3 charts) |
/profile | profile.tsx | User profile + sign out |
/request | request.tsx | Chat-based AI request posting |
/chat/[id] | chat/[id].tsx | Real-time chat per listing |
/api/scan | api/scan.ts | AI scanner — multi-item detection (Gemini) |
/api/parse-request | api/parse-request.ts | AI request parser — natural language to structured data |
shadcn/ui Components (Installed)
button, card, input, badge, dialog, label, chart, tabs
Config: radix-nova style, no RSC (rsc: false), neutral base color, CSS variables enabled.
Install more with: npx shadcn@latest add <component>
Shared Components
| Component | File | Purpose |
|---|---|---|
Layout | components/Layout.tsx | App shell + 5-tab bottom navigation |
Logo | components/Logo.tsx | Logo image component |
AuthContext | components/AuthContext.tsx | React Context for localStorage auth |
NamePrompt | components/NamePrompt.tsx | First-visit username entry prompt |
Code Style Guidelines
TypeScript
- All code is TypeScript (
.tsx/.ts) - Use proper types for Convex documents and IDs
- Zod for runtime validation (AI scanner responses)
Imports
- Path alias:
@/*maps to./src/* - shadcn/ui imports:
@/components/ui/button - Convex imports:
convex/_generated/apiandconvex/_generated/dataModel - Single quotes for import strings
Component Patterns
- Default exports for page components
- Named exports for shared/ui components
- Props typed inline or with interfaces
- Pages Router conventions:
useRouter()fromnext/router
Styling
- Tailwind CSS 4 for all styling — use utility classes
- CSS custom properties in
globals.cssfor theme tokens (OKLCH color space) - shadcn/ui components for consistent UI primitives
- No inline
style={{}}objects — use Tailwind classes instead - Animations:
fade-up,fade-in,slide-upwith stagger classes - Mobile-first: max-width 430px app shell
State Management
- Convex
useQueryfor data fetching (reactive subscriptions) - Convex
useMutationfor writes useAuth()custom hook for username (localStorage)- Local
useStatefor UI state useRouter()fromnext/routerfor navigation
Categories (Hardcoded)
lumber, steel, concrete, brick, glass, pipe, electrical, fixtures
These are currently defined as constants in multiple page files (not centralized).
Environment Variables
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_CONVEX_URL | Convex deployment URL |
OPENROUTER_API_KEY | API key for AI scanner endpoint |
Key Reminders for Agents
- Work in
web/—scrapyard/is an old mockup, do not develop there - Use Bun, not npm/yarn
- Use TypeScript — this is not a JavaScript project
- Use Tailwind classes for styling, not inline style objects
- Use shadcn/ui components (install with
npx shadcn@latest add <name>) - Read
convex/_generated/ai/guidelines.mdbefore writing Convex code - Data is real (Convex), not mocked — use
useQuery/useMutation - Pages Router (
src/pages/), NOT App Router - Run
bun run lintafter changes - New pages go in
src/pages/, new shared components insrc/components/ - AI scanner is real (Gemini via OpenRouter), not a mock
- No SSR for Leaflet/map components — use
next/dynamicwithssr: false