This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the "librarian" repo - an assistant for llm-code-docs.
- llm-code-docs (public, GitHub): AI-readable documentation store (245+ frameworks/tools)
- librarian (private, Gitea): Discovery, search indexing, format conversion, and automation for managing documentation
Core responsibilities:
- Discovery - Crawl GitHub "awesome lists" to find projects needing documentation
- Search - LanceDB-based semantic search over llm-code-docs
- Conversion - Transform HTML/RFC/SiYuan to clean Markdown
- Automation - Process documentation queues with parallel workers
# Local extraction (fast, preferred) - from cloned seed repos
python3 extract_local.py
# Remote crawling (slower, for discovering new lists)
python3 crawler.py --max-depth 1 --delay 0.3# Search documentation
python -m search.cli search "how to use FastAPI middleware"
python -m search.cli search "authentication" --framework django --limit 5
# Build/rebuild index (requires TEI servers running)
USE_GPU_CLUSTER=true python -m search.cli index --rebuild # Full rebuild
USE_GPU_CLUSTER=true python -m search.cli index --update # Incremental
# Index statistics and exploration
python -m search.cli stats --verbose
python -m search.cli list-frameworks
python -m search.cli framework fastapi
python -m search.cli show docs/llms-txt/fastapi/llms.txt# HTML/MDX to Markdown (requires ReaderLM server)
python scripts/html_to_markdown.py input.html output.md
python scripts/html_to_markdown.py input.md --inplace
# RFC txt to Markdown
python rfcs/rfc_to_markdown.py rfc793.txt rfc793.md
# SiYuan .sy to Markdown
python tools/siyuan_to_md.py input.sy output.md
python tools/siyuan_to_md.py ./siyuan-data/ ./output-md/ # Directory mode# Process doc-add tickets in a loop (integrates with trckr)
python scripts/docs-loop.py --workers 2 --limit 10
# Process docs queue from CSV
python scripts/process-docs-queue.py --workers 2 --dry-run├── extract_local.py # Fast extraction from local clones
├── crawler.py # Recursive GitHub crawler with rate limiting
├── search/ # LanceDB semantic search engine
│ ├── cli.py # Click CLI interface
│ ├── config.py # LanceDB and embedding config
│ ├── opensearch/
│ │ ├── config.py # OpenSearch connection settings
│ │ ├── client.py # OpenSearchClient wrapper
│ │ ├── searcher.py # HybridSearcher (BM25+SPLADE+dense)
│ │ └── builder.py # Index builder for documents/folders
│ ├── watcher/
│ │ ├── daemon.py # DocsWatcher with watchdog
│ │ └── indexer.py # ChangeQueue and ChangeProcessor
│ ├── embeddings/ # TEI GPU embedding generation
│ ├── indexer/ # Document scanning, chunking, building
│ ├── searcher/ # Query processing, ranking, formatting
│ └── db/ # LanceDB connection and schemas
├── opensearch/ # OpenSearch setup and configuration
│ ├── create_indices.py # Index creation script
│ ├── create_indices.sh # Wrapper script
│ ├── mappings/ # Index mapping JSONs (documents, folders)
│ ├── pipelines/ # Search/ingest pipeline configs
│ └── scripts/ # SPLADE model registration
├── scripts/
│ ├── html_to_markdown.py # ReaderLM-based HTML conversion
│ ├── docs-loop.py # Automated doc-add ticket processor
│ ├── process-docs-queue.py # CSV queue processor with worktrees
│ ├── start_opensearch.sh # Start OpenSearch server
│ ├── start_tei.sh # Start TEI embedding servers
│ ├── start_readerlm.sh # Start ReaderLM conversion server
│ ├── start_watcher.sh # Start file watcher daemon
│ └── stop_*.sh # Stop scripts for services
├── api/
│ └── main.py # FastAPI app with /health, /search, /suggest
├── hooks/
│ └── librarian.sh # Claude Code UserPromptSubmit hook
├── tools/
│ └── siyuan_to_md.py # SiYuan JSON to Markdown converter
├── rfcs/
│ └── rfc_to_markdown.py # RFC txt to Markdown converter
├── tests/ # Comprehensive test suite
│ ├── test_opensearch_*.py # OpenSearch unit/integration tests
│ ├── test_api.py # REST API tests
│ ├── test_cli.py # CLI tests
│ ├── test_watcher.py # File watcher tests
│ └── test_e2e_validation.py # End-to-end validation
└── output/ # Generated results (tracked)
All GPU services run on chungus2. GPU 3 and 5 are RTX 3090s (24GB each).
./scripts/start_tei.sh # Ports 10001 (GPU 3), 10002 (GPU 5)
./scripts/stop_tei.shPerformance: ~475 texts/sec total, 1.5GB VRAM per GPU
./scripts/start_readerlm.sh # Port 10010 (GPU 3)
./scripts/stop_readerlm.shNote: Shares GPU 3 with TEI. ~4GB VRAM.
Backend: OpenSearch with hybrid BM25 + SPLADE + dense vector retrieval (migrated from LanceDB).
OpenSearch 2.17+ with ML Commons and neural search plugins.
# Start OpenSearch (port 9200)
./scripts/start_opensearch.sh
# Stop OpenSearch
./scripts/stop_opensearch.shThe search system combines three retrieval methods for optimal results:
- BM25: Traditional keyword matching using TF-IDF scoring
- SPLADE: Neural sparse encoding for semantic expansion (e.g., "auth" → "authentication", "OAuth", "login")
- Dense Vectors: K-NN search on embeddings for semantic similarity
Results are normalized and combined using OpenSearch's search pipeline with configurable weights:
- BM25: 0.3, SPLADE: 0.4, Dense: 0.3
# Search with OpenSearch backend (default)
python -m search.cli search "how to use FastAPI middleware"
# Search with verbose SPLADE info
python -m search.cli search "authentication" --verbose
# Search with scoring breakdown
python -m search.cli search "database ORM" --explain
# Use legacy LanceDB backend
python -m search.cli search "query" --backend lancedbStart the API server:
uvicorn api.main:app --host 0.0.0.0 --port 8080Endpoints:
GET /health- Service health including OpenSearch statusGET /search?q=...- Full hybrid search (<200ms latency)GET /suggest?q=...- Fast framework suggestions (<50ms latency)
Monitors llm-code-docs for changes and triggers incremental indexing:
# Start watcher daemon
./scripts/start_watcher.sh
# Or run directly
python -m search.watcher.daemon --debounce 5 --log-level INFOFeatures:
- Watches
docs/{llms-txt,github-scraped,web-scraped}/**/*.md - 5-second debounce for rapid changes
- Survives OpenSearch restarts (auto-reconnect)
- Graceful shutdown on SIGINT/SIGTERM
Hook for automatic documentation suggestions in Claude Code:
-
Copy hook script to Claude Code config:
cp hooks/librarian.sh ~/.claude/hooks/ -
Add to Claude Code hooks config (
~/.claude/hooks.json):{ "UserPromptSubmit": ["~/.claude/hooks/librarian.sh"] } -
Configure (optional environment variables):
export LIBRARIAN_URL="http://localhost:8080" export LIBRARIAN_TIMEOUT_MS="50" export LIBRARIAN_LIMIT="3"
# Unit tests (no OpenSearch required)
pytest tests/ -v --ignore=tests/test_e2e_validation.py --ignore=tests/test_opensearch_integration.py
# Integration tests (requires running OpenSearch)
RUN_INTEGRATION_TESTS=true pytest tests/test_opensearch_integration.py -v
# End-to-end validation (requires running OpenSearch with indexed data)
RUN_E2E_TESTS=true pytest tests/test_e2e_validation.py -v| Operation | Target | Measured |
|---|---|---|
| Suggest (folders) | <50ms | ~15-30ms |
| Search (documents) | <200ms | ~50-100ms |
| Bulk indexing | >100 docs/sec | ~200 docs/sec |
LanceDB-based semantic search with hybrid ranking (semantic 70%, keyword 20%, recency 10%).
Key configuration (search/config.py):
USE_GPU_CLUSTER=true- Use multi-GPU TEI servers (required for indexing)EMBEDDING_DIMENSION=1024- Qwen3-Embedding-0.6B output dimensionMAX_CHUNK_TOKENS=1000- Target chunk size for document splittingDOCS_ROOT- Auto-detects Docker (/llm-code-docs/docs) vs local (~/github/llm-code-docs/docs)
Converts HTML/MDX to clean Markdown via local LLM. Used for:
- Pure HTML files saved as .md (scraped Notion docs)
- MDX/JSX files with embedded HTML (Weaviate, etc.)
API endpoint: http://localhost:10010/v1/chat/completions (OpenAI-compatible)
Specs: 1.54B params, bfloat16, 512K context, ~4GB VRAM
Usage:
# Convert a single file
python scripts/html_to_markdown.py input.html output.md
# Convert in-place (overwrite)
python scripts/html_to_markdown.py input.md --inplace
# Dry run to check what would be converted
python scripts/html_to_markdown.py file.md --dry-runUse Cases:
- Pure HTML files saved as .md (e.g., scraped Notion docs at
llm-code-docs/docs/llms-txt/notion/) - MDX/JSX files with embedded HTML (e.g., Weaviate docs at
llm-code-docs/docs/github-scraped/weaviate/) - Any HTML-heavy markdown that needs cleanup
API Usage:
curl http://localhost:10010/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "jinaai/ReaderLM-v2",
"messages": [{"role": "user", "content": "Convert to markdown:\n```html\n<h1>Hello</h1>\n```"}]
}'Converts IETF RFC txt files to structured Markdown with:
- YAML frontmatter (RFC number, title, date, category)
- Clickable table of contents
- Code block detection
- Page break removal
Converts SiYuan's JSON-based .sy files to clean Markdown. Handles:
- Headings, paragraphs, lists (nested), tables
- Code blocks, blockquotes, math blocks
- Inline formatting (bold, italic, code, links, marks)
Continuously processes doc-add labeled trckr tickets by invoking /docs:WTFM-next.
python scripts/docs-loop.py --workers 2 --limit 10 --max-stale 3--workers N- Parallel Claude instances (staggered start)--max-stale N- Stop if no progress for N iterations- Logs to
output/docs-loop.log
Processes tickets from output/docs_tickets.csv using git worktrees.
python scripts/process-docs-queue.py --workers 2 --status triage,todo- Creates worktree per ticket in
/tmp/llm-code-docs-<branch> - Runs
/WTFM <topic>then merges to master - Thread-safe CSV locking for parallel workers
Discovery pipeline: sindresorhus/awesome + bayandin/awesome-awesomeness → 846 unique awesome lists → extract project URLs → check for llms.txt or good docs → add to llm-code-docs
Search index: Documents are chunked (~1000 tokens), embedded via TEI, stored in LanceDB. Hybrid ranking combines semantic similarity (70%), keyword matching (20%), and recency (10%).
Ticket workflow: trckr issues with doc-add label → triage → todo → in-progress (via automation) → in-review → done
crawler.py:
AwesomeRepo- Dataclass representing a discovered repo (owner, name, url, depth, parent)AwesomeCrawler- Recursive crawler with rate limiting, deduplication, and depth controlis_likely_awesome_list()- Heuristic detection (looks for "awesome", "curated", "list", etc.)fetch_readme()- Fetches README via raw.githubusercontent.com with fallbacks
extract_local.py:
extract_from_file()- Regex-based GitHub link extraction from markdownis_awesome_name()- Simple name-based awesome list detection