# pdf-brain Local **PDF & Markdown** knowledge base with semantic search and AI-powered enrichment. > **Works with PDFs AND Markdown files** - Index your research papers, books, notes, docs, and any `.md` files in one unified, searchable knowledge base. ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ PDF / MD │────▶│ Ollama │────▶│ Ollama │────▶│ libSQL │ │ (extract) │ │ (LLM) │ │ (embeddings)│ │ (vectors) │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ pdf-parse llama3.2:3b mxbai-embed HNSW index + markdown enrichment 1024 dims cosine sim ``` ## Features - **PDF + Markdown** - Index `.pdf` and `.md` files with the same workflow - **Local-first** - Everything runs on your machine, no API costs - **AI enrichment** - LLM extracts titles, summaries, tags, and concepts - **SKOS taxonomy** - Organize documents with hierarchical concepts - **Vector search** - Semantic search via Ollama embeddings - **Hybrid search** - Combine vector similarity with full-text search - **MCP server** - Use with Claude, Cursor, and other AI assistants ## Quick Start > Note: `pdf-brain` is agent-first and emits a single JSON envelope to stdout by default. > Use `--format text` for human-readable output (and TUI/progress rendering), or inspect the machine contract via `pdf-brain capabilities`. ```bash # 1. Install (standalone binary, no runtime needed) curl -fsSL https://raw.githubusercontent.com/joelhooks/pdf-brain/main/scripts/install.sh | bash # 2. Install Ollama (macOS) brew install ollama # 3. Pull required models ollama pull mxbai-embed-large # embeddings (required) ollama pull llama3.2:3b # enrichment (optional but recommended) # 4. Start Ollama ollama serve # 5. Initialize (creates DB + seeds starter taxonomy) pdf-brain init # 6. Add your first document pdf-brain add ~/Documents/paper.pdf --enrich ``` ## Installation ### Prerequisites **Ollama** is required for embeddings. The LLM model is optional but recommended for enrichment. ```bash # macOS brew install ollama # Linux curl -fsSL https://ollama.com/install.sh | sh # Windows # Download from https://ollama.com/download ``` ### Models ```bash # Required: Embedding model (1024 dimensions) ollama pull mxbai-embed-large # Recommended: Local LLM for enrichment ollama pull llama3.2:3b # Start Ollama server ollama serve ``` ### Install pdf-brain ```bash # Standalone binary (no runtime needed) curl -fsSL https://raw.githubusercontent.com/joelhooks/pdf-brain/main/scripts/install.sh | bash # or via npm npm install -g pdf-brain ``` ## CLI Reference ### Agent Output (Default) `pdf-brain` is optimized for agentic workflows: stdout is machine-readable by default. - `--format json|ndjson|text` (default: `json`) - `--pretty` pretty-print JSON - `--quiet` (alias: `--no-hints`) omit `nextActions` - `--log-level silent|error|info|debug` (logs go to stderr) Discover the full command/tool contract (including JSON Schemas) at runtime: ```bash pdf-brain capabilities ``` ### Basic Commands ```bash # Check Ollama status pdf-brain check # Show library stats pdf-brain stats # Initialize library (creates DB, seeds taxonomy) pdf-brain init ``` ### Adding Documents ```bash # Add a PDF pdf-brain add /path/to/document.pdf # Add a Markdown file pdf-brain add /path/to/notes.md # Add from URL (PDF or MD) pdf-brain add https://example.com/paper.pdf pdf-brain add https://raw.githubusercontent.com/user/repo/main/README.md # Add with manual tags pdf-brain add document.pdf --tags "ai,agents,research" # Add with AI enrichment (extracts title, summary, concepts) pdf-brain add document.pdf --enrich pdf-brain add notes.md --enrich ``` ### Searching ```bash # Semantic search (uses embeddings) pdf-brain search "context engineering patterns" # Full-text search only (faster, no embeddings) pdf-brain search "context engineering" --fts # Hybrid search (combines both) pdf-brain search "machine learning" --hybrid # Limit results pdf-brain search "query" --limit 5 # Expand context around matches pdf-brain search "query" --expand 500 ``` ### Managing Documents ```bash # List all documents pdf-brain list # List by tag pdf-brain list --tag ai # Get document details pdf-brain read "document-title" # Remove a document pdf-brain remove "document-title" # Update tags pdf-brain tag "document-title" "new,tags,here" ``` ### Taxonomy Commands The taxonomy system uses SKOS (Simple Knowledge Organization System) for hierarchical concept organization. ```bash # List all concepts pdf-brain taxonomy list # Show concept tree pdf-brain taxonomy tree # Show subtree from a concept pdf-brain taxonomy tree programming # Search concepts pdf-brain taxonomy search "machine learning" # Add a new concept pdf-brain taxonomy add ai/transformers --label "Transformers" --broader ai-ml # Assign concept to document pdf-brain taxonomy assign "doc-id" "programming/typescript" # Seed taxonomy from JSON file pdf-brain taxonomy seed --file data/taxonomy.json ``` ### Bulk Ingest Recursively ingest directories containing PDFs and/or Markdown files: ```bash # Ingest a directory with full LLM enrichment pdf-brain ingest ~/Documents/papers --enrich # Ingest your Obsidian vault or notes folder pdf-brain ingest ~/Documents/obsidian --enrich # Ingest multiple directories (PDFs, Markdown, mixed) pdf-brain ingest ~/papers ~/books ~/notes --enrich # With manual tags pdf-brain ingest ~/books --tags "books,reference" # Auto-tag only (faster, heuristics + light LLM) pdf-brain ingest ~/docs --auto-tag # Process only first N files (for testing) pdf-brain ingest ~/papers --enrich --sample 10 # Disable TUI for simple output pdf-brain ingest ~/papers --enrich --no-tui ``` **Supported formats:** - `.pdf` - Research papers, books, documents - `.md` - Notes, documentation, Obsidian vaults, READMEs ## Enrichment When you add documents with `--enrich`, the LLM extracts: | Field | Description | | -------------------- | ------------------------------------------- | | **title** | Clean, properly formatted title | | **author** | Author name(s) if detectable | | **summary** | 2-3 sentence summary | | **documentType** | book, paper, tutorial, guide, article, etc. | | **category** | Primary category | | **tags** | 5-10 descriptive tags | | **concepts** | Matched concepts from your taxonomy | | **proposedConcepts** | New concepts the LLM suggests adding | ### LLM Providers Enrichment supports multiple providers via the config system: ```bash # Check current config pdf-brain config show # Use local Ollama (default) pdf-brain config set enrichment.provider ollama pdf-brain config set enrichment.model llama3.2:3b # Use AI Gateway (Anthropic, OpenAI, etc.) pdf-brain config set enrichment.provider gateway pdf-brain config set enrichment.model anthropic/claude-haiku-4-5 export AI_GATEWAY_API_KEY=your-key # Provider priority: config > CLI flag > auto-detect pdf-brain add paper.pdf --enrich # uses config pdf-brain add paper.pdf --enrich --provider ollama # override ``` ### Enrichment Fallback If LLM enrichment fails (API error, rate limit, malformed response), pdf-brain automatically falls back to heuristic-based enrichment: - **Title**: Cleaned from filename - **Tags**: Extracted from path, filename, and content keywords - **Category**: Inferred from directory structure The actual error is logged so you can debug provider issues. ## Taxonomy The taxonomy is a hierarchical concept system for organizing documents. It ships with a starter taxonomy covering: - **Programming** - TypeScript, React, Next.js, Testing, Architecture, DevOps, AI/ML - **Education** - Instructional Design, Learning Science, Course Creation, Assessment - **Business** - Marketing, Copywriting, Bootstrapping, Product, Sales - **Design** - UX, Visual Design, Systems Thinking, Information Architecture - **Meta** - Productivity, Note-taking, Knowledge Management, Writing ### Growing Your Taxonomy When enriching documents, the LLM may propose new concepts. These are saved for review: ```bash # See proposed concepts from enrichment pdf-brain taxonomy proposed # Accept a specific concept pdf-brain taxonomy accept ai/rag --broader ai-ml # Accept all proposed concepts pdf-brain taxonomy accept --all # Reject a concept pdf-brain taxonomy reject ai/rag # Clear all proposals pdf-brain taxonomy clear-proposed # Manually add a concept pdf-brain taxonomy add ai/rag --label "RAG" --broader ai-ml # Or edit data/taxonomy.json and re-seed pdf-brain taxonomy seed --file data/taxonomy.json ``` ### Custom Taxonomy Create your own `taxonomy.json`: ```json { "concepts": [ { "id": "cooking", "prefLabel": "Cooking" }, { "id": "cooking/baking", "prefLabel": "Baking" }, { "id": "cooking/grilling", "prefLabel": "Grilling" } ], "hierarchy": [ { "conceptId": "cooking/baking", "broaderId": "cooking" }, { "conceptId": "cooking/grilling", "broaderId": "cooking" } ] } ``` ```bash pdf-brain taxonomy seed --file my-taxonomy.json ``` ## Configuration ### Config File pdf-brain stores configuration in `$PDF_LIBRARY_PATH/config.json`: ```bash # Show all config pdf-brain config show # Get a specific value pdf-brain config get enrichment.provider # Set a value pdf-brain config set enrichment.model anthropic/claude-haiku-4-5 ``` ### Config Options ```json { "ollama": { "host": "http://localhost:11434" }, "embedding": { "provider": "ollama", "model": "mxbai-embed-large" }, "enrichment": { "provider": "gateway", "model": "anthropic/claude-haiku-4-5" }, "judge": { "provider": "gateway", "model": "anthropic/claude-haiku-4-5" } } ``` | Setting | Default | Description | | --------------------- | ------------------------ | ------------------------------------ | | `ollama.host` | `http://localhost:11434` | Ollama API endpoint | | `embedding.provider` | `ollama` | Embedding provider (ollama only) | | `embedding.model` | `mxbai-embed-large` | Embedding model (1024 dims) | | `enrichment.provider` | `ollama` | LLM provider: `ollama` or `gateway` | | `enrichment.model` | `llama3.2:3b` | Model for document enrichment | | `judge.provider` | `ollama` | Provider for concept deduplication | | `judge.model` | `llama3.2:3b` | Model for judging duplicate concepts | ### Environment Variables | Variable | Default | Description | | -------------------- | -------------------------- | ------------------------ | | `PDF_LIBRARY_PATH` | `~/Documents/.pdf-library` | Library storage location | | `OLLAMA_HOST` | `http://localhost:11434` | Ollama API endpoint | | `AI_GATEWAY_API_KEY` | - | API key for AI Gateway | | `PDF_BRAIN_LOG_LEVEL` | `silent` | stderr logging verbosity | | `PDF_BRAIN_QUERY_EMBED_CACHE_SIZE` | `256` | Query embedding LRU cache size (0 disables) | ### AI Gateway For cloud LLM providers (Anthropic, OpenAI, etc.), use the AI Gateway: ```bash # Set your API key export AI_GATEWAY_API_KEY=your-key # Configure to use gateway pdf-brain config set enrichment.provider gateway pdf-brain config set enrichment.model anthropic/claude-haiku-4-5 # Other supported models: # - anthropic/claude-sonnet-4-20250514 # - openai/gpt-4o-mini # - openai/gpt-4o ``` ## Storage ``` ~/Documents/.pdf-library/ ├── library.db # libSQL database (vectors, FTS, metadata, taxonomy) ├── library.db-shm # Shared memory (WAL mode) ├── library.db-wal # Write-ahead log └── downloads/ # PDFs downloaded from URLs ``` ### Database Size The database can get **large** due to vector index overhead. For ~500k chunks: | Component | Size | Notes | | ------------ | ------ | --------------------------------- | | Text content | ~180MB | Actual chunk text | | Embeddings | ~1.9GB | 500k × 1024 dims × 4 bytes | | Vector index | ~48GB | HNSW neighbor graphs (~100KB/row) | | FTS index | ~200MB | Full-text search | The `*_idx_shadow` tables store HNSW neighbor graphs for approximate nearest neighbor search. Each row averages ~100KB. **libSQL quirk**: `SELECT COUNT(*) FROM embeddings` returns 0. Always count a specific column: ```sql SELECT COUNT(chunk_id) FROM embeddings -- correct ``` ## How It Works 1. **Extract** - PDF text via `pdf-parse`, Markdown parsed directly 2. **Enrich** (optional) - LLM extracts metadata, matches taxonomy concepts 3. **Chunk** - Text split into ~512 token chunks with overlap 4. **Embed** - Each chunk embedded via Ollama (1024 dimensions) 5. **Store** - libSQL with vector index (HNSW) + FTS5 6. **Search** - Query embedded, compared via cosine similarity ## MCP Integration pdf-brain ships as an MCP server for AI coding assistants: ```json { "mcpServers": { "pdf-brain": { "command": "npx", "args": ["pdf-brain", "mcp"] } } } ``` ### Document Tools | Tool | Description | | --------------------- | --------------------------------------------- | | `pdf-brain_add` | Add PDF/Markdown to library (supports URLs) | | `pdf-brain_batch_add` | Bulk ingest from directory | | `pdf-brain_search` | Unified semantic search (docs + concepts) | | `pdf-brain_list` | List documents, optionally filter by tag | | `pdf-brain_read` | Get document details and metadata | | `pdf-brain_remove` | Remove document from library | | `pdf-brain_tag` | Set tags on a document | | `pdf-brain_stats` | Library statistics (docs, chunks, embeddings) | ### Taxonomy Tools | Tool | Description | | --------------------------- | ---------------------------------------- | | `pdf-brain_taxonomy_list` | List all concepts (optional tree format) | | `pdf-brain_taxonomy_tree` | Visual concept tree with box-drawing | | `pdf-brain_taxonomy_add` | Add new concept to taxonomy | | `pdf-brain_taxonomy_assign` | Assign concept to document | | `pdf-brain_taxonomy_search` | Search concepts by label | | `pdf-brain_taxonomy_seed` | Load taxonomy from JSON file | ### Config Tools | Tool | Description | | ----------------------- | ------------------------- | | `pdf-brain_config_show` | Display all config | | `pdf-brain_config_get` | Get specific config value | | `pdf-brain_config_set` | Set config value | ### Utility Tools | Tool | Description | | ------------------ | ----------------------------- | | `pdf-brain_check` | Check if Ollama is ready | | `pdf-brain_repair` | Fix database integrity issues | ## Troubleshooting ### "Ollama not available" ```bash # Check if Ollama is running curl http://localhost:11434/api/tags # Start Ollama ollama serve # Check models ollama list ``` ### "Model not found" ```bash # Pull required models ollama pull mxbai-embed-large ollama pull llama3.2:3b ``` ### "Database locked" The database uses WAL mode. If you see lock errors: ```bash # Check for zombie processes lsof ~/Documents/.pdf-library/library.db* # Force checkpoint sqlite3 ~/Documents/.pdf-library/library.db "PRAGMA wal_checkpoint(TRUNCATE);" ``` ### Slow enrichment Enrichment is CPU-intensive. For large batches: - Use `--auto-tag` instead of `--enrich` for faster processing - Run overnight for large libraries - Consider GPU acceleration for Ollama ## Development ```bash # Clone git clone https://github.com/joelhooks/pdf-brain cd pdf-brain # Install bun install # Run CLI bun run src/cli.ts # Run tests bun test # Type check bun run typecheck ``` ## License MIT