From 47d6cba1949e6fae2b638dff481723ece5905ece Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Mon, 23 Feb 2026 14:45:18 +1100 Subject: [PATCH] Add justfile --- agents.md | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ justfile | 48 ++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 agents.md create mode 100644 justfile diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..994023c --- /dev/null +++ b/agents.md @@ -0,0 +1,116 @@ +# Agents + +This file describes the project structure and conventions for AI coding agents working on Hey Search. + +## Project Overview + +Hey Search is a privacy-respecting metasearch engine that aggregates results from multiple upstream search engines (Brave, DuckDuckGo, Google, Bing). It has a Python/FastAPI backend and a React/TypeScript frontend. + +## Repository Structure + +``` +hey-search/ +├── backend/ # Python FastAPI backend +│ ├── pyproject.toml # uv/Python dependencies +│ ├── uv.lock # Lockfile +│ └── app/ +│ ├── main.py # FastAPI app entry point, lifespan, CORS, static mount +│ ├── models.py # Pydantic models (WebResult, ImageResult, SearchResponse, EngineInfo, etc.) +│ ├── search.py # Search orchestrator — concurrent engine queries, retry, dedup +│ ├── api/ +│ │ └── routes.py # REST endpoints: /search, /autocomplete, /engines +│ └── engines/ +│ ├── base.py # Abstract SearchEngine class, shared httpx AsyncClient +│ ├── registry.py # Engine registry — load, enable/disable, list +│ ├── brave.py # Brave Search (web + images) +│ ├── duckduckgo.py# DuckDuckGo (web + images) +│ ├── google.py # Google (web + images) +│ └── bing.py # Bing (web + images) +├── frontend/ # React + Vite + TypeScript frontend +│ ├── package.json +│ ├── vite.config.ts # Vite config with Tailwind plugin and /api proxy +│ ├── index.html +│ └── src/ +│ ├── main.tsx # React entry point +│ ├── App.tsx # Main app — home page, results page, routing between them +│ ├── index.css # Tailwind CSS with shadcn theme variables +│ ├── lib/ +│ │ ├── api.ts # API client — search, autocomplete, engines CRUD +│ │ └── utils.ts # cn() helper (clsx + tailwind-merge) +│ ├── hooks/ +│ │ └── useAutocomplete.ts # Debounced autocomplete hook +│ └── components/ +│ ├── SearchBar.tsx # Search input with autocomplete dropdown +│ ├── WebResults.tsx # Web results list (favicons, badges, snippets) +│ ├── ImageResults.tsx # Image grid + lightbox viewer +│ ├── EngineSettings.tsx # Modal to toggle engines on/off +│ └── ErrorToast.tsx # Toast notifications for engine failures +├── Dockerfile # Multi-stage: Node frontend build → Python runtime +├── .dockerignore +├── .gitignore +├── README.md +├── FEATURES.md +└── CHANGELOG.md +``` + +## Tech Stack + +| Layer | Technology | +| -------- | ----------------------------------------------- | +| Backend | Python 3.12+, FastAPI, uvicorn, httpx, Pydantic | +| Retry | tenacity (exponential backoff, 2 attempts) | +| Parsing | lxml, BeautifulSoup4 | +| Frontend | React 19, TypeScript, Vite | +| Styling | Tailwind CSS 4, shadcn theme, lucide-react icons | +| Packages | uv (backend), npm (frontend) | +| Docker | Multi-stage (node:20-slim → python:3.12-slim) | + +## Key Conventions + +### Backend + +- **Dependency management**: Use `uv` (not pip). Run `uv sync` to install, `uv run` to execute. +- **Engine pattern**: Each engine extends `SearchEngine` (in `engines/base.py`) and implements `search_web()`, `search_images()`, and optionally `autocomplete()`. +- **Shared HTTP client**: All engines use `get_http_client()` from `base.py` — a single `httpx.AsyncClient` instance with sensible defaults. +- **Registry**: Engines are registered in `engines/registry.py`. Add new engines there in `load_default_engines()`. +- **Retry**: The search orchestrator in `search.py` wraps engine calls with tenacity retry (2 attempts, exponential backoff). Individual engines should raise on failure rather than swallowing errors. +- **Models**: All API request/response shapes are Pydantic models in `models.py`. +- **API docs**: FastAPI auto-generates OpenAPI spec. Swagger UI at `/docs`, Redoc at `/redoc`. + +### Frontend + +- **Mobile-first**: All components are designed mobile-first, then scale up with Tailwind responsive breakpoints. +- **Path aliases**: Use `@/` to import from `src/` (configured in vite.config.ts and tsconfig.app.json). +- **Styling**: Use Tailwind utility classes with `cn()` for conditional classes. Theme colors reference CSS variables from `index.css`. +- **API layer**: All backend calls go through `src/lib/api.ts`. Components never call `fetch()` directly. +- **State**: App state lives in `App.tsx` via `useState`. No external state management library. + +### Adding a New Search Engine + +1. Create `backend/app/engines/.py` implementing `SearchEngine` +2. Register it in `backend/app/engines/registry.py` → `load_default_engines()` +3. No frontend changes needed — the engine appears automatically in the settings modal + +## Running Locally + +```bash +# Backend +cd backend && uv sync && uv run uvicorn app.main:app --reload --port 8000 + +# Frontend (separate terminal) +cd frontend && npm install && npm run dev + +# Docker (production) +docker build -t hey-search . && docker run -p 8000:8000 hey-search +``` + +## API Endpoints + +| Method | Path | Description | +| ------ | -------------------------- | ------------------------ | +| GET | `/api/search` | Metasearch (web/images) | +| GET | `/api/autocomplete` | Search suggestions | +| GET | `/api/engines` | List engines | +| PUT | `/api/engines/{name}` | Toggle engine on/off | +| GET | `/docs` | Swagger UI | +| GET | `/redoc` | Redoc API docs | diff --git a/justfile b/justfile new file mode 100644 index 0000000..7a4a590 --- /dev/null +++ b/justfile @@ -0,0 +1,48 @@ +# Hey Search — development commands + +set dotenv-load := false + +# List available commands +default: + @just --list + +# Install all dependencies +install: + cd backend && uv sync + cd frontend && npm install + +# Start backend only +backend: + cd backend && uv run uvicorn app.main:app --reload --port 8000 + +# Start frontend only +frontend: + cd frontend && npm run dev + +# Start both backend and frontend (Ctrl+C stops everything) +dev: + #!/usr/bin/env bash + set -e + trap 'echo "Shutting down..."; kill 0; wait' INT TERM + cd backend && uv run uvicorn app.main:app --reload --port 8000 & + cd frontend && npm run dev & + wait + +# Build frontend for production +build-frontend: + cd frontend && npm run build + +# Type-check frontend +check-frontend: + cd frontend && npx tsc --noEmit + +# Build Docker image +docker-build: + docker build -t hey-search . + +# Run Docker image +docker-run: + docker run -p 8000:8000 hey-search + +# Build and run via Docker +docker: docker-build docker-run