This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
semantic-docs is an Astro-based documentation theme with semantic vector search powered by libsql-search. It combines static site generation with server-rendered search using Turso (libSQL) for edge-optimized semantic search capabilities.
# Install dependencies
pnpm install
# Start dev server (runs on http://localhost:4321)
pnpm dev
# Build for production
pnpm build
# Preview production build
pnpm preview# Turso (default - requires .env with credentials)
pnpm db:init # Initialize Turso database schema
pnpm index # Index markdown content to Turso
# Local libSQL (for CI/testing - no .env required)
pnpm db:init:local # Initialize local database (file:local.db)
pnpm index:local # Index to local database
# Run tests
pnpm test
# Linting and formatting
pnpm lint # Check code with Biome
pnpm lint:fix # Auto-fix issues
pnpm format # Format all filesImportant:
- Always run
pnpm index(orpnpm index:local) after adding/modifying content in./contentdirectory before building. - Use
db:initandindexfor production with Turso (loads.envfile) - Use
db:init:localandindex:localfor CI/local testing without Turso credentials - The dev server (
pnpm dev) works with either database type
- Markdown → Database: Content in
./contentis indexed into Turso viascripts/index-content.ts - libsql-search: Handles embedding generation (local), vector storage, and semantic search
- Static Generation: Article pages are pre-rendered at build time using
getStaticPaths() - Server Search: Search API runs server-side at
/api/search.json(requiresoutput: 'server'with Node.js adapter)
- Search.tsx: Client-side search UI with debounced API calls (300ms), displays results in dropdown
- DocsHeader.astro: Header with embedded search component
- DocsSidebar.astro: Navigation sidebar built from folder structure in database
- DocsToc.tsx: Auto-generated table of contents from article headings
- [...slug].astro: Dynamic article pages, uses
getStaticPaths()to pre-render all articles
- src/lib/turso.ts: Singleton client wrapper, re-exports libsql-search utilities
- scripts/init-db.ts: Initializes database schema with vector search support
- scripts/index-content.ts: Indexes markdown files, creates table with 768-dimension vectors
- All content queries use functions from libsql-search:
getAllArticles(),getArticleBySlug(),getArticlesByFolder(),getFolders()
Optional in .env:
TURSO_DB_URL: Turso database URL (libsql://...) - if not set, uses local libSQL fileTURSO_AUTH_TOKEN: Turso authentication token - if not set, uses local libSQL file
Embeddings run locally by default; no API keys are required.
Local Development: If Turso credentials aren't provided, the project automatically falls back to a local SQLite file (local.db) for database operations. This is useful for CI builds and local development without cloud dependencies.
The search API endpoint requires SSR with an adapter. The configuration uses:
output: 'server'- Enables server-side renderingnode({ mode: 'standalone' })- For traditional Node.js deployments- Article pages marked with
prerender: trueare pre-rendered as static HTML - Search API marked with
prerender: falseruns server-side
Never remove the adapter or change output to 'static', or the search API will break.
Content in ./content must follow this pattern:
content/
├── folder-name/
│ └── article.md
Folders become sidebar sections. Each markdown file can have optional frontmatter:
---
title: Article Title
tags: [tag1, tag2]
---- Article pages (
/content/[...slug].astro): Pre-rendered static at build time (export const prerender = true) - Search API (
/api/search.json.ts): Server-rendered on-demand (export const prerender = false) - This approach provides fast static pages with dynamic server-side search
- Uses
getStaticPaths()to generate all article pages at build time
The project relies heavily on libsql-search. When modifying search behavior:
- Check libsql-search documentation for available options
- Update
scripts/index-content.tsfor indexing changes - Update
src/pages/api/search.json.tsfor search query changes - Maintain embedding dimension consistency (768) across indexing and search
The embedding dimension (768) must match across:
scripts/index-content.ts(createTable and indexContent)- Search API
- Re-index content after changing the embedding model or dimension
- Uses Tailwind CSS 4 via Vite plugin
- RGB hex colors for maximum browser compatibility
- CSS variables defined in global.css for theming
- Multi-theme system with 6 pre-built themes (dark, light, ocean, forest, sunset, purple)
- ThemeSwitcher component allows runtime theme changes
- Complementary colors for sidebar (darker) and TOC (lighter) in each theme
- In-memory rate limiting on search API: 20 requests per minute per IP
- Query length validation: 500 character maximum
- Results limit enforcement: 1-20 results
- Standard rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset)
- See
docs/SECURITY.mdfor comprehensive security considerations
The project uses the Node.js adapter and can be deployed to any platform supporting Node.js:
# Build
pnpm build
# Deploy to your platform
vercel deploy
# or
netlify deploy --prod- Always run
pnpm index(orpnpm index:localfor testing) before deploying to ensure content is indexed - Set environment variables (
TURSO_DB_URL,TURSO_AUTH_TOKEN, etc.) in your deployment platform's dashboard - only run "pnpm lint:fix" or "biome check --write ." if there are less than 5 files to be modified and the changes are simple, otherwise fix linter issues manually
IMPORTANT: Before committing any changes, always run these checks to avoid CI failures:
# 1. Format code (fixes line length, import ordering, etc.)
pnpm format
# 2. Run linter (catches unused imports, type issues, etc.)
pnpm lint
# 3. TypeScript compilation check
pnpm exec tsc --noEmit
# 4. Run tests (if code changes affect functionality)
pnpm testQuick one-liner:
pnpm format && pnpm lint && pnpm exec tsc --noEmitCommon CI failures to watch for:
- Long lines - Biome enforces line length limits;
pnpm formatfixes these - Import ordering - Types must come before values in imports;
pnpm formatfixes this - Unused imports/variables - Note: Biome has false positives for
.astrofiles (disabled via override)
This project uses Conventional Commits. All commits must follow this format:
<type>(<scope>): <description>
| Type | Use For |
|---|---|
feat: |
New features |
fix: |
Bug fixes |
docs: |
Documentation changes |
perf: |
Performance improvements |
refactor: |
Code refactoring (no feature change) |
test: |
Adding or updating tests |
ci: |
CI/CD changes |
chore: |
Maintenance tasks |
feat: add user authentication
fix(api): handle rate limit errors
docs: update installation guide
chore: bump dependencies- Make commits following the convention above
- Create and push a version tag:
git tag v1.2.3 && git push --tags - GitHub Actions will automatically generate changelog and create release