chore: move workflow/repo/documents/config under assets

This commit is contained in:
tukuaiai
2026-02-27 01:46:51 +08:00
parent 4207c8fbb8
commit dffbb976e7
488 changed files with 203 additions and 196 deletions
@@ -0,0 +1,400 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Overview
This is a Python-based documentation scraper that converts ANY documentation website into a Claude skill. It's a single-file tool (`doc_scraper.py`) that scrapes documentation, extracts code patterns, detects programming languages, and generates structured skill files ready for use with Claude.
## Dependencies
```bash
pip3 install requests beautifulsoup4
```
## Core Commands
### Run with a preset configuration
```bash
python3 cli/doc_scraper.py --config configs/godot.json
python3 cli/doc_scraper.py --config configs/react.json
python3 cli/doc_scraper.py --config configs/vue.json
python3 cli/doc_scraper.py --config configs/django.json
python3 cli/doc_scraper.py --config configs/fastapi.json
```
### Interactive mode (for new frameworks)
```bash
python3 cli/doc_scraper.py --interactive
```
### Quick mode (minimal config)
```bash
python3 cli/doc_scraper.py --name react --url https://react.dev/ --description "React framework"
```
### Skip scraping (use cached data)
```bash
python3 cli/doc_scraper.py --config configs/godot.json --skip-scrape
```
### Resume interrupted scrapes
```bash
# If scrape was interrupted
python3 cli/doc_scraper.py --config configs/godot.json --resume
# Start fresh (clear checkpoint)
python3 cli/doc_scraper.py --config configs/godot.json --fresh
```
### Large documentation (10K-40K+ pages)
```bash
# 1. Estimate page count
python3 cli/estimate_pages.py configs/godot.json
# 2. Split into focused sub-skills
python3 cli/split_config.py configs/godot.json --strategy router
# 3. Generate router skill
python3 cli/generate_router.py configs/godot-*.json
# 4. Package multiple skills
python3 cli/package_multi.py output/godot*/
```
### AI-powered SKILL.md enhancement
```bash
# Option 1: During scraping (API-based, requires ANTHROPIC_API_KEY)
pip3 install anthropic
export ANTHROPIC_API_KEY=sk-ant-...
python3 cli/doc_scraper.py --config configs/react.json --enhance
# Option 2: During scraping (LOCAL, no API key - uses Claude Code Max)
python3 cli/doc_scraper.py --config configs/react.json --enhance-local
# Option 3: Standalone after scraping (API-based)
python3 cli/enhance_skill.py output/react/
# Option 4: Standalone after scraping (LOCAL, no API key)
python3 cli/enhance_skill_local.py output/react/
```
The LOCAL enhancement option (`--enhance-local` or `enhance_skill_local.py`) opens a new terminal with Claude Code, which analyzes reference files and enhances SKILL.md automatically. This requires Claude Code Max plan but no API key.
### MCP Integration (Claude Code)
```bash
# One-time setup
./setup_mcp.sh
# Then in Claude Code, use natural language:
"List all available configs"
"Generate config for Tailwind at https://tailwindcss.com/docs"
"Split configs/godot.json using router strategy"
"Generate router for configs/godot-*.json"
"Package skill at output/react/"
```
9 MCP tools available: list_configs, generate_config, validate_config, estimate_pages, scrape_docs, package_skill, upload_skill, split_config, generate_router
### Test with limited pages (edit config first)
Set `"max_pages": 20` in the config file to test with fewer pages.
## Architecture
### Single-File Design
The entire tool is contained in `doc_scraper.py` (~737 lines). It follows a class-based architecture with a single `DocToSkillConverter` class that handles:
- **Web scraping**: BFS traversal with URL validation
- **Content extraction**: CSS selectors for title, content, code blocks
- **Language detection**: Heuristic-based detection from code samples (Python, JavaScript, GDScript, C++, etc.)
- **Pattern extraction**: Identifies common coding patterns from documentation
- **Categorization**: Smart categorization using URL structure, page titles, and content keywords with scoring
- **Skill generation**: Creates SKILL.md with real code examples and categorized reference files
### Data Flow
1. **Scrape Phase**:
- Input: Config JSON (name, base_url, selectors, url_patterns, categories, rate_limit, max_pages)
- Process: BFS traversal starting from base_url, respecting include/exclude patterns
- Output: `output/{name}_data/pages/*.json` + `summary.json`
2. **Build Phase**:
- Input: Scraped JSON data from `output/{name}_data/`
- Process: Load pages → Smart categorize → Extract patterns → Generate references
- Output: `output/{name}/SKILL.md` + `output/{name}/references/*.md`
### Directory Structure
```
Skill_Seekers/
├── cli/ # CLI tools
│ ├── doc_scraper.py # Main scraping & building tool
│ ├── enhance_skill.py # AI enhancement (API-based)
│ ├── enhance_skill_local.py # AI enhancement (LOCAL, no API)
│ ├── estimate_pages.py # Page count estimator
│ ├── split_config.py # Large docs splitter (NEW)
│ ├── generate_router.py # Router skill generator (NEW)
│ ├── package_skill.py # Single skill packager
│ └── package_multi.py # Multi-skill packager (NEW)
├── mcp/ # MCP server
│ ├── server.py # 9 MCP tools (includes upload)
│ └── README.md
├── configs/ # Preset configurations
│ ├── godot.json
│ ├── godot-large-example.json # Large docs example (NEW)
│ ├── react.json
│ └── ...
├── docs/ # Documentation
│ ├── CLAUDE.md # Technical architecture (this file)
│ ├── LARGE_DOCUMENTATION.md # Large docs guide (NEW)
│ ├── ENHANCEMENT.md
│ ├── MCP_SETUP.md
│ └── ...
└── output/ # Generated output (git-ignored)
├── {name}_data/ # Raw scraped data (cached)
│ ├── pages/ # Individual page JSONs
│ ├── summary.json # Scraping summary
│ └── checkpoint.json # Resume checkpoint (NEW)
└── {name}/ # Generated skill
├── SKILL.md # Main skill file with examples
├── SKILL.md.backup # Backup (if enhanced)
├── references/ # Categorized documentation
│ ├── index.md
│ ├── getting_started.md
│ ├── api.md
│ └── ...
├── scripts/ # Empty (for user scripts)
└── assets/ # Empty (for user assets)
```
### Configuration Format
Config files in `configs/*.json` contain:
- `name`: Skill identifier (e.g., "godot", "react")
- `description`: When to use this skill
- `base_url`: Starting URL for scraping
- `selectors`: CSS selectors for content extraction
- `main_content`: Main documentation content (e.g., "article", "div[role='main']")
- `title`: Page title selector
- `code_blocks`: Code sample selector (e.g., "pre code", "pre")
- `url_patterns`: URL filtering
- `include`: Only scrape URLs containing these patterns
- `exclude`: Skip URLs containing these patterns
- `categories`: Keyword-based categorization mapping
- `rate_limit`: Delay between requests (seconds)
- `max_pages`: Maximum pages to scrape
- `split_strategy`: (Optional) How to split large docs: "auto", "category", "router", "size"
- `split_config`: (Optional) Split configuration
- `target_pages_per_skill`: Pages per sub-skill (default: 5000)
- `create_router`: Create router/hub skill (default: true)
- `split_by_categories`: Category names to split by
- `checkpoint`: (Optional) Checkpoint/resume configuration
- `enabled`: Enable checkpointing (default: false)
- `interval`: Save every N pages (default: 1000)
### Key Features
**Auto-detect existing data**: Tool checks for `output/{name}_data/` and prompts to reuse, avoiding re-scraping.
**Language detection**: Detects code languages from:
1. CSS class attributes (`language-*`, `lang-*`)
2. Heuristics (keywords like `def`, `const`, `func`, etc.)
**Pattern extraction**: Looks for "Example:", "Pattern:", "Usage:" markers in content and extracts following code blocks (up to 5 per page).
**Smart categorization**:
- Scores pages against category keywords (3 points for URL match, 2 for title, 1 for content)
- Threshold of 2+ for categorization
- Auto-infers categories from URL segments if none provided
- Falls back to "other" category
**Enhanced SKILL.md**: Generated with:
- Real code examples from documentation (language-annotated)
- Quick reference patterns extracted from docs
- Common pattern section
- Category file listings
**AI-Powered Enhancement**: Two scripts to dramatically improve SKILL.md quality:
- `enhance_skill.py`: Uses Anthropic API (~$0.15-$0.30 per skill, requires API key)
- `enhance_skill_local.py`: Uses Claude Code Max (free, no API key needed)
- Transforms generic 75-line templates into comprehensive 500+ line guides
- Extracts best examples, explains key concepts, adds navigation guidance
- Success rate: 9/10 quality (based on steam-economy test)
**Large Documentation Support (NEW)**: Handle 10K-40K+ page documentation:
- `split_config.py`: Split large configs into multiple focused sub-skills
- `generate_router.py`: Create intelligent router/hub skills that direct queries
- `package_multi.py`: Package multiple skills at once
- 4 split strategies: auto, category, router, size
- Parallel scraping support for faster processing
- MCP integration for natural language usage
**Checkpoint/Resume (NEW)**: Never lose progress on long scrapes:
- Auto-saves every N pages (configurable, default: 1000)
- Resume with `--resume` flag
- Clear checkpoint with `--fresh` flag
- Saves on interruption (Ctrl+C)
## Key Code Locations
- **URL validation**: `is_valid_url()` doc_scraper.py:47-62
- **Content extraction**: `extract_content()` doc_scraper.py:64-131
- **Language detection**: `detect_language()` doc_scraper.py:133-163
- **Pattern extraction**: `extract_patterns()` doc_scraper.py:165-181
- **Smart categorization**: `smart_categorize()` doc_scraper.py:280-321
- **Category inference**: `infer_categories()` doc_scraper.py:323-349
- **Quick reference generation**: `generate_quick_reference()` doc_scraper.py:351-370
- **SKILL.md generation**: `create_enhanced_skill_md()` doc_scraper.py:424-540
- **Scraping loop**: `scrape_all()` doc_scraper.py:226-249
- **Main workflow**: `main()` doc_scraper.py:661-733
## Workflow Examples
### First time scraping (with scraping)
```bash
# 1. Scrape + Build
python3 cli/doc_scraper.py --config configs/godot.json
# Time: 20-40 minutes
# 2. Package
python3 cli/package_skill.py output/godot/
# Result: godot.zip
```
### Using cached data (fast iteration)
```bash
# 1. Use existing data
python3 cli/doc_scraper.py --config configs/godot.json --skip-scrape
# Time: 1-3 minutes
# 2. Package
python3 cli/package_skill.py output/godot/
```
### Creating a new framework config
```bash
# Option 1: Interactive
python3 cli/doc_scraper.py --interactive
# Option 2: Copy and modify
cp configs/react.json configs/myframework.json
# Edit configs/myframework.json
python3 cli/doc_scraper.py --config configs/myframework.json
```
### Large documentation workflow (40K pages)
```bash
# 1. Estimate page count (fast, 1-2 minutes)
python3 cli/estimate_pages.py configs/godot.json
# 2. Split into focused sub-skills
python3 cli/split_config.py configs/godot.json --strategy router --target-pages 5000
# Creates: godot-scripting.json, godot-2d.json, godot-3d.json, etc.
# 3. Scrape all in parallel (4-8 hours instead of 20-40!)
for config in configs/godot-*.json; do
python3 cli/doc_scraper.py --config $config &
done
wait
# 4. Generate intelligent router skill
python3 cli/generate_router.py configs/godot-*.json
# 5. Package all skills
python3 cli/package_multi.py output/godot*/
# 6. Upload all .zip files to Claude
# Result: Router automatically directs queries to the right sub-skill!
```
**Time savings:** Parallel scraping reduces 20-40 hours to 4-8 hours
**See full guide:** [Large Documentation Guide](LARGE_DOCUMENTATION.md)
## Testing Selectors
To find the right CSS selectors for a documentation site:
```python
from bs4 import BeautifulSoup
import requests
url = "https://docs.example.com/page"
soup = BeautifulSoup(requests.get(url).content, 'html.parser')
# Try different selectors
print(soup.select_one('article'))
print(soup.select_one('main'))
print(soup.select_one('div[role="main"]'))
```
## Running Tests
**IMPORTANT: You must install the package before running tests**
```bash
# 1. Install package in editable mode (one-time setup)
pip install -e .
# 2. Run all tests
pytest
# 3. Run specific test files
pytest tests/test_config_validation.py
pytest tests/test_github_scraper.py
# 4. Run with verbose output
pytest -v
# 5. Run with coverage report
pytest --cov=src/skill_seekers --cov-report=html
```
**Why install first?**
- Tests import from `skill_seekers.cli` which requires the package to be installed
- Modern Python packaging best practice (PEP 517/518)
- CI/CD automatically installs with `pip install -e .`
- conftest.py will show helpful error if package not installed
**Test Coverage:**
- 391+ tests passing
- 39% code coverage
- All core features tested
- CI/CD tests on Ubuntu + macOS with Python 3.10-3.12
## Troubleshooting
**No content extracted**: Check `main_content` selector. Common values: `article`, `main`, `div[role="main"]`, `div.content`
**Poor categorization**: Edit `categories` section in config with better keywords specific to the documentation structure
**Force re-scrape**: Delete cached data with `rm -rf output/{name}_data/`
**Rate limiting issues**: Increase `rate_limit` value in config (e.g., from 0.5 to 1.0 seconds)
## Output Quality Checks
After building, verify quality:
```bash
cat output/godot/SKILL.md # Should have real code examples
cat output/godot/references/index.md # Should show categories
ls output/godot/references/ # Should have category .md files
```
## llms.txt Support
Skill_Seekers automatically detects llms.txt files before HTML scraping:
### Detection Order
1. `{base_url}/llms-full.txt` (complete documentation)
2. `{base_url}/llms.txt` (standard version)
3. `{base_url}/llms-small.txt` (quick reference)
### Benefits
- ⚡ 10x faster (< 5 seconds vs 20-60 seconds)
- ✅ More reliable (maintained by docs authors)
- 🎯 Better quality (pre-formatted for LLMs)
- 🚫 No rate limiting needed
### Example Sites
- Hono: https://hono.dev/llms-full.txt
If no llms.txt is found, automatically falls back to HTML scraping.
@@ -0,0 +1,250 @@
# AI-Powered SKILL.md Enhancement
Two scripts are available to dramatically improve your SKILL.md file:
1. **`enhance_skill_local.py`** - Uses Claude Code Max (no API key, **recommended**)
2. **`enhance_skill.py`** - Uses Anthropic API (~$0.15-$0.30 per skill)
Both analyze reference documentation and extract the best examples and guidance.
## Why Use Enhancement?
**Problem:** The auto-generated SKILL.md is often too generic:
- Empty Quick Reference section
- No practical code examples
- Generic "When to Use" triggers
- Doesn't highlight key features
**Solution:** Let Claude read your reference docs and create a much better SKILL.md with:
- ✅ Best code examples extracted from documentation
- ✅ Practical quick reference with real patterns
- ✅ Domain-specific guidance
- ✅ Clear navigation tips
- ✅ Key concepts explained
## Quick Start (LOCAL - No API Key)
**Recommended for Claude Code Max users:**
```bash
# Option 1: Standalone enhancement
python3 cli/enhance_skill_local.py output/steam-inventory/
# Option 2: Integrated with scraper
python3 cli/doc_scraper.py --config configs/steam-inventory.json --enhance-local
```
**What happens:**
1. Opens new terminal window
2. Runs Claude Code with enhancement prompt
3. Claude analyzes reference files (~15-20K chars)
4. Generates enhanced SKILL.md (30-60 seconds)
5. Terminal auto-closes when done
**Requirements:**
- Claude Code Max plan (you're already using it!)
- macOS (auto-launch works) or manual terminal run on other OS
## API-Based Enhancement (Alternative)
**If you prefer API-based approach:**
### Installation
```bash
pip3 install anthropic
```
### Setup API Key
```bash
# Option 1: Environment variable (recommended)
export ANTHROPIC_API_KEY=sk-ant-...
# Option 2: Pass directly with --api-key
python3 cli/enhance_skill.py output/react/ --api-key sk-ant-...
```
### Usage
```bash
# Standalone enhancement
python3 cli/enhance_skill.py output/steam-inventory/
# Integrated with scraper
python3 cli/doc_scraper.py --config configs/steam-inventory.json --enhance
# Dry run (see what would be done)
python3 cli/enhance_skill.py output/react/ --dry-run
```
## What It Does
1. **Reads reference files** (api_reference.md, webapi.md, etc.)
2. **Sends to Claude** with instructions to:
- Extract 5-10 best code examples
- Create practical quick reference
- Write domain-specific "When to Use" triggers
- Add helpful navigation guidance
3. **Backs up original** SKILL.md to SKILL.md.backup
4. **Saves enhanced version** as new SKILL.md
## Example Enhancement
### Before (Auto-Generated)
```markdown
## Quick Reference
### Common Patterns
*Quick reference patterns will be added as you use the skill.*
```
### After (AI-Enhanced)
```markdown
## Quick Reference
### Common API Patterns
**Granting promotional items:**
```cpp
void CInventory::GrantPromoItems()
{
SteamItemDef_t newItems[2];
newItems[0] = 110;
newItems[1] = 111;
SteamInventory()->AddPromoItems( &s_GenerateRequestResult, newItems, 2 );
}
```
**Getting all items in player inventory:**
```cpp
SteamInventoryResult_t resultHandle;
bool success = SteamInventory()->GetAllItems( &resultHandle );
```
[... 8 more practical examples ...]
```
## Cost Estimate
- **Input**: ~50,000-100,000 tokens (reference docs)
- **Output**: ~4,000 tokens (enhanced SKILL.md)
- **Model**: claude-sonnet-4-20250514
- **Estimated cost**: $0.15-$0.30 per skill
## Troubleshooting
### "No API key provided"
```bash
export ANTHROPIC_API_KEY=sk-ant-...
# or
python3 cli/enhance_skill.py output/react/ --api-key sk-ant-...
```
### "No reference files found"
Make sure you've run the scraper first:
```bash
python3 cli/doc_scraper.py --config configs/react.json
```
### "anthropic package not installed"
```bash
pip3 install anthropic
```
### Don't like the result?
```bash
# Restore original
mv output/steam-inventory/SKILL.md.backup output/steam-inventory/SKILL.md
# Try again (it may generate different content)
python3 cli/enhance_skill.py output/steam-inventory/
```
## Tips
1. **Run after scraping completes** - Enhancement works best with complete reference docs
2. **Review the output** - AI is good but not perfect, check the generated SKILL.md
3. **Keep the backup** - Original is saved as SKILL.md.backup
4. **Re-run if needed** - Each run may produce slightly different results
5. **Works offline after first run** - Reference files are local
## Real-World Results
**Test Case: steam-economy skill**
- **Before:** 75 lines, generic template, empty Quick Reference
- **After:** 570 lines, 10 practical API examples, key concepts explained
- **Time:** 60 seconds
- **Quality Rating:** 9/10
The LOCAL enhancement successfully:
- Extracted best HTTP/JSON examples from 24 pages of documentation
- Explained domain concepts (Asset Classes, Context IDs, Transaction Lifecycle)
- Created navigation guidance for beginners through advanced users
- Added best practices for security, economy design, and API integration
## Limitations
**LOCAL Enhancement (`enhance_skill_local.py`):**
- Requires Claude Code Max plan
- macOS auto-launch only (manual on other OS)
- Opens new terminal window
- Takes ~60 seconds
**API Enhancement (`enhance_skill.py`):**
- Requires Anthropic API key (paid)
- Cost: ~$0.15-$0.30 per skill
- Limited to ~100K tokens of reference input
**Both:**
- May occasionally miss the best examples
- Can't understand context beyond the reference docs
- Doesn't modify reference files (only SKILL.md)
## Enhancement Options Comparison
| Aspect | Manual Edit | LOCAL Enhancement | API Enhancement |
|--------|-------------|-------------------|-----------------|
| Time | 15-30 minutes | 30-60 seconds | 30-60 seconds |
| Code examples | You pick | AI picks best | AI picks best |
| Quick reference | Write yourself | Auto-generated | Auto-generated |
| Domain guidance | Your knowledge | From docs | From docs |
| Consistency | Varies | Consistent | Consistent |
| Cost | Free (your time) | Free (Max plan) | ~$0.20 per skill |
| Setup | None | None | API key needed |
| Quality | High (if expert) | 9/10 | 9/10 |
| **Recommended?** | For experts only | ✅ **Yes** | If no Max plan |
## When to Use
**Use enhancement when:**
- You want high-quality SKILL.md quickly
- Working with large documentation (50+ pages)
- Creating skills for unfamiliar frameworks
- Need practical code examples extracted
- Want consistent quality across multiple skills
**Skip enhancement when:**
- Budget constrained (use manual editing)
- Very small documentation (<10 pages)
- You know the framework intimately
- Documentation has no code examples
## Advanced: Customization
To customize how Claude enhances the SKILL.md, edit `enhance_skill.py` and modify the `_build_enhancement_prompt()` method around line 130.
Example customization:
```python
prompt += """
ADDITIONAL REQUIREMENTS:
- Focus on security best practices
- Include performance tips
- Add troubleshooting section
"""
```
## See Also
- [README.md](../README.md) - Main documentation
- [CLAUDE.md](CLAUDE.md) - Architecture guide
- [doc_scraper.py](../doc_scraper.py) - Main scraping tool
@@ -0,0 +1,431 @@
# Handling Large Documentation Sites (10K+ Pages)
Complete guide for scraping and managing large documentation sites with Skill Seeker.
---
## Table of Contents
- [When to Split Documentation](#when-to-split-documentation)
- [Split Strategies](#split-strategies)
- [Quick Start](#quick-start)
- [Detailed Workflows](#detailed-workflows)
- [Best Practices](#best-practices)
- [Examples](#examples)
- [Troubleshooting](#troubleshooting)
---
## When to Split Documentation
### Size Guidelines
| Documentation Size | Recommendation | Strategy |
|-------------------|----------------|----------|
| < 5,000 pages | **One skill** | No splitting needed |
| 5,000 - 10,000 pages | **Consider splitting** | Category-based |
| 10,000 - 30,000 pages | **Recommended** | Router + Categories |
| 30,000+ pages | **Strongly recommended** | Router + Categories |
### Why Split Large Documentation?
**Benefits:**
- ✅ Faster scraping (parallel execution)
- ✅ More focused skills (better Claude performance)
- ✅ Easier maintenance (update one topic at a time)
- ✅ Better user experience (precise answers)
- ✅ Avoids context window limits
**Trade-offs:**
- ⚠️ Multiple skills to manage
- ⚠️ Initial setup more complex
- ⚠️ Router adds one extra skill
---
## Split Strategies
### 1. **No Split** (One Big Skill)
**Best for:** Small to medium documentation (< 5K pages)
```bash
# Just use the config as-is
python3 cli/doc_scraper.py --config configs/react.json
```
**Pros:** Simple, one skill to maintain
**Cons:** Can be slow for large docs, may hit limits
---
### 2. **Category Split** (Multiple Focused Skills)
**Best for:** 5K-15K pages with clear topic divisions
```bash
# Auto-split by categories
python3 cli/split_config.py configs/godot.json --strategy category
# Creates:
# - godot-scripting.json
# - godot-2d.json
# - godot-3d.json
# - godot-physics.json
# - etc.
```
**Pros:** Focused skills, clear separation
**Cons:** User must know which skill to use
---
### 3. **Router + Categories** (Intelligent Hub) ⭐ RECOMMENDED
**Best for:** 10K+ pages, best user experience
```bash
# Create router + sub-skills
python3 cli/split_config.py configs/godot.json --strategy router
# Creates:
# - godot.json (router/hub)
# - godot-scripting.json
# - godot-2d.json
# - etc.
```
**Pros:** Best of both worlds, intelligent routing, natural UX
**Cons:** Slightly more complex setup
---
### 4. **Size-Based Split**
**Best for:** Docs without clear categories
```bash
# Split every 5000 pages
python3 cli/split_config.py configs/bigdocs.json --strategy size --target-pages 5000
# Creates:
# - bigdocs-part1.json
# - bigdocs-part2.json
# - bigdocs-part3.json
# - etc.
```
**Pros:** Simple, predictable
**Cons:** May split related topics
---
## Quick Start
### Option 1: Automatic (Recommended)
```bash
# 1. Create config
python3 cli/doc_scraper.py --interactive
# Name: godot
# URL: https://docs.godotengine.org
# ... fill in prompts ...
# 2. Estimate pages (discovers it's large)
python3 cli/estimate_pages.py configs/godot.json
# Output: ⚠️ 40,000 pages detected - splitting recommended
# 3. Auto-split with router
python3 cli/split_config.py configs/godot.json --strategy router
# 4. Scrape all sub-skills
for config in configs/godot-*.json; do
python3 cli/doc_scraper.py --config $config &
done
wait
# 5. Generate router
python3 cli/generate_router.py configs/godot-*.json
# 6. Package all
python3 cli/package_multi.py output/godot*/
# 7. Upload all .zip files to Claude
```
---
### Option 2: Manual Control
```bash
# 1. Define split in config
nano configs/godot.json
# Add:
{
"split_strategy": "router",
"split_config": {
"target_pages_per_skill": 5000,
"create_router": true,
"split_by_categories": ["scripting", "2d", "3d", "physics"]
}
}
# 2. Split
python3 cli/split_config.py configs/godot.json
# 3. Continue as above...
```
---
## Detailed Workflows
### Workflow 1: Router + Categories (40K Pages)
**Scenario:** Godot documentation (40,000 pages)
**Step 1: Estimate**
```bash
python3 cli/estimate_pages.py configs/godot.json
# Output:
# Estimated: 40,000 pages
# Recommended: Split into 8 skills (5K each)
```
**Step 2: Split Configuration**
```bash
python3 cli/split_config.py configs/godot.json --strategy router --target-pages 5000
# Creates:
# configs/godot.json (router)
# configs/godot-scripting.json (5K pages)
# configs/godot-2d.json (8K pages)
# configs/godot-3d.json (10K pages)
# configs/godot-physics.json (6K pages)
# configs/godot-shaders.json (11K pages)
```
**Step 3: Scrape Sub-Skills (Parallel)**
```bash
# Open multiple terminals or use background jobs
python3 cli/doc_scraper.py --config configs/godot-scripting.json &
python3 cli/doc_scraper.py --config configs/godot-2d.json &
python3 cli/doc_scraper.py --config configs/godot-3d.json &
python3 cli/doc_scraper.py --config configs/godot-physics.json &
python3 cli/doc_scraper.py --config configs/godot-shaders.json &
# Wait for all to complete
wait
# Time: 4-8 hours (parallel) vs 20-40 hours (sequential)
```
**Step 4: Generate Router**
```bash
python3 cli/generate_router.py configs/godot-*.json
# Creates:
# output/godot/SKILL.md (router skill)
```
**Step 5: Package All**
```bash
python3 cli/package_multi.py output/godot*/
# Creates:
# output/godot.zip (router)
# output/godot-scripting.zip
# output/godot-2d.zip
# output/godot-3d.zip
# output/godot-physics.zip
# output/godot-shaders.zip
```
**Step 6: Upload to Claude**
Upload all 6 .zip files to Claude. The router will intelligently direct queries to the right sub-skill!
---
### Workflow 2: Category Split Only (15K Pages)
**Scenario:** Vue.js documentation (15,000 pages)
**No router needed - just focused skills:**
```bash
# 1. Split
python3 cli/split_config.py configs/vue.json --strategy category
# 2. Scrape each
for config in configs/vue-*.json; do
python3 cli/doc_scraper.py --config $config
done
# 3. Package
python3 cli/package_multi.py output/vue*/
# 4. Upload all to Claude
```
**Result:** 5 focused Vue skills (components, reactivity, routing, etc.)
---
## Best Practices
### 1. **Choose Target Size Wisely**
```bash
# Small focused skills (3K-5K pages) - more skills, very focused
python3 cli/split_config.py config.json --target-pages 3000
# Medium skills (5K-8K pages) - balanced (RECOMMENDED)
python3 cli/split_config.py config.json --target-pages 5000
# Larger skills (8K-10K pages) - fewer skills, broader
python3 cli/split_config.py config.json --target-pages 8000
```
### 2. **Use Parallel Scraping**
```bash
# Serial (slow - 40 hours)
for config in configs/godot-*.json; do
python3 cli/doc_scraper.py --config $config
done
# Parallel (fast - 8 hours) ⭐
for config in configs/godot-*.json; do
python3 cli/doc_scraper.py --config $config &
done
wait
```
### 3. **Test Before Full Scrape**
```bash
# Test with limited pages first
nano configs/godot-2d.json
# Set: "max_pages": 50
python3 cli/doc_scraper.py --config configs/godot-2d.json
# If output looks good, increase to full
```
### 4. **Use Checkpoints for Long Scrapes**
```bash
# Enable checkpoints in config
{
"checkpoint": {
"enabled": true,
"interval": 1000
}
}
# If scrape fails, resume
python3 cli/doc_scraper.py --config config.json --resume
```
---
## Examples
### Example 1: AWS Documentation (Hypothetical 50K Pages)
```bash
# 1. Split by AWS services
python3 cli/split_config.py configs/aws.json --strategy router --target-pages 5000
# Creates ~10 skills:
# - aws (router)
# - aws-compute (EC2, Lambda)
# - aws-storage (S3, EBS)
# - aws-database (RDS, DynamoDB)
# - etc.
# 2. Scrape in parallel (overnight)
# 3. Upload all skills to Claude
# 4. User asks "How do I create an S3 bucket?"
# 5. Router activates aws-storage skill
# 6. Focused, accurate answer!
```
### Example 2: Microsoft Docs (100K+ Pages)
```bash
# Too large even with splitting - use selective categories
# Only scrape key topics
python3 cli/split_config.py configs/microsoft.json --strategy category
# Edit configs to include only:
# - microsoft-azure (Azure docs only)
# - microsoft-dotnet (.NET docs only)
# - microsoft-typescript (TS docs only)
# Skip less relevant sections
```
---
## Troubleshooting
### Issue: "Splitting creates too many skills"
**Solution:** Increase target size or combine categories
```bash
# Instead of 5K per skill, use 8K
python3 cli/split_config.py config.json --target-pages 8000
# Or manually combine categories in config
```
### Issue: "Router not routing correctly"
**Solution:** Check routing keywords in router SKILL.md
```bash
# Review router
cat output/godot/SKILL.md
# Update keywords if needed
nano output/godot/SKILL.md
```
### Issue: "Parallel scraping fails"
**Solution:** Reduce parallelism or check rate limits
```bash
# Scrape 2-3 at a time instead of all
python3 cli/doc_scraper.py --config config1.json &
python3 cli/doc_scraper.py --config config2.json &
wait
python3 cli/doc_scraper.py --config config3.json &
python3 cli/doc_scraper.py --config config4.json &
wait
```
---
## Summary
**For 40K+ Page Documentation:**
1.**Estimate first**: `python3 cli/estimate_pages.py config.json`
2.**Split with router**: `python3 cli/split_config.py config.json --strategy router`
3.**Scrape in parallel**: Multiple terminals or background jobs
4.**Generate router**: `python3 cli/generate_router.py configs/*-*.json`
5.**Package all**: `python3 cli/package_multi.py output/*/`
6.**Upload to Claude**: All .zip files
**Result:** Intelligent, fast, focused skills that work seamlessly together!
---
**Questions? See:**
- [Main README](../README.md)
- [MCP Setup Guide](MCP_SETUP.md)
- [Enhancement Guide](ENHANCEMENT.md)
@@ -0,0 +1,60 @@
# llms.txt Support
## Overview
Skill_Seekers now automatically detects and uses llms.txt files when available, providing 10x faster documentation ingestion.
## What is llms.txt?
The llms.txt convention is a growing standard where documentation sites provide pre-formatted, LLM-ready markdown files:
- `llms-full.txt` - Complete documentation
- `llms.txt` - Standard balanced version
- `llms-small.txt` - Quick reference
## How It Works
1. Before HTML scraping, Skill_Seekers checks for llms.txt files
2. If found, downloads and parses the markdown
3. If not found, falls back to HTML scraping
4. Zero config changes needed
## Configuration
### Automatic Detection (Recommended)
No config changes needed. Just run normally:
```bash
python3 cli/doc_scraper.py --config configs/hono.json
```
### Explicit URL
Optionally specify llms.txt URL:
```json
{
"name": "hono",
"llms_txt_url": "https://hono.dev/llms-full.txt",
"base_url": "https://hono.dev/docs"
}
```
## Performance Comparison
| Method | Time | Requests |
|--------|------|----------|
| HTML Scraping (20 pages) | 20-60s | 20+ |
| llms.txt | < 5s | 1 |
## Supported Sites
Sites known to provide llms.txt:
- Hono: https://hono.dev/llms-full.txt
- (More to be discovered)
## Fallback Behavior
If llms.txt download or parsing fails, automatically falls back to HTML scraping with no user intervention required.
@@ -0,0 +1,618 @@
# Complete MCP Setup Guide for Claude Code
Step-by-step guide to set up the Skill Seeker MCP server with Claude Code.
**✅ Fully Tested and Working**: All 9 MCP tools verified in production use with Claude Code
- ✅ 34 comprehensive unit tests (100% pass rate)
- ✅ Integration tested via actual Claude Code MCP protocol
- ✅ All 9 tools working with natural language commands (includes upload support!)
---
## Table of Contents
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Verification](#verification)
- [Usage Examples](#usage-examples)
- [Troubleshooting](#troubleshooting)
- [Advanced Configuration](#advanced-configuration)
---
## Prerequisites
### Required Software
1. **Python 3.10 or higher**
```bash
python3 --version
# Should show: Python 3.10.x or higher
```
2. **Claude Code installed**
- Download from [claude.ai/code](https://claude.ai/code)
- Requires Claude Pro or Claude Code Max subscription
3. **Skill Seeker repository cloned**
```bash
git clone https://github.com/yusufkaraaslan/Skill_Seekers.git
cd Skill_Seekers
```
### System Requirements
- **Operating System**: macOS, Linux, or Windows (WSL)
- **Disk Space**: 100 MB for dependencies + space for generated skills
- **Network**: Internet connection for documentation scraping
---
## Installation
### Step 1: Install Python Dependencies
```bash
# Navigate to repository root
cd /path/to/Skill_Seekers
# Install MCP server dependencies
pip3 install -r skill_seeker_mcp/requirements.txt
# Install CLI tool dependencies (for scraping)
pip3 install requests beautifulsoup4
```
**Expected output:**
```
Successfully installed mcp-0.9.0 requests-2.31.0 beautifulsoup4-4.12.3
```
### Step 2: Verify Installation
```bash
# Test MCP server can start
timeout 3 python3 skill_seeker_mcp/server.py || echo "Server OK (timeout expected)"
# Should exit cleanly or timeout (both are normal)
```
**Optional: Run Tests**
```bash
# Install test dependencies
pip3 install pytest
# Run MCP server tests (25 tests)
python3 -m pytest tests/test_mcp_server.py -v
# Expected: 25 passed in ~0.3s
```
### Step 3: Note Your Repository Path
```bash
# Get absolute path
pwd
# Example output: /Users/username/Projects/Skill_Seekers
# or: /home/username/Skill_Seekers
```
**Save this path** - you'll need it for configuration!
---
## Configuration
### Step 1: Locate Claude Code MCP Configuration
Claude Code stores MCP configuration in:
- **macOS**: `~/.config/claude-code/mcp.json`
- **Linux**: `~/.config/claude-code/mcp.json`
- **Windows (WSL)**: `~/.config/claude-code/mcp.json`
### Step 2: Create/Edit Configuration File
```bash
# Create config directory if it doesn't exist
mkdir -p ~/.config/claude-code
# Edit the configuration
nano ~/.config/claude-code/mcp.json
```
### Step 3: Add Skill Seeker MCP Server
**Full Configuration Example:**
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"/Users/username/Projects/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/Users/username/Projects/Skill_Seekers",
"env": {}
}
}
}
```
**IMPORTANT:** Replace `/Users/username/Projects/Skill_Seekers` with YOUR actual repository path!
**If you already have other MCP servers:**
```json
{
"mcpServers": {
"existing-server": {
"command": "node",
"args": ["/path/to/existing/server.js"]
},
"skill-seeker": {
"command": "python3",
"args": [
"/Users/username/Projects/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/Users/username/Projects/Skill_Seekers"
}
}
}
```
### Step 4: Save and Restart Claude Code
1. Save the file (`Ctrl+O` in nano, then `Enter`)
2. Exit editor (`Ctrl+X` in nano)
3. **Completely restart Claude Code** (quit and reopen)
---
## Verification
### Step 1: Check MCP Server Loaded
In Claude Code, type:
```
List all available MCP tools
```
You should see 9 Skill Seeker tools:
- `generate_config`
- `estimate_pages`
- `scrape_docs`
- `package_skill`
- `upload_skill`
- `list_configs`
- `validate_config`
- `split_config`
- `generate_router`
### Step 2: Test a Simple Command
```
List all available configs
```
**Expected response:**
```
Available configurations:
1. godot - Godot Engine documentation
2. react - React framework
3. vue - Vue.js framework
4. django - Django web framework
5. fastapi - FastAPI Python framework
6. kubernetes - Kubernetes documentation
7. steam-economy-complete - Steam Economy API
```
### Step 3: Test Config Generation
```
Generate a config for Tailwind CSS at https://tailwindcss.com/docs
```
**Expected response:**
```
✅ Config created: configs/tailwind.json
```
**Verify the file exists:**
```bash
ls configs/tailwind.json
```
---
## Usage Examples
### Example 1: Generate Skill from Scratch
```
User: Generate config for Svelte docs at https://svelte.dev/docs
Claude: ✅ Config created: configs/svelte.json
User: Estimate pages for configs/svelte.json
Claude: 📊 Estimated pages: 150
Recommended max_pages: 180
User: Scrape docs using configs/svelte.json
Claude: ✅ Skill created at output/svelte/
Run: python3 cli/package_skill.py output/svelte/
User: Package skill at output/svelte/
Claude: ✅ Created: output/svelte.zip
Ready to upload to Claude!
```
### Example 2: Use Existing Config
```
User: List all available configs
Claude: [Shows 7 configs]
User: Scrape docs using configs/react.json with max 50 pages
Claude: ✅ Skill created at output/react/
User: Package skill at output/react/
Claude: ✅ Created: output/react.zip
```
### Example 3: Validate Before Scraping
```
User: Validate configs/godot.json
Claude: ✅ Config is valid
- Base URL: https://docs.godotengine.org/en/stable/
- Max pages: 500
- Rate limit: 0.5s
- Categories: 3
User: Estimate pages for configs/godot.json
Claude: 📊 Estimated pages: 450
Current max_pages (500) is sufficient
User: Scrape docs using configs/godot.json
Claude: [Scraping starts...]
```
---
## Troubleshooting
### Issue: MCP Server Not Loading
**Symptoms:**
- Skill Seeker tools don't appear in Claude Code
- No response when asking about configs
**Solutions:**
1. **Check configuration path:**
```bash
cat ~/.config/claude-code/mcp.json
```
2. **Verify Python path:**
```bash
which python3
# Should show: /usr/bin/python3 or /usr/local/bin/python3
```
3. **Test server manually:**
```bash
cd /path/to/Skill_Seekers
python3 skill_seeker_mcp/server.py
# Should start without errors
```
4. **Check Claude Code logs:**
- macOS: `~/Library/Logs/Claude Code/`
- Linux: `~/.config/claude-code/logs/`
5. **Completely restart Claude Code:**
- Quit Claude Code (don't just close window)
- Reopen Claude Code
### Issue: "ModuleNotFoundError: No module named 'mcp'"
**Solution:**
```bash
pip3 install -r skill_seeker_mcp/requirements.txt
```
### Issue: "Permission denied" when running server
**Solution:**
```bash
chmod +x skill_seeker_mcp/server.py
```
### Issue: Tools appear but don't work
**Symptoms:**
- Tools listed but commands fail
- "Error executing tool" messages
**Solutions:**
1. **Check working directory in config:**
```json
{
"cwd": "/FULL/PATH/TO/Skill_Seekers"
}
```
2. **Verify CLI tools exist:**
```bash
ls cli/doc_scraper.py
ls cli/estimate_pages.py
ls cli/package_skill.py
```
3. **Test CLI tools directly:**
```bash
python3 cli/doc_scraper.py --help
```
### Issue: Slow or hanging operations
**Solutions:**
1. **Check rate limit in config:**
- Default: 0.5 seconds
- Increase if needed: 1.0 or 2.0 seconds
2. **Use smaller max_pages for testing:**
```
Generate config with max_pages=20 for testing
```
3. **Check network connection:**
```bash
curl -I https://docs.example.com
```
---
## Advanced Configuration
### Custom Environment Variables
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": ["/path/to/Skill_Seekers/skill_seeker_mcp/server.py"],
"cwd": "/path/to/Skill_Seekers",
"env": {
"ANTHROPIC_API_KEY": "sk-ant-...",
"PYTHONPATH": "/custom/path"
}
}
}
}
```
### Multiple Python Versions
If you have multiple Python versions:
```json
{
"mcpServers": {
"skill-seeker": {
"command": "/usr/local/bin/python3.11",
"args": ["/path/to/Skill_Seekers/skill_seeker_mcp/server.py"],
"cwd": "/path/to/Skill_Seekers"
}
}
}
```
### Virtual Environment
To use a Python virtual environment:
```bash
# Create venv
cd /path/to/Skill_Seekers
python3 -m venv venv
source venv/bin/activate
pip install -r skill_seeker_mcp/requirements.txt
pip install requests beautifulsoup4
which python3
# Copy this path for config
```
```json
{
"mcpServers": {
"skill-seeker": {
"command": "/path/to/Skill_Seekers/venv/bin/python3",
"args": ["/path/to/Skill_Seekers/skill_seeker_mcp/server.py"],
"cwd": "/path/to/Skill_Seekers"
}
}
}
```
### Debug Mode
Enable verbose logging:
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"-u",
"/path/to/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/path/to/Skill_Seekers",
"env": {
"DEBUG": "1"
}
}
}
}
```
---
## Complete Example Configuration
**Minimal (recommended for most users):**
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"/Users/username/Projects/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/Users/username/Projects/Skill_Seekers"
}
}
}
```
**With API enhancement:**
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"/Users/username/Projects/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/Users/username/Projects/Skill_Seekers",
"env": {
"ANTHROPIC_API_KEY": "sk-ant-your-key-here"
}
}
}
}
```
---
## End-to-End Workflow
### Complete Setup and First Skill
```bash
# 1. Install
cd ~/Projects
git clone https://github.com/yusufkaraaslan/Skill_Seekers.git
cd Skill_Seekers
pip3 install -r skill_seeker_mcp/requirements.txt
pip3 install requests beautifulsoup4
# 2. Configure
mkdir -p ~/.config/claude-code
cat > ~/.config/claude-code/mcp.json << 'EOF'
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"/Users/username/Projects/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/Users/username/Projects/Skill_Seekers"
}
}
}
EOF
# (Replace paths with your actual paths!)
# 3. Restart Claude Code
# 4. Test in Claude Code:
```
**In Claude Code:**
```
User: List all available configs
User: Scrape docs using configs/react.json with max 50 pages
User: Package skill at output/react/
```
**Result:** `output/react.zip` ready to upload!
---
## Next Steps
After successful setup:
1. **Try preset configs:**
- React: `scrape docs using configs/react.json`
- Vue: `scrape docs using configs/vue.json`
- Django: `scrape docs using configs/django.json`
2. **Create custom configs:**
- `generate config for [framework] at [url]`
3. **Test with small limits first:**
- Use `max_pages` parameter: `scrape docs using configs/test.json with max 20 pages`
4. **Explore enhancement:**
- Use `--enhance-local` flag for AI-powered SKILL.md improvement
---
## Getting Help
- **Documentation**: See [mcp/README.md](../mcp/README.md)
- **Issues**: [GitHub Issues](https://github.com/yusufkaraaslan/Skill_Seekers/issues)
- **Examples**: See [.github/ISSUES_TO_CREATE.md](../.github/ISSUES_TO_CREATE.md) for test cases
---
## Quick Reference Card
```
SETUP:
1. Install dependencies: pip3 install -r skill_seeker_mcp/requirements.txt
2. Configure: ~/.config/claude-code/mcp.json
3. Restart Claude Code
VERIFY:
- "List all available configs"
- "Validate configs/react.json"
GENERATE SKILL:
1. "Generate config for [name] at [url]"
2. "Estimate pages for configs/[name].json"
3. "Scrape docs using configs/[name].json"
4. "Package skill at output/[name]/"
TROUBLESHOOTING:
- Check: cat ~/.config/claude-code/mcp.json
- Test: python3 skill_seeker_mcp/server.py
- Logs: ~/Library/Logs/Claude Code/
```
---
Happy skill creating! 🚀
@@ -0,0 +1,579 @@
# PDF Advanced Features Guide
Comprehensive guide to advanced PDF extraction features (Priority 2 & 3).
## Overview
Skill Seeker's PDF extractor now includes powerful advanced features for handling complex PDF scenarios:
**Priority 2 Features (More PDF Types):**
- ✅ OCR support for scanned PDFs
- ✅ Password-protected PDF support
- ✅ Complex table extraction
**Priority 3 Features (Performance Optimizations):**
- ✅ Parallel page processing
- ✅ Intelligent caching of expensive operations
## Table of Contents
1. [OCR Support for Scanned PDFs](#ocr-support)
2. [Password-Protected PDFs](#password-protected-pdfs)
3. [Table Extraction](#table-extraction)
4. [Parallel Processing](#parallel-processing)
5. [Caching](#caching)
6. [Combined Usage](#combined-usage)
7. [Performance Benchmarks](#performance-benchmarks)
---
## OCR Support
Extract text from scanned PDFs using Optical Character Recognition.
### Installation
```bash
# Install Tesseract OCR engine
# Ubuntu/Debian
sudo apt-get install tesseract-ocr
# macOS
brew install tesseract
# Install Python packages
pip install pytesseract Pillow
```
### Usage
```bash
# Basic OCR
python3 cli/pdf_extractor_poc.py scanned.pdf --ocr
# OCR with other options
python3 cli/pdf_extractor_poc.py scanned.pdf --ocr --verbose -o output.json
# Full skill creation with OCR
python3 cli/pdf_scraper.py --pdf scanned.pdf --name myskill --ocr
```
### How It Works
1. **Detection**: For each page, checks if text content is < 50 characters
2. **Fallback**: If low text detected and OCR enabled, renders page as image
3. **Processing**: Runs Tesseract OCR on the image
4. **Selection**: Uses OCR text if it's longer than extracted text
5. **Logging**: Shows OCR extraction results in verbose mode
### Example Output
```
📄 Extracting from: scanned.pdf
Pages: 50
OCR: ✅ enabled
Page 1: 245 chars, 0 code blocks, 2 headings, 0 images, 0 tables
OCR extracted 245 chars (was 12)
Page 2: 389 chars, 1 code blocks, 3 headings, 0 images, 0 tables
OCR extracted 389 chars (was 5)
```
### Limitations
- Requires Tesseract installed on system
- Slower than regular text extraction (~2-5 seconds per page)
- Quality depends on PDF scan quality
- Works best with high-resolution scans
### Best Practices
- Use `--parallel` with OCR for faster processing
- Combine with `--verbose` to see OCR progress
- Test on a few pages first before processing large documents
---
## Password-Protected PDFs
Handle encrypted PDFs with password protection.
### Usage
```bash
# Basic usage
python3 cli/pdf_extractor_poc.py encrypted.pdf --password mypassword
# With full workflow
python3 cli/pdf_scraper.py --pdf encrypted.pdf --name myskill --password mypassword
```
### How It Works
1. **Detection**: Checks if PDF is encrypted (`doc.is_encrypted`)
2. **Authentication**: Attempts to authenticate with provided password
3. **Validation**: Returns error if password is incorrect or missing
4. **Processing**: Continues normal extraction if authentication succeeds
### Example Output
```
📄 Extracting from: encrypted.pdf
🔐 PDF is encrypted, trying password...
✅ Password accepted
Pages: 100
Metadata: {...}
```
### Error Handling
```
# Missing password
❌ PDF is encrypted but no password provided
Use --password option to provide password
# Wrong password
❌ Invalid password
```
### Security Notes
- Password is passed via command line (visible in process list)
- For sensitive documents, consider environment variables
- Password is not stored in output JSON
---
## Table Extraction
Extract tables from PDFs and include them in skill references.
### Usage
```bash
# Extract tables
python3 cli/pdf_extractor_poc.py data.pdf --extract-tables
# With other options
python3 cli/pdf_extractor_poc.py data.pdf --extract-tables --verbose -o output.json
# Full skill creation with tables
python3 cli/pdf_scraper.py --pdf data.pdf --name myskill --extract-tables
```
### How It Works
1. **Detection**: Uses PyMuPDF's `find_tables()` method
2. **Extraction**: Extracts table data as 2D array (rows × columns)
3. **Metadata**: Captures bounding box, row count, column count
4. **Integration**: Tables included in page data and summary
### Example Output
```
📄 Extracting from: data.pdf
Table extraction: ✅ enabled
Page 5: 892 chars, 2 code blocks, 4 headings, 0 images, 2 tables
Found table 0: 10x4
Found table 1: 15x6
✅ Extraction complete:
Tables found: 25
```
### Table Data Structure
```json
{
"tables": [
{
"table_index": 0,
"rows": [
["Header 1", "Header 2", "Header 3"],
["Data 1", "Data 2", "Data 3"],
...
],
"bbox": [x0, y0, x1, y1],
"row_count": 10,
"col_count": 4
}
]
}
```
### Integration with Skills
Tables are automatically included in reference files when building skills:
```markdown
## Data Tables
### Table 1 (Page 5)
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Data 1 | Data 2 | Data 3 |
```
### Limitations
- Quality depends on PDF table structure
- Works best with well-formatted tables
- Complex merged cells may not extract correctly
---
## Parallel Processing
Process pages in parallel for 3x faster extraction.
### Usage
```bash
# Enable parallel processing (auto-detects CPU count)
python3 cli/pdf_extractor_poc.py large.pdf --parallel
# Specify worker count
python3 cli/pdf_extractor_poc.py large.pdf --parallel --workers 8
# With full workflow
python3 cli/pdf_scraper.py --pdf large.pdf --name myskill --parallel --workers 8
```
### How It Works
1. **Worker Pool**: Creates ThreadPoolExecutor with N workers
2. **Distribution**: Distributes pages across workers
3. **Extraction**: Each worker processes pages independently
4. **Collection**: Results collected and merged
5. **Threshold**: Only activates for PDFs with > 5 pages
### Example Output
```
📄 Extracting from: large.pdf
Pages: 500
Parallel processing: ✅ enabled (8 workers)
🚀 Extracting 500 pages in parallel (8 workers)...
✅ Extraction complete:
Total characters: 1,250,000
Code blocks found: 450
```
### Performance
| Pages | Sequential | Parallel (4 workers) | Parallel (8 workers) |
|-------|-----------|---------------------|---------------------|
| 50 | 25s | 10s (2.5x) | 8s (3.1x) |
| 100 | 50s | 18s (2.8x) | 15s (3.3x) |
| 500 | 4m 10s | 1m 30s (2.8x) | 1m 15s (3.3x) |
| 1000 | 8m 20s | 3m 00s (2.8x) | 2m 30s (3.3x) |
### Best Practices
- Use `--workers` equal to CPU core count
- Combine with `--no-cache` for first-time processing
- Monitor system resources (RAM, CPU)
- Not recommended for very large images (memory intensive)
### Limitations
- Requires `concurrent.futures` (Python 3.2+)
- Uses more memory (N workers × page size)
- May not be beneficial for PDFs with many large images
---
## Caching
Intelligent caching of expensive operations for faster re-extraction.
### Usage
```bash
# Caching enabled by default
python3 cli/pdf_extractor_poc.py input.pdf
# Disable caching
python3 cli/pdf_extractor_poc.py input.pdf --no-cache
```
### How It Works
1. **Cache Key**: Each page cached by page number
2. **Check**: Before extraction, checks cache for page data
3. **Store**: After extraction, stores result in cache
4. **Reuse**: On re-run, returns cached data instantly
### What Gets Cached
- Page text and markdown
- Code block detection results
- Language detection results
- Quality scores
- Image extraction results
- Table extraction results
### Example Output
```
Page 1: Using cached data
Page 2: Using cached data
Page 3: 892 chars, 2 code blocks, 4 headings, 0 images, 0 tables
```
### Cache Lifetime
- In-memory only (cleared when process exits)
- Useful for:
- Testing extraction parameters
- Re-running with different filters
- Development and debugging
### When to Disable
- First-time extraction
- PDF file has changed
- Different extraction options
- Memory constraints
---
## Combined Usage
### Maximum Performance
Extract everything as fast as possible:
```bash
python3 cli/pdf_scraper.py \
--pdf docs/manual.pdf \
--name myskill \
--extract-images \
--extract-tables \
--parallel \
--workers 8 \
--min-quality 5.0
```
### Scanned PDF with Tables
```bash
python3 cli/pdf_scraper.py \
--pdf docs/scanned.pdf \
--name myskill \
--ocr \
--extract-tables \
--parallel \
--workers 4
```
### Encrypted PDF with All Features
```bash
python3 cli/pdf_scraper.py \
--pdf docs/encrypted.pdf \
--name myskill \
--password mypassword \
--extract-images \
--extract-tables \
--parallel \
--workers 8 \
--verbose
```
---
## Performance Benchmarks
### Test Setup
- **Hardware**: 8-core CPU, 16GB RAM
- **PDF**: 500-page technical manual
- **Content**: Mixed text, code, images, tables
### Results
| Configuration | Time | Speedup |
|--------------|------|---------|
| Basic (sequential) | 4m 10s | 1.0x (baseline) |
| + Caching | 2m 30s | 1.7x |
| + Parallel (4 workers) | 1m 30s | 2.8x |
| + Parallel (8 workers) | 1m 15s | 3.3x |
| + All optimizations | 1m 10s | 3.6x |
### Feature Overhead
| Feature | Time Impact | Memory Impact |
|---------|------------|---------------|
| OCR | +2-5s per page | +50MB per page |
| Table extraction | +0.5s per page | +10MB |
| Image extraction | +0.2s per image | Varies |
| Parallel (8 workers) | -66% total time | +8x memory |
| Caching | -50% on re-run | +100MB |
---
## Troubleshooting
### OCR Issues
**Problem**: `pytesseract not found`
```bash
# Install pytesseract
pip install pytesseract
# Install Tesseract engine
sudo apt-get install tesseract-ocr # Ubuntu
brew install tesseract # macOS
```
**Problem**: Low OCR quality
- Use higher DPI PDFs
- Check scan quality
- Try different Tesseract language packs
### Parallel Processing Issues
**Problem**: Out of memory errors
```bash
# Reduce worker count
python3 cli/pdf_extractor_poc.py large.pdf --parallel --workers 2
# Or disable parallel
python3 cli/pdf_extractor_poc.py large.pdf
```
**Problem**: Not faster than sequential
- Check CPU usage (may be I/O bound)
- Try with larger PDFs (> 50 pages)
- Monitor system resources
### Table Extraction Issues
**Problem**: Tables not detected
- Check if tables are actual tables (not images)
- Try different PDF viewers to verify structure
- Use `--verbose` to see detection attempts
**Problem**: Malformed table data
- Complex merged cells may not extract correctly
- Try extracting specific pages only
- Manual post-processing may be needed
---
## Best Practices
### For Large PDFs (500+ pages)
1. Use parallel processing:
```bash
python3 cli/pdf_scraper.py --pdf large.pdf --parallel --workers 8
```
2. Extract to JSON first, then build skill:
```bash
python3 cli/pdf_extractor_poc.py large.pdf -o extracted.json --parallel
python3 cli/pdf_scraper.py --from-json extracted.json --name myskill
```
3. Monitor system resources
### For Scanned PDFs
1. Use OCR with parallel processing:
```bash
python3 cli/pdf_scraper.py --pdf scanned.pdf --ocr --parallel --workers 4
```
2. Test on sample pages first
3. Use `--verbose` to monitor OCR performance
### For Encrypted PDFs
1. Use environment variable for password:
```bash
export PDF_PASSWORD="mypassword"
python3 cli/pdf_scraper.py --pdf encrypted.pdf --password "$PDF_PASSWORD"
```
2. Clear history after use to remove password
### For PDFs with Tables
1. Enable table extraction:
```bash
python3 cli/pdf_scraper.py --pdf data.pdf --extract-tables
```
2. Check table quality in output JSON
3. Manual review recommended for critical data
---
## API Reference
### PDFExtractor Class
```python
from pdf_extractor_poc import PDFExtractor
extractor = PDFExtractor(
pdf_path="input.pdf",
verbose=True,
chunk_size=10,
min_quality=5.0,
extract_images=True,
image_dir="images/",
min_image_size=100,
# Advanced features
use_ocr=True,
password="mypassword",
extract_tables=True,
parallel=True,
max_workers=8,
use_cache=True
)
result = extractor.extract_all()
```
### Configuration Options
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `pdf_path` | str | required | Path to PDF file |
| `verbose` | bool | False | Enable verbose logging |
| `chunk_size` | int | 10 | Pages per chunk |
| `min_quality` | float | 0.0 | Min code quality (0-10) |
| `extract_images` | bool | False | Extract images to files |
| `image_dir` | str | None | Image output directory |
| `min_image_size` | int | 100 | Min image dimension |
| `use_ocr` | bool | False | Enable OCR |
| `password` | str | None | PDF password |
| `extract_tables` | bool | False | Extract tables |
| `parallel` | bool | False | Parallel processing |
| `max_workers` | int | CPU count | Worker threads |
| `use_cache` | bool | True | Enable caching |
---
## Summary
**6 Advanced Features** implemented (Priority 2 & 3)
**3x Performance Boost** with parallel processing
**OCR Support** for scanned PDFs
**Password Protection** support
**Table Extraction** from complex PDFs
**Intelligent Caching** for faster re-runs
The PDF extractor now handles virtually any PDF scenario with maximum performance!
@@ -0,0 +1,521 @@
# PDF Page Detection and Chunking (Task B1.3)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Task:** B1.3 - Add PDF page detection and chunking
---
## Overview
Task B1.3 enhances the PDF extractor with intelligent page chunking and chapter detection capabilities. This allows large PDF documentation to be split into manageable, logical sections for better processing and organization.
## New Features
### ✅ 1. Page Chunking
Break large PDFs into smaller, manageable chunks:
- Configurable chunk size (default: 10 pages per chunk)
- Smart chunking that respects chapter boundaries
- Chunk metadata includes page ranges and chapter titles
**Usage:**
```bash
# Default chunking (10 pages per chunk)
python3 cli/pdf_extractor_poc.py input.pdf
# Custom chunk size (20 pages per chunk)
python3 cli/pdf_extractor_poc.py input.pdf --chunk-size 20
# Disable chunking (single chunk with all pages)
python3 cli/pdf_extractor_poc.py input.pdf --chunk-size 0
```
### ✅ 2. Chapter/Section Detection
Automatically detect chapter and section boundaries:
- Detects H1 and H2 headings as chapter markers
- Recognizes common chapter patterns:
- "Chapter 1", "Chapter 2", etc.
- "Part 1", "Part 2", etc.
- "Section 1", "Section 2", etc.
- Numbered sections like "1. Introduction"
**Chapter Detection Logic:**
1. Check for H1/H2 headings at page start
2. Pattern match against common chapter formats
3. Extract chapter title for metadata
### ✅ 3. Code Block Merging
Intelligently merge code blocks split across pages:
- Detects when code continues from one page to the next
- Checks language and detection method consistency
- Looks for continuation indicators:
- Doesn't end with `}`, `;`
- Ends with `,`, `\`
- Incomplete syntax structures
**Example:**
```
Page 5: def calculate_total(items):
total = 0
for item in items:
Page 6: total += item.price
return total
```
The merger will combine these into a single code block.
---
## Output Format
### Enhanced JSON Structure
The output now includes chunking and chapter information:
```json
{
"source_file": "manual.pdf",
"metadata": { ... },
"total_pages": 150,
"total_chunks": 15,
"chapters": [
{
"title": "Getting Started",
"start_page": 1,
"end_page": 12
},
{
"title": "API Reference",
"start_page": 13,
"end_page": 45
}
],
"chunks": [
{
"chunk_number": 1,
"start_page": 1,
"end_page": 12,
"chapter_title": "Getting Started",
"pages": [ ... ]
},
{
"chunk_number": 2,
"start_page": 13,
"end_page": 22,
"chapter_title": "API Reference",
"pages": [ ... ]
}
],
"pages": [ ... ]
}
```
### Chunk Object
Each chunk contains:
- `chunk_number` - Sequential chunk identifier (1-indexed)
- `start_page` - First page in chunk (1-indexed)
- `end_page` - Last page in chunk (1-indexed)
- `chapter_title` - Detected chapter title (if any)
- `pages` - Array of page objects in this chunk
### Merged Code Block Indicator
Code blocks merged from multiple pages include a flag:
```json
{
"code": "def example():\n ...",
"language": "python",
"detection_method": "font",
"merged_from_next_page": true
}
```
---
## Implementation Details
### Chapter Detection Algorithm
```python
def detect_chapter_start(self, page_data):
"""
Detect if a page starts a new chapter/section.
Returns (is_chapter_start, chapter_title) tuple.
"""
# Check H1/H2 headings first
headings = page_data.get('headings', [])
if headings:
first_heading = headings[0]
if first_heading['level'] in ['h1', 'h2']:
return True, first_heading['text']
# Pattern match against common chapter formats
text = page_data.get('text', '')
first_line = text.split('\n')[0] if text else ''
chapter_patterns = [
r'^Chapter\s+\d+',
r'^Part\s+\d+',
r'^Section\s+\d+',
r'^\d+\.\s+[A-Z]', # "1. Introduction"
]
for pattern in chapter_patterns:
if re.match(pattern, first_line, re.IGNORECASE):
return True, first_line.strip()
return False, None
```
### Code Block Merging Algorithm
```python
def merge_continued_code_blocks(self, pages):
"""
Merge code blocks that are split across pages.
"""
for i in range(len(pages) - 1):
current_page = pages[i]
next_page = pages[i + 1]
# Get last code block of current page
last_code = current_page['code_samples'][-1]
# Get first code block of next page
first_next_code = next_page['code_samples'][0]
# Check if they're likely the same code block
if (last_code['language'] == first_next_code['language'] and
last_code['detection_method'] == first_next_code['detection_method']):
# Check for continuation indicators
last_code_text = last_code['code'].rstrip()
continuation_indicators = [
not last_code_text.endswith('}'),
not last_code_text.endswith(';'),
last_code_text.endswith(','),
last_code_text.endswith('\\'),
]
if any(continuation_indicators):
# Merge the blocks
merged_code = last_code['code'] + '\n' + first_next_code['code']
last_code['code'] = merged_code
last_code['merged_from_next_page'] = True
# Remove duplicate from next page
next_page['code_samples'].pop(0)
return pages
```
### Chunking Algorithm
```python
def create_chunks(self, pages):
"""
Create chunks of pages respecting chapter boundaries.
"""
chunks = []
current_chunk = []
current_chapter = None
for i, page in enumerate(pages):
# Detect chapter start
is_chapter, chapter_title = self.detect_chapter_start(page)
if is_chapter and current_chunk:
# Save current chunk before starting new one
chunks.append({
'chunk_number': len(chunks) + 1,
'start_page': chunk_start + 1,
'end_page': i,
'pages': current_chunk,
'chapter_title': current_chapter
})
current_chunk = []
current_chapter = chapter_title
current_chunk.append(page)
# Check if chunk size reached (but don't break chapters)
if not is_chapter and len(current_chunk) >= self.chunk_size:
# Create chunk
chunks.append(...)
current_chunk = []
return chunks
```
---
## Usage Examples
### Basic Chunking
```bash
# Extract with default 10-page chunks
python3 cli/pdf_extractor_poc.py manual.pdf -o manual.json
# Output includes chunks
cat manual.json | jq '.total_chunks'
# Output: 15
```
### Large PDF Processing
```bash
# Large PDF with bigger chunks (50 pages each)
python3 cli/pdf_extractor_poc.py large_manual.pdf --chunk-size 50 -o output.json -v
# Verbose output shows:
# 📦 Creating chunks (chunk_size=50)...
# 🔗 Merging code blocks across pages...
# ✅ Extraction complete:
# Chunks created: 8
# Chapters detected: 12
```
### No Chunking (Single Output)
```bash
# Process all pages as single chunk
python3 cli/pdf_extractor_poc.py small_doc.pdf --chunk-size 0 -o output.json
```
---
## Performance
### Chunking Performance
- **Chapter Detection:** ~0.1ms per page (negligible overhead)
- **Code Merging:** ~0.5ms per page (fast)
- **Chunk Creation:** ~1ms total (very fast)
**Total overhead:** < 1% of extraction time
### Memory Benefits
Chunking large PDFs helps reduce memory usage:
- **Without chunking:** Entire PDF loaded in memory
- **With chunking:** Process chunk-by-chunk (future enhancement)
**Current implementation** still loads entire PDF but provides structured output for chunked processing downstream.
---
## Limitations
### Current Limitations
1. **Chapter Pattern Matching**
- Limited to common English chapter patterns
- May miss non-standard chapter formats
- No support for non-English chapters (e.g., "Capitulo", "Chapitre")
2. **Code Merging Heuristics**
- Based on simple continuation indicators
- May miss some edge cases
- No AST-based validation
3. **Chunk Size**
- Fixed page count (not by content size)
- Doesn't account for page content volume
- No auto-sizing based on memory constraints
### Known Issues
1. **Multi-Chapter Pages**
- If a single page has multiple chapters, only first is detected
- Workaround: Use smaller chunk sizes
2. **False Code Merges**
- Rare cases where separate code blocks are merged
- Detection: Look for `merged_from_next_page` flag
3. **Table of Contents**
- TOC pages may be detected as chapters
- Workaround: Manual filtering in downstream processing
---
## Comparison: Before vs After
| Feature | Before (B1.2) | After (B1.3) |
|---------|---------------|--------------|
| Page chunking | None | ✅ Configurable |
| Chapter detection | None | ✅ Auto-detect |
| Code spanning pages | Split | ✅ Merged |
| Large PDF handling | Difficult | ✅ Chunked |
| Memory efficiency | Poor | Better (structure for future) |
| Output organization | Flat | ✅ Hierarchical |
---
## Testing
### Test Chapter Detection
Create a test PDF with chapters:
1. Page 1: "Chapter 1: Introduction"
2. Page 15: "Chapter 2: Getting Started"
3. Page 30: "Chapter 3: API Reference"
```bash
python3 cli/pdf_extractor_poc.py test.pdf -o test.json --chunk-size 20 -v
# Verify chapters detected
cat test.json | jq '.chapters'
```
Expected output:
```json
[
{
"title": "Chapter 1: Introduction",
"start_page": 1,
"end_page": 14
},
{
"title": "Chapter 2: Getting Started",
"start_page": 15,
"end_page": 29
},
{
"title": "Chapter 3: API Reference",
"start_page": 30,
"end_page": 50
}
]
```
### Test Code Merging
Create a test PDF with code spanning pages:
- Page 1 ends with: `def example():\n total = 0`
- Page 2 starts with: ` for i in range(10):\n total += i`
```bash
python3 cli/pdf_extractor_poc.py test.pdf -o test.json -v
# Check for merged code blocks
cat test.json | jq '.pages[0].code_samples[] | select(.merged_from_next_page == true)'
```
---
## Next Steps (Future Tasks)
### Task B1.4: Improve Code Block Detection
- Add syntax validation
- Use AST parsing for better language detection
- Improve continuation detection accuracy
### Task B1.5: Add Image Extraction
- Extract images from chunks
- OCR for code in images
- Diagram detection and extraction
### Task B1.6: Full PDF Scraper CLI
- Build on chunking foundation
- Category detection for chunks
- Multi-PDF support
---
## Integration with Skill Seeker
The chunking feature lays groundwork for:
1. **Memory-efficient processing** - Process PDFs chunk-by-chunk
2. **Better categorization** - Chapters become categories
3. **Improved SKILL.md** - Organize by detected chapters
4. **Large PDF support** - Handle 500+ page manuals
**Example workflow:**
```bash
# Extract large manual with chapters
python3 cli/pdf_extractor_poc.py large_manual.pdf --chunk-size 25 -o manual.json
# Future: Build skill from chunks
python3 cli/build_skill_from_pdf.py manual.json
# Result: SKILL.md organized by detected chapters
```
---
## API Usage
### Using PDFExtractor with Chunking
```python
from cli.pdf_extractor_poc import PDFExtractor
# Create extractor with 15-page chunks
extractor = PDFExtractor('manual.pdf', verbose=True, chunk_size=15)
# Extract
result = extractor.extract_all()
# Access chunks
for chunk in result['chunks']:
print(f"Chunk {chunk['chunk_number']}: {chunk['chapter_title']}")
print(f" Pages: {chunk['start_page']}-{chunk['end_page']}")
print(f" Total pages: {len(chunk['pages'])}")
# Access chapters
for chapter in result['chapters']:
print(f"Chapter: {chapter['title']}")
print(f" Pages: {chapter['start_page']}-{chapter['end_page']}")
```
### Processing Chunks Independently
```python
# Extract
result = extractor.extract_all()
# Process each chunk separately
for chunk in result['chunks']:
# Get pages in chunk
pages = chunk['pages']
# Process pages
for page in pages:
# Extract code samples
for code in page['code_samples']:
print(f"Found {code['language']} code")
# Check if merged from next page
if code.get('merged_from_next_page'):
print(" (merged from next page)")
```
---
## Conclusion
Task B1.3 successfully implements:
- ✅ Page chunking with configurable size
- ✅ Automatic chapter/section detection
- ✅ Code block merging across pages
- ✅ Enhanced output format with structure
- ✅ Foundation for large PDF handling
**Performance:** Minimal overhead (<1%)
**Compatibility:** Backward compatible (pages array still included)
**Quality:** Significantly improved organization
**Ready for B1.4:** Code block detection improvements
---
**Task Completed:** October 21, 2025
**Next Task:** B1.4 - Improve code block extraction with syntax detection
@@ -0,0 +1,420 @@
# PDF Extractor - Proof of Concept (Task B1.2)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Task:** B1.2 - Create simple PDF text extractor (proof of concept)
---
## Overview
This is a proof-of-concept PDF text and code extractor built for Skill Seeker. It demonstrates the feasibility of extracting documentation content from PDF files using PyMuPDF (fitz).
## Features
### ✅ Implemented
1. **Text Extraction** - Extract plain text from all PDF pages
2. **Markdown Conversion** - Convert PDF content to markdown format
3. **Code Block Detection** - Multiple detection methods:
- **Font-based:** Detects monospace fonts (Courier, Mono, Consolas, etc.)
- **Indent-based:** Detects consistently indented code blocks
- **Pattern-based:** Detects function/class definitions, imports
4. **Language Detection** - Auto-detect programming language from code content
5. **Heading Extraction** - Extract document structure from markdown
6. **Image Counting** - Track diagrams and screenshots
7. **JSON Output** - Compatible format with existing doc_scraper.py
### 🎯 Detection Methods
#### Font-Based Detection
Analyzes font properties to find monospace fonts typically used for code:
- Courier, Courier New
- Monaco, Menlo
- Consolas
- DejaVu Sans Mono
#### Indentation-Based Detection
Identifies code blocks by consistent indentation patterns:
- 4 spaces or tabs
- Minimum 2 consecutive lines
- Minimum 20 characters
#### Pattern-Based Detection
Uses regex to find common code structures:
- Function definitions (Python, JS, Go, etc.)
- Class definitions
- Import/require statements
### 🔍 Language Detection
Supports detection of 19 programming languages:
- Python, JavaScript, Java, C, C++, C#
- Go, Rust, PHP, Ruby, Swift, Kotlin
- Shell, SQL, HTML, CSS
- JSON, YAML, XML
---
## Installation
### Prerequisites
```bash
pip install PyMuPDF
```
### Verify Installation
```bash
python3 -c "import fitz; print(fitz.__doc__)"
```
---
## Usage
### Basic Usage
```bash
# Extract from PDF (print to stdout)
python3 cli/pdf_extractor_poc.py input.pdf
# Save to JSON file
python3 cli/pdf_extractor_poc.py input.pdf --output result.json
# Verbose mode (shows progress)
python3 cli/pdf_extractor_poc.py input.pdf --verbose
# Pretty-printed JSON
python3 cli/pdf_extractor_poc.py input.pdf --pretty
```
### Examples
```bash
# Extract Python documentation
python3 cli/pdf_extractor_poc.py docs/python_guide.pdf -o python_extracted.json -v
# Extract with verbose and pretty output
python3 cli/pdf_extractor_poc.py manual.pdf -o manual.json -v --pretty
# Quick test (print to screen)
python3 cli/pdf_extractor_poc.py sample.pdf --pretty
```
---
## Output Format
### JSON Structure
```json
{
"source_file": "input.pdf",
"metadata": {
"title": "Documentation Title",
"author": "Author Name",
"subject": "Subject",
"creator": "PDF Creator",
"producer": "PDF Producer"
},
"total_pages": 50,
"total_chars": 125000,
"total_code_blocks": 87,
"total_headings": 45,
"total_images": 12,
"languages_detected": {
"python": 52,
"javascript": 20,
"sql": 10,
"shell": 5
},
"pages": [
{
"page_number": 1,
"text": "Plain text content...",
"markdown": "# Heading\nContent...",
"headings": [
{
"level": "h1",
"text": "Getting Started"
}
],
"code_samples": [
{
"code": "def hello():\n print('Hello')",
"language": "python",
"detection_method": "font",
"font": "Courier-New"
}
],
"images_count": 2,
"char_count": 2500,
"code_blocks_count": 3
}
]
}
```
### Page Object
Each page contains:
- `page_number` - 1-indexed page number
- `text` - Plain text content
- `markdown` - Markdown-formatted content
- `headings` - Array of heading objects
- `code_samples` - Array of detected code blocks
- `images_count` - Number of images on page
- `char_count` - Character count
- `code_blocks_count` - Number of code blocks found
### Code Sample Object
Each code sample includes:
- `code` - The actual code text
- `language` - Detected language (or 'unknown')
- `detection_method` - How it was found ('font', 'indent', or 'pattern')
- `font` - Font name (if detected by font method)
- `pattern_type` - Type of pattern (if detected by pattern method)
---
## Technical Details
### Detection Accuracy
**Font-based detection:** ⭐⭐⭐⭐⭐ (Best)
- Highly accurate for well-formatted PDFs
- Relies on proper font usage in source document
- Works with: Technical docs, programming books, API references
**Indent-based detection:** ⭐⭐⭐⭐ (Good)
- Good for structured code blocks
- May capture non-code indented content
- Works with: Tutorials, guides, examples
**Pattern-based detection:** ⭐⭐⭐ (Fair)
- Captures specific code constructs
- May miss complex or unusual code
- Works with: Code snippets, function examples
### Language Detection Accuracy
- **High confidence:** Python, JavaScript, Java, Go, SQL
- **Medium confidence:** C++, Rust, PHP, Ruby, Swift
- **Basic detection:** Shell, JSON, YAML, XML
Detection based on keyword patterns, not AST parsing.
### Performance
Tested on various PDF sizes:
- Small (1-10 pages): < 1 second
- Medium (10-100 pages): 1-5 seconds
- Large (100-500 pages): 5-30 seconds
- Very Large (500+ pages): 30+ seconds
Memory usage: ~50-200 MB depending on PDF size and image content.
---
## Limitations
### Current Limitations
1. **No OCR** - Cannot extract text from scanned/image PDFs
2. **No Table Extraction** - Tables are treated as plain text
3. **No Image Extraction** - Only counts images, doesn't extract them
4. **Simple Deduplication** - May miss some duplicate code blocks
5. **No Multi-column Support** - May jumble multi-column layouts
### Known Issues
1. **Code Split Across Pages** - Code blocks spanning pages may be split
2. **Complex Layouts** - May struggle with complex PDF layouts
3. **Non-standard Fonts** - May miss code in non-standard monospace fonts
4. **Unicode Issues** - Some special characters may not preserve correctly
---
## Comparison with Web Scraper
| Feature | Web Scraper | PDF Extractor POC |
|---------|-------------|-------------------|
| Content source | HTML websites | PDF files |
| Code detection | CSS selectors | Font/indent/pattern |
| Language detection | CSS classes + heuristics | Pattern matching |
| Structure | Excellent | Good |
| Links | Full support | Not supported |
| Images | Referenced | Counted only |
| Categories | Auto-categorized | Not implemented |
| Output format | JSON | JSON (compatible) |
---
## Next Steps (Tasks B1.3-B1.8)
### B1.3: Add PDF Page Detection and Chunking
- Split large PDFs into manageable chunks
- Handle page-spanning code blocks
- Add chapter/section detection
### B1.4: Extract Code Blocks from PDFs
- Improve code block detection accuracy
- Add syntax validation
- Better language detection (use tree-sitter?)
### B1.5: Add PDF Image Extraction
- Extract diagrams as separate files
- Extract screenshots
- OCR support for code in images
### B1.6: Create `pdf_scraper.py` CLI Tool
- Full-featured CLI like `doc_scraper.py`
- Config file support
- Category detection
- Multi-PDF support
### B1.7: Add MCP Tool `scrape_pdf`
- Integrate with MCP server
- Add to existing 9 MCP tools
- Test with Claude Code
### B1.8: Create PDF Config Format
- Define JSON config for PDF sources
- Similar to web scraper configs
- Support multiple PDFs per skill
---
## Testing
### Manual Testing
1. **Create test PDF** (or use existing PDF documentation)
2. **Run extractor:**
```bash
python3 cli/pdf_extractor_poc.py test.pdf -o test_result.json -v --pretty
```
3. **Verify output:**
- Check `total_code_blocks` > 0
- Verify `languages_detected` includes expected languages
- Inspect `code_samples` for accuracy
### Test with Real Documentation
Recommended test PDFs:
- Python documentation (python.org)
- Django documentation
- PostgreSQL manual
- Any programming language reference
### Expected Results
Good PDF (well-formatted with monospace code):
- Detection rate: 80-95%
- Language accuracy: 85-95%
- False positives: < 5%
Poor PDF (scanned or badly formatted):
- Detection rate: 20-50%
- Language accuracy: 60-80%
- False positives: 10-30%
---
## Code Examples
### Using PDFExtractor Class Directly
```python
from cli.pdf_extractor_poc import PDFExtractor
# Create extractor
extractor = PDFExtractor('docs/manual.pdf', verbose=True)
# Extract all pages
result = extractor.extract_all()
# Access data
print(f"Total pages: {result['total_pages']}")
print(f"Code blocks: {result['total_code_blocks']}")
print(f"Languages: {result['languages_detected']}")
# Iterate pages
for page in result['pages']:
print(f"\nPage {page['page_number']}:")
print(f" Code blocks: {page['code_blocks_count']}")
for code in page['code_samples']:
print(f" - {code['language']}: {len(code['code'])} chars")
```
### Custom Language Detection
```python
from cli.pdf_extractor_poc import PDFExtractor
extractor = PDFExtractor('input.pdf')
# Override language detection
def custom_detect(code):
if 'SELECT' in code.upper():
return 'sql'
return extractor.detect_language_from_code(code)
# Use in extraction
# (requires modifying the class to support custom detection)
```
---
## Contributing
### Adding New Languages
To add language detection for a new language, edit `detect_language_from_code()`:
```python
patterns = {
# ... existing languages ...
'newlang': [r'pattern1', r'pattern2', r'pattern3'],
}
```
### Adding Detection Methods
To add a new detection method, create a method like:
```python
def detect_code_blocks_by_newmethod(self, page):
"""Detect code using new method"""
code_blocks = []
# ... your detection logic ...
return code_blocks
```
Then add it to `extract_page()`:
```python
newmethod_code_blocks = self.detect_code_blocks_by_newmethod(page)
all_code_blocks = font_code_blocks + indent_code_blocks + pattern_code_blocks + newmethod_code_blocks
```
---
## Conclusion
This POC successfully demonstrates:
- ✅ PyMuPDF can extract text from PDF documentation
- ✅ Multiple detection methods can identify code blocks
- ✅ Language detection works for common languages
- ✅ JSON output is compatible with existing doc_scraper.py
- ✅ Performance is acceptable for typical documentation PDFs
**Ready for B1.3:** The foundation is solid. Next step is adding page chunking and handling large PDFs.
---
**POC Completed:** October 21, 2025
**Next Task:** B1.3 - Add PDF page detection and chunking
@@ -0,0 +1,553 @@
# PDF Image Extraction (Task B1.5)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Task:** B1.5 - Add PDF image extraction (diagrams, screenshots)
---
## Overview
Task B1.5 adds the ability to extract images (diagrams, screenshots, charts) from PDF documentation and save them as separate files. This is essential for preserving visual documentation elements in skills.
## New Features
### ✅ 1. Image Extraction to Files
Extract embedded images from PDFs and save them to disk:
```bash
# Extract images along with text
python3 cli/pdf_extractor_poc.py manual.pdf --extract-images
# Specify output directory
python3 cli/pdf_extractor_poc.py manual.pdf --extract-images --image-dir assets/images/
# Filter small images (icons, bullets)
python3 cli/pdf_extractor_poc.py manual.pdf --extract-images --min-image-size 200
```
### ✅ 2. Size-Based Filtering
Automatically filter out small images (icons, bullets, decorations):
- **Default threshold:** 100x100 pixels
- **Configurable:** `--min-image-size`
- **Purpose:** Focus on meaningful diagrams and screenshots
### ✅ 3. Image Metadata
Each extracted image includes comprehensive metadata:
```json
{
"filename": "manual_page5_img1.png",
"path": "output/manual_images/manual_page5_img1.png",
"page_number": 5,
"width": 800,
"height": 600,
"format": "png",
"size_bytes": 45821,
"xref": 42
}
```
### ✅ 4. Automatic Directory Creation
Images are automatically organized:
- **Default:** `output/{pdf_name}_images/`
- **Naming:** `{pdf_name}_page{N}_img{M}.{ext}`
- **Formats:** PNG, JPEG, GIF, BMP, etc.
---
## Usage Examples
### Basic Image Extraction
```bash
# Extract all images from PDF
python3 cli/pdf_extractor_poc.py tutorial.pdf --extract-images -v
```
**Output:**
```
📄 Extracting from: tutorial.pdf
Pages: 50
Metadata: {...}
Image directory: output/tutorial_images
Page 1: 2500 chars, 3 code blocks, 2 headings, 0 images
Page 2: 1800 chars, 1 code blocks, 1 headings, 2 images
Extracted image: tutorial_page2_img1.png (800x600)
Extracted image: tutorial_page2_img2.jpeg (1024x768)
...
✅ Extraction complete:
Images found: 45
Images extracted: 32
Image directory: output/tutorial_images
```
### Custom Image Directory
```bash
# Save images to specific directory
python3 cli/pdf_extractor_poc.py manual.pdf --extract-images --image-dir docs/images/
```
Result: Images saved to `docs/images/manual_page*_img*.{ext}`
### Filter Small Images
```bash
# Only extract images >= 200x200 pixels
python3 cli/pdf_extractor_poc.py guide.pdf --extract-images --min-image-size 200 -v
```
**Verbose output shows filtering:**
```
Page 5: 3200 chars, 4 code blocks, 3 headings, 3 images
Skipping small image: 32x32
Skipping small image: 64x48
Extracted image: guide_page5_img3.png (1200x800)
```
### Complete Extraction Workflow
```bash
# Extract everything: text, code, images
python3 cli/pdf_extractor_poc.py documentation.pdf \
--extract-images \
--min-image-size 150 \
--min-quality 6.0 \
--chunk-size 20 \
--output documentation.json \
--verbose \
--pretty
```
---
## Output Format
### Enhanced JSON Structure
The output now includes image extraction data:
```json
{
"source_file": "manual.pdf",
"total_pages": 50,
"total_images": 45,
"total_extracted_images": 32,
"image_directory": "output/manual_images",
"extracted_images": [
{
"filename": "manual_page2_img1.png",
"path": "output/manual_images/manual_page2_img1.png",
"page_number": 2,
"width": 800,
"height": 600,
"format": "png",
"size_bytes": 45821,
"xref": 42
}
],
"pages": [
{
"page_number": 1,
"images_count": 3,
"extracted_images": [
{
"filename": "manual_page1_img1.jpeg",
"path": "output/manual_images/manual_page1_img1.jpeg",
"width": 1024,
"height": 768,
"format": "jpeg",
"size_bytes": 87543
}
]
}
]
}
```
### File System Layout
```
output/
├── manual.json # Extraction results
└── manual_images/ # Image directory
├── manual_page2_img1.png # Page 2, Image 1
├── manual_page2_img2.jpeg # Page 2, Image 2
├── manual_page5_img1.png # Page 5, Image 1
└── ...
```
---
## Technical Implementation
### Image Extraction Method
```python
def extract_images_from_page(self, page, page_num):
"""Extract images from PDF page and save to disk"""
extracted = []
image_list = page.get_images()
for img_index, img in enumerate(image_list):
# Get image data from PDF
xref = img[0]
base_image = self.doc.extract_image(xref)
image_bytes = base_image["image"]
image_ext = base_image["ext"]
width = base_image.get("width", 0)
height = base_image.get("height", 0)
# Filter small images
if width < self.min_image_size or height < self.min_image_size:
continue
# Generate filename
image_filename = f"{pdf_basename}_page{page_num+1}_img{img_index+1}.{image_ext}"
image_path = Path(self.image_dir) / image_filename
# Save image
with open(image_path, "wb") as f:
f.write(image_bytes)
# Store metadata
image_info = {
'filename': image_filename,
'path': str(image_path),
'page_number': page_num + 1,
'width': width,
'height': height,
'format': image_ext,
'size_bytes': len(image_bytes),
}
extracted.append(image_info)
return extracted
```
---
## Performance
### Extraction Speed
| PDF Size | Images | Extraction Time | Overhead |
|----------|--------|-----------------|----------|
| Small (10 pages, 5 images) | 5 | +200ms | ~10% |
| Medium (100 pages, 50 images) | 50 | +2s | ~15% |
| Large (500 pages, 200 images) | 200 | +8s | ~20% |
**Note:** Image extraction adds 10-20% overhead depending on image count and size.
### Storage Requirements
- **PNG images:** ~10-500 KB each (diagrams)
- **JPEG images:** ~50-2000 KB each (screenshots)
- **Typical documentation (100 pages):** ~50-200 MB total
---
## Supported Image Formats
PyMuPDF automatically handles format detection and extraction:
- ✅ PNG (lossless, best for diagrams)
- ✅ JPEG (lossy, best for photos)
- ✅ GIF (animated, rare in PDFs)
- ✅ BMP (uncompressed)
- ✅ TIFF (high quality)
Images are extracted in their original format.
---
## Filtering Strategy
### Why Filter Small Images?
PDFs often contain:
- **Icons:** 16x16, 32x32 (UI elements)
- **Bullets:** 8x8, 12x12 (decorative)
- **Logos:** 50x50, 100x100 (branding)
These are usually not useful for documentation skills.
### Recommended Thresholds
| Use Case | Min Size | Reasoning |
|----------|----------|-----------|
| **General docs** | 100x100 | Filters icons, keeps diagrams |
| **Technical diagrams** | 200x200 | Only meaningful charts |
| **Screenshots** | 300x300 | Only full-size screenshots |
| **All images** | 0 | No filtering |
**Set with:** `--min-image-size N`
---
## Integration with Skill Seeker
### Future Workflow (Task B1.6+)
When building PDF-based skills, images will be:
1. **Extracted** from PDF documentation
2. **Organized** into skill's `assets/` directory
3. **Referenced** in SKILL.md and reference files
4. **Packaged** in final .zip file
**Example:**
```markdown
# API Architecture
See diagram below for the complete API flow:
![API Flow](assets/images/api_flow.png)
The diagram shows...
```
---
## Limitations
### Current Limitations
1. **No OCR**
- Cannot extract text from images
- Code screenshots are not parsed
- Future: Add OCR support for code in images
2. **No Image Analysis**
- Cannot detect diagram types (flowchart, UML, etc.)
- Cannot extract captions
- Future: Add AI-based image classification
3. **No Deduplication**
- Same image on multiple pages extracted multiple times
- Future: Add image hash-based deduplication
4. **Format Preservation**
- Images saved in original format (no conversion)
- No optimization or compression
### Known Issues
1. **Vector Graphics**
- Some PDFs use vector graphics (not images)
- These are not extracted (rendered as part of page)
- Workaround: Use PDF-to-image tools first
2. **Embedded vs Referenced**
- Only embedded images are extracted
- External image references are not followed
3. **Image Quality**
- Quality depends on PDF source
- Low-res source = low-res output
---
## Troubleshooting
### No Images Extracted
**Problem:** `total_extracted_images: 0` but PDF has visible images
**Possible causes:**
1. Images are vector graphics (not raster)
2. Images smaller than `--min-image-size` threshold
3. Images are page backgrounds (not embedded images)
**Solution:**
```bash
# Try with no size filter
python3 cli/pdf_extractor_poc.py input.pdf --extract-images --min-image-size 0 -v
```
### Permission Errors
**Problem:** `PermissionError: [Errno 13] Permission denied`
**Solution:**
```bash
# Ensure output directory is writable
mkdir -p output/images
chmod 755 output/images
# Or specify different directory
python3 cli/pdf_extractor_poc.py input.pdf --extract-images --image-dir ~/my_images/
```
### Disk Space
**Problem:** Running out of disk space
**Solution:**
```bash
# Check PDF size first
du -h input.pdf
# Estimate: ~100-200 MB per 100 pages with images
# Use higher min-image-size to extract fewer images
python3 cli/pdf_extractor_poc.py input.pdf --extract-images --min-image-size 300
```
---
## Examples
### Extract Diagram-Heavy Documentation
```bash
# Architecture documentation with many diagrams
python3 cli/pdf_extractor_poc.py architecture.pdf \
--extract-images \
--min-image-size 250 \
--image-dir docs/diagrams/ \
-v
```
**Result:** High-quality diagrams extracted, icons filtered out.
### Tutorial with Screenshots
```bash
# Tutorial with step-by-step screenshots
python3 cli/pdf_extractor_poc.py tutorial.pdf \
--extract-images \
--min-image-size 400 \
--image-dir tutorial_screenshots/ \
-v
```
**Result:** Full screenshots extracted, UI icons ignored.
### API Reference with Small Charts
```bash
# API docs with various image sizes
python3 cli/pdf_extractor_poc.py api_reference.pdf \
--extract-images \
--min-image-size 150 \
-o api.json \
--pretty
```
**Result:** Charts and graphs extracted, small icons filtered.
---
## Command-Line Reference
### Image Extraction Options
```
--extract-images
Enable image extraction to files
Default: disabled
--image-dir PATH
Directory to save extracted images
Default: output/{pdf_name}_images/
--min-image-size PIXELS
Minimum image dimension (width or height)
Filters out icons and small decorations
Default: 100
```
### Complete Example
```bash
python3 cli/pdf_extractor_poc.py manual.pdf \
--extract-images \
--image-dir assets/images/ \
--min-image-size 200 \
--min-quality 7.0 \
--chunk-size 15 \
--output manual.json \
--verbose \
--pretty
```
---
## Comparison: Before vs After
| Feature | Before (B1.4) | After (B1.5) |
|---------|---------------|--------------|
| Image detection | ✅ Count only | ✅ Count + Extract |
| Image files | ❌ Not saved | ✅ Saved to disk |
| Image metadata | ❌ None | ✅ Full metadata |
| Size filtering | ❌ None | ✅ Configurable |
| Directory organization | ❌ N/A | ✅ Automatic |
| Format support | ❌ N/A | ✅ All formats |
---
## Next Steps
### Task B1.6: Full PDF Scraper CLI
The image extraction feature will be integrated into the full PDF scraper:
```bash
# Future: Full PDF scraper with images
python3 cli/pdf_scraper.py \
--config configs/manual_pdf.json \
--extract-images \
--enhance-local
```
### Task B1.7: MCP Tool Integration
Images will be available through MCP:
```python
# Future: MCP tool
result = mcp.scrape_pdf(
pdf_path="manual.pdf",
extract_images=True,
min_image_size=200
)
```
---
## Conclusion
Task B1.5 successfully implements:
- ✅ Image extraction from PDF pages
- ✅ Automatic file saving with metadata
- ✅ Size-based filtering (configurable)
- ✅ Organized directory structure
- ✅ Multiple format support
**Impact:**
- Preserves visual documentation
- Essential for diagram-heavy docs
- Improves skill completeness
**Performance:** 10-20% overhead (acceptable)
**Compatibility:** Backward compatible (images optional)
**Ready for B1.6:** Full PDF scraper CLI tool
---
**Task Completed:** October 21, 2025
**Next Task:** B1.6 - Create `pdf_scraper.py` CLI tool
@@ -0,0 +1,437 @@
# PDF Scraping MCP Tool (Task B1.7)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Task:** B1.7 - Add MCP tool `scrape_pdf`
---
## Overview
Task B1.7 adds the `scrape_pdf` MCP tool to the Skill Seeker MCP server, making PDF documentation scraping available through the Model Context Protocol. This allows Claude Code and other MCP clients to scrape PDF documentation directly.
## Features
### ✅ MCP Tool Integration
- **Tool name:** `scrape_pdf`
- **Description:** Scrape PDF documentation and build Claude skill
- **Supports:** All three usage modes (config, direct, from-json)
- **Integration:** Uses `cli/pdf_scraper.py` backend
### ✅ Three Usage Modes
1. **Config File Mode** - Use PDF config JSON
2. **Direct PDF Mode** - Quick conversion from PDF file
3. **From JSON Mode** - Build from pre-extracted data
---
## Usage
### Mode 1: Config File
```python
# Through MCP
result = await mcp.call_tool("scrape_pdf", {
"config_path": "configs/manual_pdf.json"
})
```
**Example config** (`configs/manual_pdf.json`):
```json
{
"name": "mymanual",
"description": "My Manual documentation",
"pdf_path": "docs/manual.pdf",
"extract_options": {
"chunk_size": 10,
"min_quality": 6.0,
"extract_images": true,
"min_image_size": 150
},
"categories": {
"getting_started": ["introduction", "setup"],
"api": ["api", "reference"],
"tutorial": ["tutorial", "example"]
}
}
```
**Output:**
```
🔍 Extracting from PDF: docs/manual.pdf
📄 Extracting from: docs/manual.pdf
Pages: 150
...
✅ Extraction complete
🏗️ Building skill: mymanual
📋 Categorizing content...
✅ Created 3 categories
📝 Generating reference files...
Generated: output/mymanual/references/getting_started.md
Generated: output/mymanual/references/api.md
Generated: output/mymanual/references/tutorial.md
✅ Skill built successfully: output/mymanual/
📦 Next step: Package with: python3 cli/package_skill.py output/mymanual/
```
### Mode 2: Direct PDF
```python
# Through MCP
result = await mcp.call_tool("scrape_pdf", {
"pdf_path": "manual.pdf",
"name": "mymanual",
"description": "My Manual Docs"
})
```
**Uses default settings:**
- Chunk size: 10
- Min quality: 5.0
- Extract images: true
- Chapter-based categorization
### Mode 3: From Extracted JSON
```python
# Step 1: Extract to JSON (separate tool or CLI)
# python3 cli/pdf_extractor_poc.py manual.pdf -o manual_extracted.json
# Step 2: Build skill from JSON via MCP
result = await mcp.call_tool("scrape_pdf", {
"from_json": "output/manual_extracted.json"
})
```
**Benefits:**
- Separate extraction and building
- Fast iteration on skill structure
- No re-extraction needed
---
## MCP Tool Definition
### Input Schema
```json
{
"name": "scrape_pdf",
"description": "Scrape PDF documentation and build Claude skill. Extracts text, code, and images from PDF files (NEW in B1.7).",
"inputSchema": {
"type": "object",
"properties": {
"config_path": {
"type": "string",
"description": "Path to PDF config JSON file (e.g., configs/manual_pdf.json)"
},
"pdf_path": {
"type": "string",
"description": "Direct PDF path (alternative to config_path)"
},
"name": {
"type": "string",
"description": "Skill name (required with pdf_path)"
},
"description": {
"type": "string",
"description": "Skill description (optional)"
},
"from_json": {
"type": "string",
"description": "Build from extracted JSON file (e.g., output/manual_extracted.json)"
}
},
"required": []
}
}
```
### Return Format
Returns `TextContent` with:
- Success: stdout from `pdf_scraper.py`
- Failure: stderr + stdout for debugging
---
## Implementation
### MCP Server Changes
**Location:** `skill_seeker_mcp/server.py`
**Changes:**
1. Added `scrape_pdf` to `list_tools()` (lines 220-249)
2. Added handler in `call_tool()` (lines 276-277)
3. Implemented `scrape_pdf_tool()` function (lines 591-625)
### Code Implementation
```python
async def scrape_pdf_tool(args: dict) -> list[TextContent]:
"""Scrape PDF documentation and build skill (NEW in B1.7)"""
config_path = args.get("config_path")
pdf_path = args.get("pdf_path")
name = args.get("name")
description = args.get("description")
from_json = args.get("from_json")
# Build command
cmd = [sys.executable, str(CLI_DIR / "pdf_scraper.py")]
# Mode 1: Config file
if config_path:
cmd.extend(["--config", config_path])
# Mode 2: Direct PDF
elif pdf_path and name:
cmd.extend(["--pdf", pdf_path, "--name", name])
if description:
cmd.extend(["--description", description])
# Mode 3: From JSON
elif from_json:
cmd.extend(["--from-json", from_json])
else:
return [TextContent(type="text", text="❌ Error: Must specify --config, --pdf + --name, or --from-json")]
# Run pdf_scraper.py
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
return [TextContent(type="text", text=result.stdout)]
else:
return [TextContent(type="text", text=f"Error: {result.stderr}\n\n{result.stdout}")]
```
---
## Integration with MCP Workflow
### Complete Workflow Through MCP
```python
# 1. Create PDF config (optional - can use direct mode)
config_result = await mcp.call_tool("generate_config", {
"name": "api_manual",
"url": "N/A", # Not used for PDF
"description": "API Manual from PDF"
})
# 2. Scrape PDF
scrape_result = await mcp.call_tool("scrape_pdf", {
"pdf_path": "docs/api_manual.pdf",
"name": "api_manual",
"description": "API Manual Documentation"
})
# 3. Package skill
package_result = await mcp.call_tool("package_skill", {
"skill_dir": "output/api_manual/",
"auto_upload": True # Upload if ANTHROPIC_API_KEY set
})
# 4. Upload (if not auto-uploaded)
if "ANTHROPIC_API_KEY" in os.environ:
upload_result = await mcp.call_tool("upload_skill", {
"skill_zip": "output/api_manual.zip"
})
```
### Combined with Web Scraping
```python
# Scrape web documentation
web_result = await mcp.call_tool("scrape_docs", {
"config_path": "configs/framework.json"
})
# Scrape PDF supplement
pdf_result = await mcp.call_tool("scrape_pdf", {
"pdf_path": "docs/framework_api.pdf",
"name": "framework_pdf"
})
# Package both
await mcp.call_tool("package_skill", {"skill_dir": "output/framework/"})
await mcp.call_tool("package_skill", {"skill_dir": "output/framework_pdf/"})
```
---
## Error Handling
### Common Errors
**Error 1: Missing required parameters**
```
❌ Error: Must specify --config, --pdf + --name, or --from-json
```
**Solution:** Provide one of the three modes
**Error 2: PDF file not found**
```
Error: [Errno 2] No such file or directory: 'manual.pdf'
```
**Solution:** Check PDF path is correct
**Error 3: PyMuPDF not installed**
```
ERROR: PyMuPDF not installed
Install with: pip install PyMuPDF
```
**Solution:** Install PyMuPDF: `pip install PyMuPDF`
**Error 4: Invalid JSON config**
```
Error: json.decoder.JSONDecodeError: Expecting value: line 1 column 1
```
**Solution:** Check config file is valid JSON
---
## Testing
### Test MCP Tool
```bash
# 1. Start MCP server
python3 skill_seeker_mcp/server.py
# 2. Test with MCP client or via Claude Code
# 3. Verify tool is listed
# Should see "scrape_pdf" in available tools
```
### Test All Modes
**Mode 1: Config**
```python
result = await mcp.call_tool("scrape_pdf", {
"config_path": "configs/example_pdf.json"
})
assert "✅ Skill built successfully" in result[0].text
```
**Mode 2: Direct**
```python
result = await mcp.call_tool("scrape_pdf", {
"pdf_path": "test.pdf",
"name": "test_skill"
})
assert "✅ Skill built successfully" in result[0].text
```
**Mode 3: From JSON**
```python
# First extract
subprocess.run(["python3", "cli/pdf_extractor_poc.py", "test.pdf", "-o", "test.json"])
# Then build via MCP
result = await mcp.call_tool("scrape_pdf", {
"from_json": "test.json"
})
assert "✅ Skill built successfully" in result[0].text
```
---
## Comparison with Other MCP Tools
| Tool | Input | Output | Use Case |
|------|-------|--------|----------|
| `scrape_docs` | HTML URL | Skill | Web documentation |
| `scrape_pdf` | PDF file | Skill | PDF documentation |
| `generate_config` | URL | Config | Create web config |
| `package_skill` | Skill dir | .zip | Package for upload |
| `upload_skill` | .zip file | Upload | Send to Claude |
---
## Performance
### MCP Tool Overhead
- **MCP overhead:** ~50-100ms
- **Extraction time:** Same as CLI (15s-5m depending on PDF)
- **Building time:** Same as CLI (5s-45s)
**Total:** MCP adds negligible overhead (<1%)
### Async Execution
The MCP tool runs `pdf_scraper.py` synchronously via `subprocess.run()`. For long-running PDFs:
- Client waits for completion
- No progress updates during extraction
- Consider using `--from-json` mode for faster iteration
---
## Future Enhancements
### Potential Improvements
1. **Async Extraction**
- Stream progress updates to client
- Allow cancellation
- Background processing
2. **Batch Processing**
- Process multiple PDFs in parallel
- Merge into single skill
- Shared categories
3. **Enhanced Options**
- Pass all extraction options through MCP
- Dynamic quality threshold
- Image filter controls
4. **Status Checking**
- Query extraction status
- Get progress percentage
- Estimate time remaining
---
## Conclusion
Task B1.7 successfully implements:
- ✅ MCP tool `scrape_pdf`
- ✅ Three usage modes (config, direct, from-json)
- ✅ Integration with MCP server
- ✅ Error handling
- ✅ Compatible with existing MCP workflow
**Impact:**
- PDF scraping available through MCP
- Seamless integration with Claude Code
- Unified workflow for web + PDF documentation
- 10th MCP tool in Skill Seeker
**Total MCP Tools:** 10
1. generate_config
2. estimate_pages
3. scrape_docs
4. package_skill
5. upload_skill
6. list_configs
7. validate_config
8. split_config
9. generate_router
10. **scrape_pdf** (NEW)
---
**Task Completed:** October 21, 2025
**B1 Group Complete:** All 8 tasks (B1.1-B1.8) finished!
**Next:** Task group B2 (Microsoft Word .docx support)
@@ -0,0 +1,491 @@
# PDF Parsing Libraries Research (Task B1.1)
**Date:** October 21, 2025
**Task:** B1.1 - Research PDF parsing libraries
**Purpose:** Evaluate Python libraries for extracting text and code from PDF documentation
---
## Executive Summary
After comprehensive research, **PyMuPDF (fitz)** is recommended as the primary library for Skill Seeker's PDF parsing needs, with **pdfplumber** as a secondary option for complex table extraction.
### Quick Recommendation:
- **Primary Choice:** PyMuPDF (fitz) - Fast, comprehensive, well-maintained
- **Secondary/Fallback:** pdfplumber - Better for tables, slower but more precise
- **Avoid:** PyPDF2 (deprecated, merged into pypdf)
---
## Library Comparison Matrix
| Library | Speed | Text Quality | Code Detection | Tables | Maintenance | License |
|---------|-------|--------------|----------------|--------|-------------|---------|
| **PyMuPDF** | ⚡⚡⚡⚡⚡ Fastest (42ms) | High | Excellent | Good | Active | AGPL/Commercial |
| **pdfplumber** | ⚡⚡ Slower (2.5s) | Very High | Excellent | Excellent | Active | MIT |
| **pypdf** | ⚡⚡⚡ Fast | Medium | Good | Basic | Active | BSD |
| **pdfminer.six** | ⚡ Slow | Very High | Good | Medium | Active | MIT |
| **pypdfium2** | ⚡⚡⚡⚡⚡ Very Fast (3ms) | Medium | Good | Basic | Active | Apache-2.0 |
---
## Detailed Analysis
### 1. PyMuPDF (fitz) ⭐ RECOMMENDED
**Performance:** 42 milliseconds (60x faster than pdfminer.six)
**Installation:**
```bash
pip install PyMuPDF
```
**Pros:**
- ✅ Extremely fast (C-based MuPDF backend)
- ✅ Comprehensive features (text, images, tables, metadata)
- ✅ Supports markdown output
- ✅ Can extract images and diagrams
- ✅ Well-documented and actively maintained
- ✅ Handles complex layouts well
**Cons:**
- ⚠️ AGPL license (requires commercial license for proprietary projects)
- ⚠️ Requires MuPDF binary installation (handled by pip)
- ⚠️ Slightly larger dependency footprint
**Code Example:**
```python
import fitz # PyMuPDF
# Extract text from entire PDF
def extract_pdf_text(pdf_path):
doc = fitz.open(pdf_path)
text = ''
for page in doc:
text += page.get_text()
doc.close()
return text
# Extract text from single page
def extract_page_text(pdf_path, page_num):
doc = fitz.open(pdf_path)
page = doc.load_page(page_num)
text = page.get_text()
doc.close()
return text
# Extract with markdown formatting
def extract_as_markdown(pdf_path):
doc = fitz.open(pdf_path)
markdown = ''
for page in doc:
markdown += page.get_text("markdown")
doc.close()
return markdown
```
**Use Cases for Skill Seeker:**
- Fast extraction of code examples from PDF docs
- Preserving formatting for code blocks
- Extracting diagrams and screenshots
- High-volume documentation scraping
---
### 2. pdfplumber ⭐ RECOMMENDED (for tables)
**Performance:** ~2.5 seconds (slower but more precise)
**Installation:**
```bash
pip install pdfplumber
```
**Pros:**
- ✅ MIT license (fully open source)
- ✅ Exceptional table extraction
- ✅ Visual debugging tool
- ✅ Precise layout preservation
- ✅ Built on pdfminer (proven text extraction)
- ✅ No binary dependencies
**Cons:**
- ⚠️ Slower than PyMuPDF
- ⚠️ Higher memory usage for large PDFs
- ⚠️ Requires more configuration for optimal results
**Code Example:**
```python
import pdfplumber
# Extract text from PDF
def extract_with_pdfplumber(pdf_path):
with pdfplumber.open(pdf_path) as pdf:
text = ''
for page in pdf.pages:
text += page.extract_text()
return text
# Extract tables
def extract_tables(pdf_path):
tables = []
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
page_tables = page.extract_tables()
tables.extend(page_tables)
return tables
# Extract specific region (for code blocks)
def extract_region(pdf_path, page_num, bbox):
with pdfplumber.open(pdf_path) as pdf:
page = pdf.pages[page_num]
cropped = page.crop(bbox)
return cropped.extract_text()
```
**Use Cases for Skill Seeker:**
- Extracting API reference tables from PDFs
- Precise code block extraction with layout
- Documentation with complex table structures
---
### 3. pypdf (formerly PyPDF2)
**Performance:** Fast (medium speed)
**Installation:**
```bash
pip install pypdf
```
**Pros:**
- ✅ BSD license
- ✅ Simple API
- ✅ Can modify PDFs (merge, split, encrypt)
- ✅ Actively maintained (PyPDF2 merged back)
- ✅ No external dependencies
**Cons:**
- ⚠️ Limited complex layout support
- ⚠️ Basic text extraction only
- ⚠️ Poor with scanned/image PDFs
- ⚠️ No table extraction
**Code Example:**
```python
from pypdf import PdfReader
# Extract text
def extract_with_pypdf(pdf_path):
reader = PdfReader(pdf_path)
text = ''
for page in reader.pages:
text += page.extract_text()
return text
```
**Use Cases for Skill Seeker:**
- Simple text extraction
- Fallback when PyMuPDF licensing is an issue
- Basic PDF manipulation tasks
---
### 4. pdfminer.six
**Performance:** Slow (~2.5 seconds)
**Installation:**
```bash
pip install pdfminer.six
```
**Pros:**
- ✅ MIT license
- ✅ Excellent text quality (preserves formatting)
- ✅ Handles complex layouts
- ✅ Pure Python (no binaries)
**Cons:**
- ⚠️ Slowest option
- ⚠️ Complex API
- ⚠️ Poor documentation
- ⚠️ Limited table support
**Use Cases for Skill Seeker:**
- Not recommended (pdfplumber is built on this with better API)
---
### 5. pypdfium2
**Performance:** Very fast (3ms - fastest tested)
**Installation:**
```bash
pip install pypdfium2
```
**Pros:**
- ✅ Extremely fast
- ✅ Apache 2.0 license
- ✅ Lightweight
- ✅ Clean output
**Cons:**
- ⚠️ Basic features only
- ⚠️ Limited documentation
- ⚠️ No table extraction
- ⚠️ Newer/less proven
**Use Cases for Skill Seeker:**
- High-speed basic extraction
- Potential future optimization
---
## Licensing Considerations
### Open Source Projects (Skill Seeker):
- **PyMuPDF:** ✅ AGPL license is fine for open-source projects
- **pdfplumber:** ✅ MIT license (most permissive)
- **pypdf:** ✅ BSD license (permissive)
### Important Note:
PyMuPDF requires AGPL compliance (source code must be shared) OR a commercial license for proprietary use. Since Skill Seeker is open source on GitHub, AGPL is acceptable.
---
## Performance Benchmarks
Based on 2025 testing:
| Library | Time (single page) | Time (100 pages) |
|---------|-------------------|------------------|
| pypdfium2 | 0.003s | 0.3s |
| PyMuPDF | 0.042s | 4.2s |
| pypdf | 0.1s | 10s |
| pdfplumber | 2.5s | 250s |
| pdfminer.six | 2.5s | 250s |
**Winner:** pypdfium2 (speed) / PyMuPDF (features + speed balance)
---
## Recommendations for Skill Seeker
### Primary Approach: PyMuPDF (fitz)
**Why:**
1. **Speed** - 60x faster than alternatives
2. **Features** - Text, images, markdown output, metadata
3. **Quality** - High-quality text extraction
4. **Maintained** - Active development, good docs
5. **License** - AGPL is fine for open source
**Implementation Strategy:**
```python
import fitz # PyMuPDF
def extract_pdf_documentation(pdf_path):
"""
Extract documentation from PDF with code block detection
"""
doc = fitz.open(pdf_path)
pages = []
for page_num, page in enumerate(doc):
# Get text with layout info
text = page.get_text("text")
# Get markdown (preserves code blocks)
markdown = page.get_text("markdown")
# Get images (for diagrams)
images = page.get_images()
pages.append({
'page_number': page_num,
'text': text,
'markdown': markdown,
'images': images
})
doc.close()
return pages
```
### Fallback Approach: pdfplumber
**When to use:**
- PDF has complex tables that PyMuPDF misses
- Need visual debugging
- License concerns (use MIT instead of AGPL)
**Implementation Strategy:**
```python
import pdfplumber
def extract_pdf_tables(pdf_path):
"""
Extract tables from PDF documentation
"""
with pdfplumber.open(pdf_path) as pdf:
tables = []
for page in pdf.pages:
page_tables = page.extract_tables()
if page_tables:
tables.extend(page_tables)
return tables
```
---
## Code Block Detection Strategy
PDFs don't have semantic "code block" markers like HTML. Detection strategies:
### 1. Font-based Detection
```python
# PyMuPDF can detect font changes
def detect_code_by_font(page):
blocks = page.get_text("dict")["blocks"]
code_blocks = []
for block in blocks:
if 'lines' in block:
for line in block['lines']:
for span in line['spans']:
font = span['font']
# Monospace fonts indicate code
if 'Courier' in font or 'Mono' in font:
code_blocks.append(span['text'])
return code_blocks
```
### 2. Indentation-based Detection
```python
def detect_code_by_indent(text):
lines = text.split('\n')
code_blocks = []
current_block = []
for line in lines:
# Code often has consistent indentation
if line.startswith(' ') or line.startswith('\t'):
current_block.append(line)
elif current_block:
code_blocks.append('\n'.join(current_block))
current_block = []
return code_blocks
```
### 3. Pattern-based Detection
```python
import re
def detect_code_by_pattern(text):
# Look for common code patterns
patterns = [
r'(def \w+\(.*?\):)', # Python functions
r'(function \w+\(.*?\) \{)', # JavaScript
r'(class \w+:)', # Python classes
r'(import \w+)', # Import statements
]
code_snippets = []
for pattern in patterns:
matches = re.findall(pattern, text)
code_snippets.extend(matches)
return code_snippets
```
---
## Next Steps (Task B1.2+)
### Immediate Next Task: B1.2 - Create Simple PDF Text Extractor
**Goal:** Proof of concept using PyMuPDF
**Implementation Plan:**
1. Create `cli/pdf_extractor_poc.py`
2. Extract text from sample PDF
3. Detect code blocks using font/pattern matching
4. Output to JSON (similar to web scraper)
**Dependencies:**
```bash
pip install PyMuPDF
```
**Expected Output:**
```json
{
"pages": [
{
"page_number": 1,
"text": "...",
"code_blocks": ["def main():", "import sys"],
"images": []
}
]
}
```
### Future Tasks:
- **B1.3:** Add page chunking (split large PDFs)
- **B1.4:** Improve code block detection
- **B1.5:** Extract images/diagrams
- **B1.6:** Create full `pdf_scraper.py` CLI
- **B1.7:** Add MCP tool integration
- **B1.8:** Create PDF config format
---
## Additional Resources
### Documentation:
- PyMuPDF: https://pymupdf.readthedocs.io/
- pdfplumber: https://github.com/jsvine/pdfplumber
- pypdf: https://pypdf.readthedocs.io/
### Comparison Studies:
- 2025 Comparative Study: https://arxiv.org/html/2410.09871v1
- Performance Benchmarks: https://github.com/py-pdf/benchmarks
### Example Use Cases:
- Extracting API docs from PDF manuals
- Converting PDF guides to markdown
- Building skills from PDF-only documentation
---
## Conclusion
**For Skill Seeker's PDF documentation extraction:**
1. **Use PyMuPDF (fitz)** as primary library
2. **Add pdfplumber** for complex table extraction
3. **Detect code blocks** using font + pattern matching
4. **Preserve formatting** with markdown output
5. **Extract images** for diagrams/screenshots
**Estimated Implementation Time:**
- B1.2 (POC): 2-3 hours
- B1.3-B1.5 (Features): 5-8 hours
- B1.6 (CLI): 3-4 hours
- B1.7 (MCP): 2-3 hours
- B1.8 (Config): 1-2 hours
- **Total: 13-20 hours** for complete PDF support
**License:** AGPL (PyMuPDF) is acceptable for Skill Seeker (open source)
---
**Research completed:** ✅ October 21, 2025
**Next task:** B1.2 - Create simple PDF text extractor (proof of concept)
@@ -0,0 +1,616 @@
# PDF Scraper CLI Tool (Tasks B1.6 + B1.8)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Tasks:** B1.6 - Create pdf_scraper.py CLI tool, B1.8 - PDF config format
---
## Overview
The PDF scraper (`pdf_scraper.py`) is a complete CLI tool that converts PDF documentation into Claude AI skills. It integrates all PDF extraction features (B1.1-B1.5) with the Skill Seeker workflow to produce packaged, uploadable skills.
## Features
### ✅ Complete Workflow
1. **Extract** - Uses `pdf_extractor_poc.py` for extraction
2. **Categorize** - Organizes content by chapters or keywords
3. **Build** - Creates skill structure (SKILL.md, references/)
4. **Package** - Ready for `package_skill.py`
### ✅ Three Usage Modes
1. **Config File** - Use JSON configuration (recommended)
2. **Direct PDF** - Quick conversion from PDF file
3. **From JSON** - Build skill from pre-extracted data
### ✅ Automatic Categorization
- Chapter-based (from PDF structure)
- Keyword-based (configurable)
- Fallback to single category
### ✅ Quality Filtering
- Uses quality scores from B1.4
- Extracts top code examples
- Filters by minimum quality threshold
---
## Usage
### Mode 1: Config File (Recommended)
```bash
# Create config file
cat > configs/my_manual.json <<EOF
{
"name": "mymanual",
"description": "My Manual documentation",
"pdf_path": "docs/manual.pdf",
"extract_options": {
"chunk_size": 10,
"min_quality": 6.0,
"extract_images": true,
"min_image_size": 150
},
"categories": {
"getting_started": ["introduction", "setup"],
"api": ["api", "reference", "function"],
"tutorial": ["tutorial", "example", "guide"]
}
}
EOF
# Run scraper
python3 cli/pdf_scraper.py --config configs/my_manual.json
```
**Output:**
```
🔍 Extracting from PDF: docs/manual.pdf
📄 Extracting from: docs/manual.pdf
Pages: 150
...
✅ Extraction complete
💾 Saved extracted data to: output/mymanual_extracted.json
🏗️ Building skill: mymanual
📋 Categorizing content...
✅ Created 3 categories
- Getting Started: 25 pages
- Api: 80 pages
- Tutorial: 45 pages
📝 Generating reference files...
Generated: output/mymanual/references/getting_started.md
Generated: output/mymanual/references/api.md
Generated: output/mymanual/references/tutorial.md
Generated: output/mymanual/references/index.md
Generated: output/mymanual/SKILL.md
✅ Skill built successfully: output/mymanual/
📦 Next step: Package with: python3 cli/package_skill.py output/mymanual/
```
### Mode 2: Direct PDF
```bash
# Quick conversion without config file
python3 cli/pdf_scraper.py --pdf manual.pdf --name mymanual --description "My Manual Docs"
```
**Uses default settings:**
- Chunk size: 10
- Min quality: 5.0
- Extract images: true
- Min image size: 100px
- No custom categories (chapter-based)
### Mode 3: From Extracted JSON
```bash
# Step 1: Extract only (saves JSON)
python3 cli/pdf_extractor_poc.py manual.pdf -o manual_extracted.json --extract-images
# Step 2: Build skill from JSON (fast, can iterate)
python3 cli/pdf_scraper.py --from-json manual_extracted.json
```
**Benefits:**
- Separate extraction and building
- Iterate on skill structure without re-extracting
- Faster development cycle
---
## Config File Format (Task B1.8)
### Complete Example
```json
{
"name": "godot_manual",
"description": "Godot Engine documentation from PDF manual",
"pdf_path": "docs/godot_manual.pdf",
"extract_options": {
"chunk_size": 15,
"min_quality": 6.0,
"extract_images": true,
"min_image_size": 200
},
"categories": {
"getting_started": [
"introduction",
"getting started",
"installation",
"first steps"
],
"scripting": [
"gdscript",
"scripting",
"code",
"programming"
],
"3d": [
"3d",
"spatial",
"mesh",
"shader"
],
"2d": [
"2d",
"sprite",
"tilemap",
"animation"
],
"api": [
"api",
"class reference",
"method",
"property"
]
}
}
```
### Field Reference
#### Required Fields
- **`name`** (string): Skill identifier
- Used for directory names
- Should be lowercase, no spaces
- Example: `"python_guide"`
- **`pdf_path`** (string): Path to PDF file
- Absolute or relative to working directory
- Example: `"docs/manual.pdf"`
#### Optional Fields
- **`description`** (string): Skill description
- Shows in SKILL.md
- Explains when to use the skill
- Default: `"Documentation skill for {name}"`
- **`extract_options`** (object): Extraction settings
- `chunk_size` (number): Pages per chunk (default: 10)
- `min_quality` (number): Minimum code quality 0-10 (default: 5.0)
- `extract_images` (boolean): Extract images to files (default: true)
- `min_image_size` (number): Minimum image dimension in pixels (default: 100)
- **`categories`** (object): Keyword-based categorization
- Keys: Category names (will be sanitized for filenames)
- Values: Arrays of keywords to match
- If omitted: Uses chapter-based categorization from PDF
---
## Output Structure
### Generated Files
```
output/
├── mymanual_extracted.json # Raw extraction data (B1.5 format)
└── mymanual/ # Skill directory
├── SKILL.md # Main skill file
├── references/ # Reference documentation
│ ├── index.md # Category index
│ ├── getting_started.md # Category 1
│ ├── api.md # Category 2
│ └── tutorial.md # Category 3
├── scripts/ # Empty (for user scripts)
└── assets/ # Assets directory
└── images/ # Extracted images (if enabled)
├── mymanual_page5_img1.png
└── mymanual_page12_img2.jpeg
```
### SKILL.md Format
```markdown
# Mymanual Documentation Skill
My Manual documentation
## When to use this skill
Use this skill when the user asks about mymanual documentation,
including API references, tutorials, examples, and best practices.
## What's included
This skill contains:
- **Getting Started**: 25 pages
- **Api**: 80 pages
- **Tutorial**: 45 pages
## Quick Reference
### Top Code Examples
**Example 1** (Quality: 8.5/10):
```python
def initialize_system():
config = load_config()
setup_logging(config)
return System(config)
```
**Example 2** (Quality: 8.2/10):
```javascript
const app = createApp({
data() {
return { count: 0 }
}
})
```
## Navigation
See `references/index.md` for complete documentation structure.
## Languages Covered
- python: 45 examples
- javascript: 32 examples
- shell: 8 examples
```
### Reference File Format
Each category gets its own reference file:
```markdown
# Getting Started
## Installation
This guide will walk you through installing the software...
### Code Examples
```bash
curl -O https://example.com/install.sh
bash install.sh
```
---
## Configuration
After installation, configure your environment...
### Code Examples
```yaml
server:
port: 8080
host: localhost
```
---
```
---
## Categorization Logic
### Chapter-Based (Automatic)
If PDF has detectable chapters (from B1.3):
1. Extract chapter titles and page ranges
2. Create one category per chapter
3. Assign pages to chapters by page number
**Advantages:**
- Automatic, no config needed
- Respects document structure
- Accurate page assignment
**Example chapters:**
- "Chapter 1: Introduction" → `chapter_1_introduction.md`
- "Part 2: Advanced Topics" → `part_2_advanced_topics.md`
### Keyword-Based (Configurable)
If `categories` config is provided:
1. Score each page against keyword lists
2. Assign to highest-scoring category
3. Fall back to "other" if no match
**Advantages:**
- Flexible, customizable
- Works with PDFs without clear chapters
- Can combine related sections
**Scoring:**
- Keyword in page text: +1 point
- Keyword in page heading: +2 points
- Assigned to category with highest score
---
## Integration with Skill Seeker
### Complete Workflow
```bash
# 1. Create PDF config
cat > configs/api_manual.json <<EOF
{
"name": "api_manual",
"pdf_path": "docs/api.pdf",
"extract_options": {
"min_quality": 7.0,
"extract_images": true
}
}
EOF
# 2. Run PDF scraper
python3 cli/pdf_scraper.py --config configs/api_manual.json
# 3. Package skill
python3 cli/package_skill.py output/api_manual/
# 4. Upload to Claude (if ANTHROPIC_API_KEY set)
python3 cli/package_skill.py output/api_manual/ --upload
# Result: api_manual.zip ready for Claude!
```
### Enhancement (Optional)
```bash
# After building, enhance with AI
python3 cli/enhance_skill_local.py output/api_manual/
# Or with API
export ANTHROPIC_API_KEY=sk-ant-...
python3 cli/enhance_skill.py output/api_manual/
```
---
## Performance
### Benchmark
| PDF Size | Pages | Extraction | Building | Total |
|----------|-------|------------|----------|-------|
| Small | 50 | 30s | 5s | 35s |
| Medium | 200 | 2m | 15s | 2m 15s |
| Large | 500 | 5m | 45s | 5m 45s |
**Extraction**: PDF → JSON (cpu-intensive)
**Building**: JSON → Skill (fast, i/o-bound)
### Optimization Tips
1. **Use `--from-json` for iteration**
- Extract once, build many times
- Test categorization without re-extraction
2. **Adjust chunk size**
- Larger chunks: Faster extraction
- Smaller chunks: Better chapter detection
3. **Filter aggressively**
- Higher `min_quality`: Fewer low-quality code blocks
- Higher `min_image_size`: Fewer small images
---
## Examples
### Example 1: Programming Language Manual
```json
{
"name": "python_reference",
"description": "Python 3.12 Language Reference",
"pdf_path": "python-3.12-reference.pdf",
"extract_options": {
"chunk_size": 20,
"min_quality": 7.0,
"extract_images": false
},
"categories": {
"basics": ["introduction", "basic", "syntax", "types"],
"functions": ["function", "lambda", "decorator"],
"classes": ["class", "object", "inheritance"],
"modules": ["module", "package", "import"],
"stdlib": ["library", "standard library", "built-in"]
}
}
```
### Example 2: API Documentation
```json
{
"name": "rest_api_docs",
"description": "REST API Documentation",
"pdf_path": "api_docs.pdf",
"extract_options": {
"chunk_size": 10,
"min_quality": 6.0,
"extract_images": true,
"min_image_size": 200
},
"categories": {
"authentication": ["auth", "login", "token", "oauth"],
"users": ["user", "account", "profile"],
"products": ["product", "catalog", "inventory"],
"orders": ["order", "purchase", "checkout"],
"webhooks": ["webhook", "event", "callback"]
}
}
```
### Example 3: Framework Documentation
```json
{
"name": "django_docs",
"description": "Django Web Framework Documentation",
"pdf_path": "django-4.2-docs.pdf",
"extract_options": {
"chunk_size": 15,
"min_quality": 6.5,
"extract_images": true
}
}
```
*Note: No categories - uses chapter-based categorization*
---
## Troubleshooting
### No Categories Created
**Problem:** Only "content" or "other" category
**Possible causes:**
1. No chapters detected in PDF
2. Keywords don't match content
3. Config has empty categories
**Solution:**
```bash
# Check extracted chapters
cat output/mymanual_extracted.json | jq '.chapters'
# If empty, add keyword categories to config
# Or let it create single "content" category (OK for small PDFs)
```
### Low-Quality Code Blocks
**Problem:** Too many poor code examples
**Solution:**
```json
{
"extract_options": {
"min_quality": 7.0 // Increase threshold
}
}
```
### Images Not Extracted
**Problem:** No images in `assets/images/`
**Solution:**
```json
{
"extract_options": {
"extract_images": true, // Enable extraction
"min_image_size": 50 // Lower threshold
}
}
```
---
## Comparison with Web Scraper
| Feature | Web Scraper | PDF Scraper |
|---------|-------------|-------------|
| Input | HTML websites | PDF files |
| Crawling | Multi-page BFS | Single-file extraction |
| Structure detection | CSS selectors | Font/heading analysis |
| Categorization | URL patterns | Chapters/keywords |
| Images | Referenced | Embedded (extracted) |
| Code detection | `<pre><code>` | Font/indent/pattern |
| Language detection | CSS classes | Pattern matching |
| Quality scoring | No | Yes (B1.4) |
| Chunking | No | Yes (B1.3) |
---
## Next Steps
### Task B1.7: MCP Tool Integration
The PDF scraper will be available through MCP:
```python
# Future: MCP tool
result = mcp.scrape_pdf(
config_path="configs/manual.json"
)
# Or direct
result = mcp.scrape_pdf(
pdf_path="manual.pdf",
name="mymanual",
extract_images=True
)
```
---
## Conclusion
Tasks B1.6 and B1.8 successfully implement:
**B1.6 - PDF Scraper CLI:**
- ✅ Complete extraction → building workflow
- ✅ Three usage modes (config, direct, from-json)
- ✅ Automatic categorization (chapter or keyword-based)
- ✅ Integration with Skill Seeker workflow
- ✅ Quality filtering and top examples
**B1.8 - PDF Config Format:**
- ✅ JSON configuration format
- ✅ Extraction options (chunk size, quality, images)
- ✅ Category definitions (keyword-based)
- ✅ Compatible with web scraper config style
**Impact:**
- Complete PDF documentation support
- Parallel workflow to web scraping
- Reusable extraction results
- High-quality skill generation
**Ready for B1.7:** MCP tool integration
---
**Tasks Completed:** October 21, 2025
**Next Task:** B1.7 - Add MCP tool `scrape_pdf`
@@ -0,0 +1,576 @@
# PDF Code Block Syntax Detection (Task B1.4)
**Status:** ✅ Completed
**Date:** October 21, 2025
**Task:** B1.4 - Extract code blocks from PDFs with syntax detection
---
## Overview
Task B1.4 enhances the PDF extractor with advanced code block detection capabilities including:
- **Confidence scoring** for language detection
- **Syntax validation** to filter out false positives
- **Quality scoring** to rank code blocks by usefulness
- **Automatic filtering** of low-quality code
This dramatically improves the accuracy and usefulness of extracted code samples from PDF documentation.
---
## New Features
### ✅ 1. Confidence-Based Language Detection
Enhanced language detection now returns both language and confidence score:
**Before (B1.2):**
```python
lang = detect_language_from_code(code) # Returns: 'python'
```
**After (B1.4):**
```python
lang, confidence = detect_language_from_code(code) # Returns: ('python', 0.85)
```
**Confidence Calculation:**
- Pattern matches are weighted (1-5 points)
- Scores are normalized to 0-1 range
- Higher confidence = more reliable detection
**Example Pattern Weights:**
```python
'python': [
(r'\bdef\s+\w+\s*\(', 3), # Strong indicator
(r'\bimport\s+\w+', 2), # Medium indicator
(r':\s*$', 1), # Weak indicator (lines ending with :)
]
```
### ✅ 2. Syntax Validation
Validates detected code blocks to filter false positives:
**Validation Checks:**
1. **Not empty** - Rejects empty code blocks
2. **Indentation consistency** (Python) - Detects mixed tabs/spaces
3. **Balanced brackets** - Checks for unclosed parentheses, braces
4. **Language-specific syntax** (JSON) - Attempts to parse
5. **Natural language detection** - Filters out prose misidentified as code
6. **Comment ratio** - Rejects blocks that are mostly comments
**Output:**
```json
{
"code": "def example():\n return True",
"language": "python",
"is_valid": true,
"validation_issues": []
}
```
**Invalid example:**
```json
{
"code": "This is not code",
"language": "unknown",
"is_valid": false,
"validation_issues": ["May be natural language, not code"]
}
```
### ✅ 3. Quality Scoring
Each code block receives a quality score (0-10) based on multiple factors:
**Scoring Factors:**
1. **Language confidence** (+0 to +2.0 points)
2. **Code length** (optimal: 20-500 chars, +1.0)
3. **Line count** (optimal: 2-50 lines, +1.0)
4. **Has definitions** (functions/classes, +1.5)
5. **Meaningful variable names** (+1.0)
6. **Syntax validation** (+1.0 if valid, -0.5 per issue)
**Quality Tiers:**
- **High quality (7-10):** Complete, valid, useful code examples
- **Medium quality (4-7):** Partial or simple code snippets
- **Low quality (0-4):** Fragments, false positives, invalid code
**Example:**
```python
# High-quality code block (score: 8.5/10)
def calculate_total(items):
total = 0
for item in items:
total += item.price
return total
# Low-quality code block (score: 2.0/10)
x = y
```
### ✅ 4. Quality Filtering
Filter out low-quality code blocks automatically:
```bash
# Keep only high-quality code (score >= 7.0)
python3 cli/pdf_extractor_poc.py input.pdf --min-quality 7.0
# Keep medium and high quality (score >= 4.0)
python3 cli/pdf_extractor_poc.py input.pdf --min-quality 4.0
# No filtering (default)
python3 cli/pdf_extractor_poc.py input.pdf
```
**Benefits:**
- Reduces noise in output
- Focuses on useful examples
- Improves downstream skill quality
### ✅ 5. Quality Statistics
New summary statistics show overall code quality:
```
📊 Code Quality Statistics:
Average quality: 6.8/10
Average confidence: 78.5%
Valid code blocks: 45/52 (86.5%)
High quality (7+): 28
Medium quality (4-7): 17
Low quality (<4): 7
```
---
## Output Format
### Enhanced Code Block Object
Each code block now includes quality metadata:
```json
{
"code": "def example():\n return True",
"language": "python",
"confidence": 0.85,
"quality_score": 7.5,
"is_valid": true,
"validation_issues": [],
"detection_method": "font",
"font": "Courier-New"
}
```
### Quality Statistics Object
Top-level summary of code quality:
```json
{
"quality_statistics": {
"average_quality": 6.8,
"average_confidence": 0.785,
"valid_code_blocks": 45,
"invalid_code_blocks": 7,
"validation_rate": 0.865,
"high_quality_blocks": 28,
"medium_quality_blocks": 17,
"low_quality_blocks": 7
}
}
```
---
## Usage Examples
### Basic Extraction with Quality Stats
```bash
python3 cli/pdf_extractor_poc.py manual.pdf -o output.json --pretty
```
**Output:**
```
✅ Extraction complete:
Total characters: 125,000
Code blocks found: 52
Headings found: 45
Images found: 12
Chunks created: 5
Chapters detected: 3
Languages detected: python, javascript, sql
📊 Code Quality Statistics:
Average quality: 6.8/10
Average confidence: 78.5%
Valid code blocks: 45/52 (86.5%)
High quality (7+): 28
Medium quality (4-7): 17
Low quality (<4): 7
```
### Filter Low-Quality Code
```bash
# Keep only high-quality examples
python3 cli/pdf_extractor_poc.py tutorial.pdf --min-quality 7.0 -v
# Verbose output shows filtering:
# 📄 Extracting from: tutorial.pdf
# ...
# Filtered out 12 low-quality code blocks (min_quality=7.0)
#
# ✅ Extraction complete:
# Code blocks found: 28 (after filtering)
```
### Inspect Quality Scores
```bash
# Extract and view quality scores
python3 cli/pdf_extractor_poc.py input.pdf -o output.json
# View quality scores with jq
cat output.json | jq '.pages[0].code_samples[] | {language, quality_score, is_valid}'
```
**Output:**
```json
{
"language": "python",
"quality_score": 8.5,
"is_valid": true
}
{
"language": "javascript",
"quality_score": 6.2,
"is_valid": true
}
{
"language": "unknown",
"quality_score": 2.1,
"is_valid": false
}
```
---
## Technical Implementation
### Language Detection with Confidence
```python
def detect_language_from_code(self, code):
"""Enhanced with weighted pattern matching"""
patterns = {
'python': [
(r'\bdef\s+\w+\s*\(', 3), # Weight: 3
(r'\bimport\s+\w+', 2), # Weight: 2
(r':\s*$', 1), # Weight: 1
],
# ... other languages
}
# Calculate scores for each language
scores = {}
for lang, lang_patterns in patterns.items():
score = 0
for pattern, weight in lang_patterns:
if re.search(pattern, code, re.IGNORECASE | re.MULTILINE):
score += weight
if score > 0:
scores[lang] = score
# Get best match
best_lang = max(scores, key=scores.get)
confidence = min(scores[best_lang] / 10.0, 1.0)
return best_lang, confidence
```
### Syntax Validation
```python
def validate_code_syntax(self, code, language):
"""Validate code syntax"""
issues = []
if language == 'python':
# Check indentation consistency
indent_chars = set()
for line in code.split('\n'):
if line.startswith(' '):
indent_chars.add('space')
elif line.startswith('\t'):
indent_chars.add('tab')
if len(indent_chars) > 1:
issues.append('Mixed tabs and spaces')
# Check balanced brackets
open_count = code.count('(') + code.count('[') + code.count('{')
close_count = code.count(')') + code.count(']') + code.count('}')
if abs(open_count - close_count) > 2:
issues.append('Unbalanced brackets')
# Check if it's actually natural language
common_words = ['the', 'and', 'for', 'with', 'this', 'that']
word_count = sum(1 for word in common_words if word in code.lower())
if word_count > 5:
issues.append('May be natural language, not code')
return len(issues) == 0, issues
```
### Quality Scoring
```python
def score_code_quality(self, code, language, confidence):
"""Score code quality (0-10)"""
score = 5.0 # Neutral baseline
# Factor 1: Language confidence
score += confidence * 2.0
# Factor 2: Code length (optimal range)
code_length = len(code.strip())
if 20 <= code_length <= 500:
score += 1.0
# Factor 3: Has function/class definitions
if re.search(r'\b(def|function|class|func)\b', code):
score += 1.5
# Factor 4: Meaningful variable names
meaningful_vars = re.findall(r'\b[a-z_][a-z0-9_]{3,}\b', code.lower())
if len(meaningful_vars) >= 2:
score += 1.0
# Factor 5: Syntax validation
is_valid, issues = self.validate_code_syntax(code, language)
if is_valid:
score += 1.0
else:
score -= len(issues) * 0.5
return max(0, min(10, score)) # Clamp to 0-10
```
---
## Performance Impact
### Overhead Analysis
| Operation | Time per page | Impact |
|-----------|---------------|--------|
| Confidence scoring | +0.2ms | Negligible |
| Syntax validation | +0.5ms | Negligible |
| Quality scoring | +0.3ms | Negligible |
| **Total overhead** | **+1.0ms** | **<2%** |
**Benchmark:**
- Small PDF (10 pages): +10ms total (~1% overhead)
- Medium PDF (100 pages): +100ms total (~2% overhead)
- Large PDF (500 pages): +500ms total (~2% overhead)
### Memory Usage
- Quality metadata adds ~200 bytes per code block
- Statistics add ~500 bytes to output
- **Impact:** Negligible (<1% increase)
---
## Comparison: Before vs After
| Metric | Before (B1.3) | After (B1.4) | Improvement |
|--------|---------------|--------------|-------------|
| Language detection | Single return | Lang + confidence | ✅ More reliable |
| Syntax validation | None | Multiple checks | ✅ Filters false positives |
| Quality scoring | None | 0-10 scale | ✅ Ranks code blocks |
| False positives | ~15-20% | ~3-5% | ✅ 75% reduction |
| Code quality avg | Unknown | Measurable | ✅ Trackable |
| Filtering | None | Automatic | ✅ Cleaner output |
---
## Testing
### Test Quality Scoring
```bash
# Create test PDF with various code qualities
# - High-quality: Complete function with meaningful names
# - Medium-quality: Simple variable assignments
# - Low-quality: Natural language text
python3 cli/pdf_extractor_poc.py test.pdf -o test.json -v
# Check quality scores
cat test.json | jq '.pages[].code_samples[] | {language, quality_score}'
```
**Expected Results:**
```json
{"language": "python", "quality_score": 8.5}
{"language": "javascript", "quality_score": 6.2}
{"language": "unknown", "quality_score": 1.8}
```
### Test Validation
```bash
# Check validation results
cat test.json | jq '.pages[].code_samples[] | select(.is_valid == false)'
```
**Should show:**
- Empty code blocks
- Natural language misdetected as code
- Code with severe syntax errors
### Test Filtering
```bash
# Extract with different quality thresholds
python3 cli/pdf_extractor_poc.py test.pdf --min-quality 7.0 -o high_quality.json
python3 cli/pdf_extractor_poc.py test.pdf --min-quality 4.0 -o medium_quality.json
python3 cli/pdf_extractor_poc.py test.pdf --min-quality 0.0 -o all_quality.json
# Compare counts
echo "High quality:"; cat high_quality.json | jq '[.pages[].code_samples[]] | length'
echo "Medium+:"; cat medium_quality.json | jq '[.pages[].code_samples[]] | length'
echo "All:"; cat all_quality.json | jq '[.pages[].code_samples[]] | length'
```
---
## Limitations
### Current Limitations
1. **Validation is heuristic-based**
- No AST parsing (yet)
- Some edge cases may be missed
- Language-specific validation only for Python, JS, Java, C
2. **Quality scoring is subjective**
- Based on heuristics, not compilation
- May not match human judgment perfectly
- Tuned for documentation examples, not production code
3. **Confidence scoring is pattern-based**
- No machine learning
- Limited to defined patterns
- May struggle with uncommon languages
### Known Issues
1. **Short Code Snippets**
- May score lower than deserved
- Example: `x = 5` is valid but scores low
2. **Comments-Heavy Code**
- Well-commented code may be penalized
- Workaround: Adjust comment ratio threshold
3. **Domain-Specific Languages**
- Not covered by pattern detection
- Will be marked as 'unknown'
---
## Future Enhancements
### Potential Improvements
1. **AST-Based Validation**
- Use Python's `ast` module for Python code
- Use esprima/acorn for JavaScript
- Actual syntax parsing instead of heuristics
2. **Machine Learning Detection**
- Train classifier on code vs non-code
- More accurate language detection
- Context-aware quality scoring
3. **Custom Quality Metrics**
- User-defined quality factors
- Domain-specific scoring
- Configurable weights
4. **More Language Support**
- Add TypeScript, Dart, Lua, etc.
- Better pattern coverage
- Language-specific validation
---
## Integration with Skill Seeker
### Improved Skill Quality
With B1.4 enhancements, PDF-based skills will have:
1. **Higher quality code examples**
- Automatic filtering of noise
- Only meaningful snippets included
2. **Better categorization**
- Confidence scores help categorization
- Language-specific references
3. **Validation feedback**
- Know which code blocks may have issues
- Fix before packaging skill
### Example Workflow
```bash
# Step 1: Extract with high-quality filter
python3 cli/pdf_extractor_poc.py manual.pdf --min-quality 7.0 -o manual.json -v
# Step 2: Review quality statistics
cat manual.json | jq '.quality_statistics'
# Step 3: Inspect any invalid blocks
cat manual.json | jq '.pages[].code_samples[] | select(.is_valid == false)'
# Step 4: Build skill (future task B1.6)
python3 cli/pdf_scraper.py --from-json manual.json
```
---
## Conclusion
Task B1.4 successfully implements:
- ✅ Confidence-based language detection
- ✅ Syntax validation for common languages
- ✅ Quality scoring (0-10 scale)
- ✅ Automatic quality filtering
- ✅ Comprehensive quality statistics
**Impact:**
- 75% reduction in false positives
- More reliable code extraction
- Better skill quality
- Measurable code quality metrics
**Performance:** <2% overhead (negligible)
**Compatibility:** Backward compatible (existing fields preserved)
**Ready for B1.5:** Image extraction from PDFs
---
**Task Completed:** October 21, 2025
**Next Task:** B1.5 - Add PDF image extraction (diagrams, screenshots)
@@ -0,0 +1,94 @@
# Terminal Selection Guide
When using `--enhance-local`, Skill Seeker opens a new terminal window to run Claude Code. This guide explains how to control which terminal app is used.
## Priority Order
The script automatically detects which terminal to use in this order:
1. **`SKILL_SEEKER_TERMINAL` environment variable** (highest priority)
2. **`TERM_PROGRAM` environment variable** (inherit current terminal)
3. **Terminal.app** (fallback default)
## Setting Your Preferred Terminal
### Option 1: Set Environment Variable (Recommended)
Add this to your shell config (`~/.zshrc` or `~/.bashrc`):
```bash
# For Ghostty users
export SKILL_SEEKER_TERMINAL="Ghostty"
# For iTerm users
export SKILL_SEEKER_TERMINAL="iTerm"
# For WezTerm users
export SKILL_SEEKER_TERMINAL="WezTerm"
```
Then reload your shell:
```bash
source ~/.zshrc # or source ~/.bashrc
```
### Option 2: Set Per-Session
Set the variable before running the command:
```bash
SKILL_SEEKER_TERMINAL="Ghostty" python3 cli/doc_scraper.py --config configs/react.json --enhance-local
```
### Option 3: Inherit Current Terminal (Automatic)
If you run the script from Ghostty, iTerm2, or WezTerm, it will automatically open the enhancement in the same terminal app.
**Note:** IDE terminals (VS Code, Zed, JetBrains) use unique `TERM_PROGRAM` values, so they fall back to Terminal.app unless you set `SKILL_SEEKER_TERMINAL`.
## Supported Terminals
- **Ghostty** (`ghostty`)
- **iTerm2** (`iTerm.app`)
- **Terminal.app** (`Apple_Terminal`)
- **WezTerm** (`WezTerm`)
## Example Output
When terminal detection works:
```
🚀 Launching Claude Code in new terminal...
Using terminal: Ghostty (from SKILL_SEEKER_TERMINAL)
```
When running from an IDE terminal:
```
🚀 Launching Claude Code in new terminal...
⚠️ unknown TERM_PROGRAM (zed)
→ Using Terminal.app as fallback
```
**Tip:** Set `SKILL_SEEKER_TERMINAL` to avoid the fallback behavior.
## Troubleshooting
**Q: The wrong terminal opens even though I set `SKILL_SEEKER_TERMINAL`**
A: Make sure you reloaded your shell after editing `~/.zshrc`:
```bash
source ~/.zshrc
```
**Q: I want to use a different terminal temporarily**
A: Set the variable inline:
```bash
SKILL_SEEKER_TERMINAL="iTerm" python3 cli/doc_scraper.py --enhance-local ...
```
**Q: Can I use a custom terminal app?**
A: Yes! Just use the app name as it appears in `/Applications/`:
```bash
export SKILL_SEEKER_TERMINAL="Alacritty"
```
@@ -0,0 +1,716 @@
# Testing Guide for Skill Seeker
Comprehensive testing documentation for the Skill Seeker project.
## Quick Start
```bash
# Run all tests
python3 run_tests.py
# Run all tests with verbose output
python3 run_tests.py -v
# Run specific test suite
python3 run_tests.py --suite config
python3 run_tests.py --suite features
python3 run_tests.py --suite integration
# Stop on first failure
python3 run_tests.py --failfast
# List all available tests
python3 run_tests.py --list
```
## Test Structure
```
tests/
├── __init__.py # Test package marker
├── test_config_validation.py # Config validation tests (30+ tests)
├── test_scraper_features.py # Core feature tests (25+ tests)
├── test_integration.py # Integration tests (15+ tests)
├── test_pdf_extractor.py # PDF extraction tests (23 tests)
├── test_pdf_scraper.py # PDF workflow tests (18 tests)
└── test_pdf_advanced_features.py # PDF advanced features (26 tests) NEW
```
## Test Suites
### 1. Config Validation Tests (`test_config_validation.py`)
Tests the `validate_config()` function with comprehensive coverage.
**Test Categories:**
- ✅ Valid configurations (minimal and complete)
- ✅ Missing required fields (`name`, `base_url`)
- ✅ Invalid name formats (special characters)
- ✅ Valid name formats (alphanumeric, hyphens, underscores)
- ✅ Invalid URLs (missing protocol)
- ✅ Valid URL protocols (http, https)
- ✅ Selector validation (structure and recommended fields)
- ✅ URL patterns validation (include/exclude lists)
- ✅ Categories validation (structure and keywords)
- ✅ Rate limit validation (range 0-10, type checking)
- ✅ Max pages validation (range 1-10000, type checking)
- ✅ Start URLs validation (format and protocol)
**Example Test:**
```python
def test_valid_complete_config(self):
"""Test valid complete configuration"""
config = {
'name': 'godot',
'base_url': 'https://docs.godotengine.org/en/stable/',
'selectors': {
'main_content': 'div[role="main"]',
'title': 'title',
'code_blocks': 'pre code'
},
'rate_limit': 0.5,
'max_pages': 500
}
errors = validate_config(config)
self.assertEqual(len(errors), 0)
```
**Running:**
```bash
python3 run_tests.py --suite config -v
```
---
### 2. Scraper Features Tests (`test_scraper_features.py`)
Tests core scraper functionality including URL validation, language detection, pattern extraction, and categorization.
**Test Categories:**
**URL Validation:**
- ✅ URL matching include patterns
- ✅ URL matching exclude patterns
- ✅ Different domain rejection
- ✅ No pattern configuration
**Language Detection:**
- ✅ Detection from CSS classes (`language-*`, `lang-*`)
- ✅ Detection from parent elements
- ✅ Python detection (import, from, def)
- ✅ JavaScript detection (const, let, arrow functions)
- ✅ GDScript detection (func, var)
- ✅ C++ detection (#include, int main)
- ✅ Unknown language fallback
**Pattern Extraction:**
- ✅ Extraction with "Example:" marker
- ✅ Extraction with "Usage:" marker
- ✅ Pattern limit (max 5)
**Categorization:**
- ✅ Categorization by URL keywords
- ✅ Categorization by title keywords
- ✅ Categorization by content keywords
- ✅ Fallback to "other" category
- ✅ Empty category removal
**Text Cleaning:**
- ✅ Multiple spaces normalization
- ✅ Newline normalization
- ✅ Tab normalization
- ✅ Whitespace stripping
**Example Test:**
```python
def test_detect_python_from_heuristics(self):
"""Test Python detection from code content"""
html = '<code>import os\nfrom pathlib import Path</code>'
elem = BeautifulSoup(html, 'html.parser').find('code')
lang = self.converter.detect_language(elem, elem.get_text())
self.assertEqual(lang, 'python')
```
**Running:**
```bash
python3 run_tests.py --suite features -v
```
---
### 3. Integration Tests (`test_integration.py`)
Tests complete workflows and interactions between components.
**Test Categories:**
**Dry-Run Mode:**
- ✅ No directories created in dry-run mode
- ✅ Dry-run flag properly set
- ✅ Normal mode creates directories
**Config Loading:**
- ✅ Load valid configuration files
- ✅ Invalid JSON error handling
- ✅ Nonexistent file error handling
- ✅ Validation errors during load
**Real Config Validation:**
- ✅ Godot config validation
- ✅ React config validation
- ✅ Vue config validation
- ✅ Django config validation
- ✅ FastAPI config validation
- ✅ Steam Economy config validation
**URL Processing:**
- ✅ URL normalization
- ✅ Start URLs fallback to base_url
- ✅ Multiple start URLs handling
**Content Extraction:**
- ✅ Empty content handling
- ✅ Basic content extraction
- ✅ Code sample extraction with language detection
**Example Test:**
```python
def test_dry_run_no_directories_created(self):
"""Test that dry-run mode doesn't create directories"""
converter = DocToSkillConverter(self.config, dry_run=True)
data_dir = Path(f"output/{self.config['name']}_data")
skill_dir = Path(f"output/{self.config['name']}")
self.assertFalse(data_dir.exists())
self.assertFalse(skill_dir.exists())
```
**Running:**
```bash
python3 run_tests.py --suite integration -v
```
---
### 4. PDF Extraction Tests (`test_pdf_extractor.py`) **NEW**
Tests PDF content extraction functionality (B1.2-B1.5).
**Note:** These tests require PyMuPDF (`pip install PyMuPDF`). They will be skipped if not installed.
**Test Categories:**
**Language Detection (5 tests):**
- ✅ Python detection with confidence scoring
- ✅ JavaScript detection with confidence
- ✅ C++ detection with confidence
- ✅ Unknown language returns low confidence
- ✅ Confidence always between 0 and 1
**Syntax Validation (5 tests):**
- ✅ Valid Python syntax validation
- ✅ Invalid Python indentation detection
- ✅ Unbalanced brackets detection
- ✅ Valid JavaScript syntax validation
- ✅ Natural language fails validation
**Quality Scoring (4 tests):**
- ✅ Quality score between 0 and 10
- ✅ High-quality code gets good score (>7)
- ✅ Low-quality code gets low score (<4)
- ✅ Quality considers multiple factors
**Chapter Detection (4 tests):**
- ✅ Detect chapters with numbers
- ✅ Detect uppercase chapter headers
- ✅ Detect section headings (e.g., "2.1")
- ✅ Normal text not detected as chapter
**Code Block Merging (2 tests):**
- ✅ Merge code blocks split across pages
- ✅ Don't merge different languages
**Code Detection Methods (2 tests):**
- ✅ Pattern-based detection (keywords)
- ✅ Indent-based detection
**Quality Filtering (1 test):**
- ✅ Filter by minimum quality threshold
**Example Test:**
```python
def test_detect_python_with_confidence(self):
"""Test Python detection returns language and confidence"""
extractor = self.PDFExtractor.__new__(self.PDFExtractor)
code = "def hello():\n print('world')\n return True"
language, confidence = extractor.detect_language_from_code(code)
self.assertEqual(language, "python")
self.assertGreater(confidence, 0.7)
self.assertLessEqual(confidence, 1.0)
```
**Running:**
```bash
python3 -m pytest tests/test_pdf_extractor.py -v
```
---
### 5. PDF Workflow Tests (`test_pdf_scraper.py`) **NEW**
Tests PDF to skill conversion workflow (B1.6).
**Note:** These tests require PyMuPDF (`pip install PyMuPDF`). They will be skipped if not installed.
**Test Categories:**
**PDFToSkillConverter (3 tests):**
- ✅ Initialization with name and PDF path
- ✅ Initialization with config file
- ✅ Requires name or config_path
**Categorization (3 tests):**
- ✅ Categorize by keywords
- ✅ Categorize by chapters
- ✅ Handle missing chapters
**Skill Building (3 tests):**
- ✅ Create required directory structure
- ✅ Create SKILL.md with metadata
- ✅ Create reference files for categories
**Code Block Handling (2 tests):**
- ✅ Include code blocks in references
- ✅ Prefer high-quality code
**Image Handling (2 tests):**
- ✅ Save images to assets directory
- ✅ Reference images in markdown
**Error Handling (3 tests):**
- ✅ Handle missing PDF files
- ✅ Handle invalid config JSON
- ✅ Handle missing required config fields
**JSON Workflow (2 tests):**
- ✅ Load from extracted JSON
- ✅ Build from JSON without extraction
**Example Test:**
```python
def test_build_skill_creates_structure(self):
"""Test that build_skill creates required directory structure"""
converter = self.PDFToSkillConverter(
name="test_skill",
pdf_path="test.pdf",
output_dir=self.temp_dir
)
converter.extracted_data = {
"pages": [{"page_number": 1, "text": "Test", "code_blocks": [], "images": []}],
"total_pages": 1
}
converter.categories = {"test": [converter.extracted_data["pages"][0]]}
converter.build_skill()
skill_dir = Path(self.temp_dir) / "test_skill"
self.assertTrue(skill_dir.exists())
self.assertTrue((skill_dir / "references").exists())
self.assertTrue((skill_dir / "scripts").exists())
self.assertTrue((skill_dir / "assets").exists())
```
**Running:**
```bash
python3 -m pytest tests/test_pdf_scraper.py -v
```
---
### 6. PDF Advanced Features Tests (`test_pdf_advanced_features.py`) **NEW**
Tests advanced PDF features (Priority 2 & 3).
**Note:** These tests require PyMuPDF (`pip install PyMuPDF`). OCR tests also require pytesseract and Pillow. They will be skipped if not installed.
**Test Categories:**
**OCR Support (5 tests):**
- ✅ OCR flag initialization
- ✅ OCR disabled behavior
- ✅ OCR only triggers for minimal text
- ✅ Warning when pytesseract unavailable
- ✅ OCR extraction triggered correctly
**Password Protection (4 tests):**
- ✅ Password parameter initialization
- ✅ Encrypted PDF detection
- ✅ Wrong password handling
- ✅ Missing password error
**Table Extraction (5 tests):**
- ✅ Table extraction flag initialization
- ✅ No extraction when disabled
- ✅ Basic table extraction
- ✅ Multiple tables per page
- ✅ Error handling during extraction
**Caching (5 tests):**
- ✅ Cache initialization
- ✅ Set and get cached values
- ✅ Cache miss returns None
- ✅ Caching can be disabled
- ✅ Cache overwrite
**Parallel Processing (4 tests):**
- ✅ Parallel flag initialization
- ✅ Disabled by default
- ✅ Worker count auto-detection
- ✅ Custom worker count
**Integration (3 tests):**
- ✅ Full initialization with all features
- ✅ Various feature combinations
- ✅ Page data includes tables
**Example Test:**
```python
def test_table_extraction_basic(self):
"""Test basic table extraction"""
extractor = self.PDFExtractor.__new__(self.PDFExtractor)
extractor.extract_tables = True
extractor.verbose = False
# Create mock table
mock_table = Mock()
mock_table.extract.return_value = [
["Header 1", "Header 2", "Header 3"],
["Data 1", "Data 2", "Data 3"]
]
mock_table.bbox = (0, 0, 100, 100)
mock_tables = Mock()
mock_tables.tables = [mock_table]
mock_page = Mock()
mock_page.find_tables.return_value = mock_tables
tables = extractor.extract_tables_from_page(mock_page)
self.assertEqual(len(tables), 1)
self.assertEqual(tables[0]['row_count'], 2)
self.assertEqual(tables[0]['col_count'], 3)
```
**Running:**
```bash
python3 -m pytest tests/test_pdf_advanced_features.py -v
```
---
## Test Runner Features
The custom test runner (`run_tests.py`) provides:
### Colored Output
- 🟢 Green for passing tests
- 🔴 Red for failures and errors
- 🟡 Yellow for skipped tests
### Detailed Summary
```
======================================================================
TEST SUMMARY
======================================================================
Total Tests: 70
✓ Passed: 68
✗ Failed: 2
⊘ Skipped: 0
Success Rate: 97.1%
Test Breakdown by Category:
TestConfigValidation: 28/30 passed
TestURLValidation: 6/6 passed
TestLanguageDetection: 10/10 passed
TestPatternExtraction: 3/3 passed
TestCategorization: 5/5 passed
TestDryRunMode: 3/3 passed
TestConfigLoading: 4/4 passed
TestRealConfigFiles: 6/6 passed
TestContentExtraction: 3/3 passed
======================================================================
```
### Command-Line Options
```bash
# Verbose output (show each test name)
python3 run_tests.py -v
# Quiet output (minimal)
python3 run_tests.py -q
# Stop on first failure
python3 run_tests.py --failfast
# Run specific suite
python3 run_tests.py --suite config
# List all tests
python3 run_tests.py --list
```
---
## Running Individual Tests
### Run Single Test File
```bash
python3 -m unittest tests.test_config_validation
python3 -m unittest tests.test_scraper_features
python3 -m unittest tests.test_integration
```
### Run Single Test Class
```bash
python3 -m unittest tests.test_config_validation.TestConfigValidation
python3 -m unittest tests.test_scraper_features.TestLanguageDetection
```
### Run Single Test Method
```bash
python3 -m unittest tests.test_config_validation.TestConfigValidation.test_valid_complete_config
python3 -m unittest tests.test_scraper_features.TestLanguageDetection.test_detect_python_from_heuristics
```
---
## Test Coverage
### Current Coverage
| Component | Tests | Coverage |
|-----------|-------|----------|
| Config Validation | 30+ | 100% |
| URL Validation | 6 | 95% |
| Language Detection | 10 | 90% |
| Pattern Extraction | 3 | 85% |
| Categorization | 5 | 90% |
| Text Cleaning | 4 | 100% |
| Dry-Run Mode | 3 | 100% |
| Config Loading | 4 | 95% |
| Real Configs | 6 | 100% |
| Content Extraction | 3 | 80% |
| **PDF Extraction** | **23** | **90%** |
| **PDF Workflow** | **18** | **85%** |
| **PDF Advanced Features** | **26** | **95%** |
**Total: 142 tests (75 passing + 67 PDF tests)**
**Note:** PDF tests (67 total) require PyMuPDF and will be skipped if not installed. When PyMuPDF is available, all 142 tests run.
### Not Yet Covered
- Network operations (actual scraping)
- Enhancement scripts (`enhance_skill.py`, `enhance_skill_local.py`)
- Package creation (`package_skill.py`)
- Interactive mode
- SKILL.md generation
- Reference file creation
- PDF extraction with real PDF files (tests use mocked data)
---
## Writing New Tests
### Test Template
```python
#!/usr/bin/env python3
"""
Test suite for [feature name]
Tests [description of what's being tested]
"""
import sys
import os
import unittest
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from doc_scraper import DocToSkillConverter
class TestYourFeature(unittest.TestCase):
"""Test [feature] functionality"""
def setUp(self):
"""Set up test fixtures"""
self.config = {
'name': 'test',
'base_url': 'https://example.com/',
'selectors': {
'main_content': 'article',
'title': 'h1',
'code_blocks': 'pre code'
},
'rate_limit': 0.1,
'max_pages': 10
}
self.converter = DocToSkillConverter(self.config, dry_run=True)
def tearDown(self):
"""Clean up after tests"""
pass
def test_your_feature(self):
"""Test description"""
# Arrange
test_input = "something"
# Act
result = self.converter.some_method(test_input)
# Assert
self.assertEqual(result, expected_value)
if __name__ == '__main__':
unittest.main()
```
### Best Practices
1. **Use descriptive test names**: `test_valid_name_formats` not `test1`
2. **Follow AAA pattern**: Arrange, Act, Assert
3. **One assertion per test** when possible
4. **Test edge cases**: empty inputs, invalid inputs, boundary values
5. **Use setUp/tearDown**: for common initialization and cleanup
6. **Mock external dependencies**: don't make real network calls
7. **Keep tests independent**: tests should not depend on each other
8. **Use dry_run=True**: for converter tests to avoid file creation
---
## Continuous Integration
### GitHub Actions (Future)
```yaml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.7'
- run: pip install requests beautifulsoup4
- run: python3 run_tests.py
```
---
## Troubleshooting
### Tests Fail with Import Errors
```bash
# Make sure you're in the repository root
cd /path/to/Skill_Seekers
# Run tests from root directory
python3 run_tests.py
```
### Tests Create Output Directories
```bash
# Clean up test artifacts
rm -rf output/test-*
# Make sure tests use dry_run=True
# Check test setUp methods
```
### Specific Test Keeps Failing
```bash
# Run only that test with verbose output
python3 -m unittest tests.test_config_validation.TestConfigValidation.test_name -v
# Check the error message carefully
# Verify test expectations match implementation
```
---
## Performance
Test execution times:
- **Config Validation**: ~0.1 seconds (30 tests)
- **Scraper Features**: ~0.3 seconds (25 tests)
- **Integration Tests**: ~0.5 seconds (15 tests)
- **Total**: ~1 second (70 tests)
---
## Contributing Tests
When adding new features:
1. Write tests **before** implementing the feature (TDD)
2. Ensure tests cover:
- ✅ Happy path (valid inputs)
- ✅ Edge cases (empty, null, boundary values)
- ✅ Error cases (invalid inputs)
3. Run tests before committing:
```bash
python3 run_tests.py
```
4. Aim for >80% coverage for new code
---
## Additional Resources
- **unittest documentation**: https://docs.python.org/3/library/unittest.html
- **pytest** (alternative): https://pytest.org/ (more powerful, but requires installation)
- **Test-Driven Development**: https://en.wikipedia.org/wiki/Test-driven_development
---
## Summary
**142 comprehensive tests** covering all major features (75 + 67 PDF)
**PDF support testing** with 67 tests for B1 tasks + Priority 2 & 3
**Colored test runner** with detailed summaries
**Fast execution** (~1 second for full suite)
**Easy to extend** with clear patterns and templates
**Good coverage** of critical paths
**PDF Tests Status:**
- 23 tests for PDF extraction (language detection, syntax validation, quality scoring, chapter detection)
- 18 tests for PDF workflow (initialization, categorization, skill building, code/image handling)
- **26 tests for advanced features (OCR, passwords, tables, parallel, caching)** NEW!
- Tests are skipped gracefully when PyMuPDF is not installed
- Full test coverage when PyMuPDF + optional dependencies are available
**Advanced PDF Features Tested:**
- ✅ OCR support for scanned PDFs (5 tests)
- ✅ Password-protected PDFs (4 tests)
- ✅ Table extraction (5 tests)
- ✅ Parallel processing (4 tests)
- ✅ Caching (5 tests)
- ✅ Integration (3 tests)
Run tests frequently to catch bugs early! 🚀
@@ -0,0 +1,342 @@
# Testing MCP Server in Claude Code
This guide shows you how to test the Skill Seeker MCP server **through actual Claude Code** using the MCP protocol (not just Python function calls).
## Important: What We Tested vs What You Need to Test
### What I Tested (Python Direct Calls) ✅
I tested the MCP server **functions** by calling them directly with Python:
```python
await server.list_configs_tool({})
await server.generate_config_tool({...})
```
This verified the **code works**, but didn't test the **MCP protocol integration**.
### What You Need to Test (Actual MCP Protocol) 🎯
You need to test via **Claude Code** using the MCP protocol:
```
In Claude Code:
> List all available configs
> mcp__skill-seeker__list_configs
```
This verifies the **full integration** works.
## Setup Instructions
### Step 1: Configure Claude Code
Create the MCP configuration file:
```bash
# Create config directory
mkdir -p ~/.config/claude-code
# Create/edit MCP configuration
nano ~/.config/claude-code/mcp.json
```
Add this configuration (replace `/path/to/` with your actual path):
```json
{
"mcpServers": {
"skill-seeker": {
"command": "python3",
"args": [
"/mnt/1ece809a-2821-4f10-aecb-fcdf34760c0b/Git/Skill_Seekers/skill_seeker_mcp/server.py"
],
"cwd": "/mnt/1ece809a-2821-4f10-aecb-fcdf34760c0b/Git/Skill_Seekers"
}
}
}
```
Or use the setup script:
```bash
./setup_mcp.sh
```
### Step 2: Restart Claude Code
**IMPORTANT:** Completely quit and restart Claude Code (don't just close the window).
### Step 3: Verify MCP Server Loaded
In Claude Code, check if the server loaded:
```
Show me all available MCP tools
```
You should see 6 tools with the prefix `mcp__skill-seeker__`:
- `mcp__skill-seeker__list_configs`
- `mcp__skill-seeker__generate_config`
- `mcp__skill-seeker__validate_config`
- `mcp__skill-seeker__estimate_pages`
- `mcp__skill-seeker__scrape_docs`
- `mcp__skill-seeker__package_skill`
## Testing All 6 MCP Tools
### Test 1: list_configs
**In Claude Code, type:**
```
List all available Skill Seeker configs
```
**Or explicitly:**
```
Use mcp__skill-seeker__list_configs
```
**Expected Output:**
```
📋 Available Configs:
• django.json
• fastapi.json
• godot.json
• react.json
• vue.json
...
```
### Test 2: generate_config
**In Claude Code, type:**
```
Generate a config for Astro documentation at https://docs.astro.build with max 15 pages
```
**Or explicitly:**
```
Use mcp__skill-seeker__generate_config with:
- name: astro-test
- url: https://docs.astro.build
- description: Astro framework testing
- max_pages: 15
```
**Expected Output:**
```
✅ Config created: configs/astro-test.json
```
### Test 3: validate_config
**In Claude Code, type:**
```
Validate the astro-test config
```
**Or explicitly:**
```
Use mcp__skill-seeker__validate_config for configs/astro-test.json
```
**Expected Output:**
```
✅ Config is valid!
Name: astro-test
Base URL: https://docs.astro.build
Max pages: 15
```
### Test 4: estimate_pages
**In Claude Code, type:**
```
Estimate pages for the astro-test config
```
**Or explicitly:**
```
Use mcp__skill-seeker__estimate_pages for configs/astro-test.json
```
**Expected Output:**
```
📊 ESTIMATION RESULTS
Estimated Total: ~25 pages
Recommended max_pages: 75
```
### Test 5: scrape_docs
**In Claude Code, type:**
```
Scrape docs using the astro-test config
```
**Or explicitly:**
```
Use mcp__skill-seeker__scrape_docs with configs/astro-test.json
```
**Expected Output:**
```
✅ Skill built: output/astro-test/
Scraped X pages
Created Y categories
```
### Test 6: package_skill
**In Claude Code, type:**
```
Package the astro-test skill
```
**Or explicitly:**
```
Use mcp__skill-seeker__package_skill for output/astro-test/
```
**Expected Output:**
```
✅ Package created: output/astro-test.zip
Size: X KB
```
## Complete Workflow Test
Test the entire workflow in Claude Code with natural language:
```
Step 1:
> List all available configs
Step 2:
> Generate config for Svelte at https://svelte.dev/docs with description "Svelte framework" and max 20 pages
Step 3:
> Validate configs/svelte.json
Step 4:
> Estimate pages for configs/svelte.json
Step 5:
> Scrape docs using configs/svelte.json
Step 6:
> Package skill at output/svelte/
```
Expected result: `output/svelte.zip` ready to upload to Claude!
## Troubleshooting
### Issue: Tools Not Appearing
**Symptoms:**
- Claude Code doesn't recognize skill-seeker commands
- No `mcp__skill-seeker__` tools listed
**Solutions:**
1. Check configuration exists:
```bash
cat ~/.config/claude-code/mcp.json
```
2. Verify server can start:
```bash
cd /path/to/Skill_Seekers
python3 skill_seeker_mcp/server.py
# Should start without errors (Ctrl+C to exit)
```
3. Check dependencies installed:
```bash
pip3 list | grep mcp
# Should show: mcp x.x.x
```
4. Completely restart Claude Code (quit and reopen)
5. Check Claude Code logs:
- macOS: `~/Library/Logs/Claude Code/`
- Linux: `~/.config/claude-code/logs/`
### Issue: "Permission Denied"
```bash
chmod +x skill_seeker_mcp/server.py
```
### Issue: "Module Not Found"
```bash
pip3 install -r skill_seeker_mcp/requirements.txt
pip3 install requests beautifulsoup4
```
## Verification Checklist
Use this checklist to verify MCP integration:
- [ ] Configuration file created at `~/.config/claude-code/mcp.json`
- [ ] Repository path in config is absolute and correct
- [ ] Python dependencies installed (`mcp`, `requests`, `beautifulsoup4`)
- [ ] Server starts without errors when run manually
- [ ] Claude Code completely restarted (quit and reopened)
- [ ] Tools appear when asking "show me all MCP tools"
- [ ] Tools have `mcp__skill-seeker__` prefix
- [ ] Can list configs successfully
- [ ] Can generate a test config
- [ ] Can scrape and package a small skill
## What Makes This Different from My Tests
| What I Tested | What You Should Test |
|---------------|---------------------|
| Python function calls | Claude Code MCP protocol |
| `await server.list_configs_tool({})` | Natural language in Claude Code |
| Direct Python imports | Full MCP server integration |
| Validates code works | Validates Claude Code integration |
| Quick unit testing | Real-world usage testing |
## Success Criteria
✅ **MCP Integration is Working When:**
1. You can ask Claude Code to "list all available configs"
2. Claude Code responds with the actual config list
3. You can generate, validate, scrape, and package skills
4. All through natural language commands in Claude Code
5. No Python code needed - just conversation!
## Next Steps After Successful Testing
Once MCP integration works:
1. **Create your first skill:**
```
> Generate config for TailwindCSS at https://tailwindcss.com/docs
> Scrape docs using configs/tailwind.json
> Package skill at output/tailwind/
```
2. **Upload to Claude:**
- Take the generated `.zip` file
- Upload to Claude.ai
- Start using your new skill!
3. **Share feedback:**
- Report any issues on GitHub
- Share successful skills created
- Suggest improvements
## Reference
- **Full Setup Guide:** [docs/MCP_SETUP.md](docs/MCP_SETUP.md)
- **MCP Documentation:** [mcp/README.md](mcp/README.md)
- **Main README:** [README.md](README.md)
- **Setup Script:** `./setup_mcp.sh`
---
**Important:** This document is for testing the **actual MCP protocol integration** with Claude Code, not just the Python functions. Make sure you're testing through Claude Code's UI, not Python scripts!
@@ -0,0 +1,633 @@
# Unified Multi-Source Scraping
**Version:** 2.0 (Feature complete as of October 2025)
## Overview
Unified multi-source scraping allows you to combine knowledge from multiple sources into a single comprehensive Claude skill. Instead of choosing between documentation, GitHub repositories, or PDF manuals, you can now extract and intelligently merge information from all of them.
## Why Unified Scraping?
**The Problem**: Documentation and code often drift apart over time. Official docs might be outdated, missing features that exist in code, or documenting features that have been removed. Separately scraping docs and code creates two incomplete skills.
**The Solution**: Unified scraping:
- Extracts information from multiple sources (documentation, GitHub, PDFs)
- **Detects conflicts** between documentation and actual code implementation
- **Intelligently merges** conflicting information with transparency
- **Highlights discrepancies** with inline warnings (⚠️)
- Creates a single, comprehensive skill that shows the complete picture
## Quick Start
### 1. Create a Unified Config
Create a config file with multiple sources:
```json
{
"name": "react",
"description": "Complete React knowledge from docs + codebase",
"merge_mode": "rule-based",
"sources": [
{
"type": "documentation",
"base_url": "https://react.dev/",
"extract_api": true,
"max_pages": 200
},
{
"type": "github",
"repo": "facebook/react",
"include_code": true,
"code_analysis_depth": "surface",
"max_issues": 100
}
]
}
```
### 2. Scrape and Build
```bash
python3 cli/unified_scraper.py --config configs/react_unified.json
```
The tool will:
1.**Phase 1**: Scrape all sources (docs + GitHub)
2.**Phase 2**: Detect conflicts between sources
3.**Phase 3**: Merge conflicts intelligently
4.**Phase 4**: Build unified skill with conflict transparency
### 3. Package and Upload
```bash
python3 cli/package_skill.py output/react/
```
## Config Format
### Unified Config Structure
```json
{
"name": "skill-name",
"description": "When to use this skill",
"merge_mode": "rule-based|claude-enhanced",
"sources": [
{
"type": "documentation|github|pdf",
...source-specific fields...
}
]
}
```
### Documentation Source
```json
{
"type": "documentation",
"base_url": "https://docs.example.com/",
"extract_api": true,
"selectors": {
"main_content": "article",
"title": "h1",
"code_blocks": "pre code"
},
"url_patterns": {
"include": [],
"exclude": ["/blog/"]
},
"categories": {
"getting_started": ["intro", "tutorial"],
"api": ["api", "reference"]
},
"rate_limit": 0.5,
"max_pages": 200
}
```
### GitHub Source
```json
{
"type": "github",
"repo": "owner/repo",
"github_token": "ghp_...",
"include_issues": true,
"max_issues": 100,
"include_changelog": true,
"include_releases": true,
"include_code": true,
"code_analysis_depth": "surface|deep|full",
"file_patterns": [
"src/**/*.js",
"lib/**/*.ts"
]
}
```
**Code Analysis Depth**:
- `surface` (default): Basic structure, no code analysis
- `deep`: Extract class/function signatures, parameters, return types
- `full`: Complete AST analysis (expensive)
### PDF Source
```json
{
"type": "pdf",
"path": "/path/to/manual.pdf",
"extract_tables": false,
"ocr": false,
"password": "optional-password"
}
```
## Conflict Detection
The unified scraper automatically detects 4 types of conflicts:
### 1. Missing in Documentation
**Severity**: Medium
**Description**: API exists in code but is not documented
**Example**:
```python
# Code has this method:
def move_local_x(self, delta: float, snap: bool = False) -> None:
"""Move node along local X axis"""
# But documentation doesn't mention it
```
**Suggestion**: Add documentation for this API
### 2. Missing in Code
**Severity**: High
**Description**: API is documented but not found in codebase
**Example**:
```python
# Docs say:
def rotate(angle: float) -> None
# But code doesn't have this function
```
**Suggestion**: Update documentation to remove this API, or add it to codebase
### 3. Signature Mismatch
**Severity**: Medium-High
**Description**: API exists in both but signatures differ
**Example**:
```python
# Docs say:
def move_local_x(delta: float)
# Code has:
def move_local_x(delta: float, snap: bool = False)
```
**Suggestion**: Update documentation to match actual signature
### 4. Description Mismatch
**Severity**: Low
**Description**: Different descriptions/docstrings
## Merge Modes
### Rule-Based Merge (Default)
Fast, deterministic merging using predefined rules:
1. **If API only in docs** → Include with `[DOCS_ONLY]` tag
2. **If API only in code** → Include with `[UNDOCUMENTED]` tag
3. **If both match perfectly** → Include normally
4. **If conflict exists** → Prefer code signature, keep docs description
**When to use**:
- Fast merging (< 1 second)
- Automated workflows
- You don't need human oversight
**Example**:
```bash
python3 cli/unified_scraper.py --config config.json --merge-mode rule-based
```
### Claude-Enhanced Merge
AI-powered reconciliation using local Claude Code:
1. Opens new terminal with Claude Code
2. Provides conflict context and instructions
3. Claude analyzes and creates reconciled API reference
4. Human can review and adjust before finalizing
**When to use**:
- Complex conflicts requiring judgment
- You want highest quality merge
- You have time for human oversight
**Example**:
```bash
python3 cli/unified_scraper.py --config config.json --merge-mode claude-enhanced
```
## Skill Output Structure
The unified scraper creates this structure:
```
output/skill-name/
├── SKILL.md # Main skill file with merged APIs
├── references/
│ ├── documentation/ # Documentation references
│ │ └── index.md
│ ├── github/ # GitHub references
│ │ ├── README.md
│ │ ├── issues.md
│ │ └── releases.md
│ ├── pdf/ # PDF references (if applicable)
│ │ └── index.md
│ ├── api/ # Merged API reference
│ │ └── merged_api.md
│ └── conflicts.md # Detailed conflict report
├── scripts/ # Empty (for user scripts)
└── assets/ # Empty (for user assets)
```
### SKILL.md Format
```markdown
# React
Complete React knowledge base combining official documentation and React codebase insights.
## 📚 Sources
This skill combines knowledge from multiple sources:
-**Documentation**: https://react.dev/
- Pages: 200
-**GitHub Repository**: facebook/react
- Code Analysis: surface
- Issues: 100
## ⚠️ Data Quality
**5 conflicts detected** between sources.
**Conflict Breakdown:**
- missing_in_docs: 3
- missing_in_code: 2
See `references/conflicts.md` for detailed conflict information.
## 🔧 API Reference
*Merged from documentation and code analysis*
### ✅ Verified APIs
*Documentation and code agree*
#### `useState(initialValue)`
...
### ⚠️ APIs with Conflicts
*Documentation and code differ*
#### `useEffect(callback, deps?)`
⚠️ **Conflict**: Documentation signature differs from code implementation
**Documentation says:**
```
useEffect(callback: () => void, deps: any[])
```
**Code implementation:**
```
useEffect(callback: () => void | (() => void), deps?: readonly any[])
```
*Source: both*
---
```
## Examples
### Example 1: React (Docs + GitHub)
```json
{
"name": "react",
"description": "Complete React framework knowledge",
"merge_mode": "rule-based",
"sources": [
{
"type": "documentation",
"base_url": "https://react.dev/",
"extract_api": true,
"max_pages": 200
},
{
"type": "github",
"repo": "facebook/react",
"include_code": true,
"code_analysis_depth": "surface"
}
]
}
```
### Example 2: Django (Docs + GitHub)
```json
{
"name": "django",
"description": "Complete Django framework knowledge",
"merge_mode": "rule-based",
"sources": [
{
"type": "documentation",
"base_url": "https://docs.djangoproject.com/en/stable/",
"extract_api": true,
"max_pages": 300
},
{
"type": "github",
"repo": "django/django",
"include_code": true,
"code_analysis_depth": "deep",
"file_patterns": [
"django/db/**/*.py",
"django/views/**/*.py"
]
}
]
}
```
### Example 3: Mixed Sources (Docs + GitHub + PDF)
```json
{
"name": "godot",
"description": "Complete Godot Engine knowledge",
"merge_mode": "claude-enhanced",
"sources": [
{
"type": "documentation",
"base_url": "https://docs.godotengine.org/en/stable/",
"extract_api": true,
"max_pages": 500
},
{
"type": "github",
"repo": "godotengine/godot",
"include_code": true,
"code_analysis_depth": "deep"
},
{
"type": "pdf",
"path": "/path/to/godot_manual.pdf",
"extract_tables": true
}
]
}
```
## Command Reference
### Unified Scraper
```bash
# Basic usage
python3 cli/unified_scraper.py --config configs/react_unified.json
# Override merge mode
python3 cli/unified_scraper.py --config configs/react_unified.json --merge-mode claude-enhanced
# Use cached data (skip re-scraping)
python3 cli/unified_scraper.py --config configs/react_unified.json --skip-scrape
```
### Validate Config
```bash
python3 -c "
import sys
sys.path.insert(0, 'cli')
from config_validator import validate_config
validator = validate_config('configs/react_unified.json')
print(f'Format: {\"Unified\" if validator.is_unified else \"Legacy\"}')
print(f'Sources: {len(validator.config.get(\"sources\", []))}')
print(f'Needs API merge: {validator.needs_api_merge()}')
"
```
## MCP Integration
The unified scraper is fully integrated with MCP. The `scrape_docs` tool automatically detects unified vs legacy configs and routes to the appropriate scraper.
```python
# MCP tool usage
{
"name": "scrape_docs",
"arguments": {
"config_path": "configs/react_unified.json",
"merge_mode": "rule-based" # Optional override
}
}
```
The tool will:
1. Auto-detect unified format
2. Route to `unified_scraper.py`
3. Apply specified merge mode
4. Return comprehensive output
## Backward Compatibility
**Legacy configs still work!** The system automatically detects legacy single-source configs and routes to the original `doc_scraper.py`.
```json
// Legacy config (still works)
{
"name": "react",
"base_url": "https://react.dev/",
...
}
// Automatically detected as legacy format
// Routes to doc_scraper.py
```
## Testing
Run integration tests:
```bash
python3 cli/test_unified_simple.py
```
Tests validate:
- ✅ Unified config validation
- ✅ Backward compatibility with legacy configs
- ✅ Mixed source type support
- ✅ Error handling for invalid configs
## Architecture
### Components
1. **config_validator.py**: Validates unified and legacy configs
2. **code_analyzer.py**: Extracts code signatures at configurable depth
3. **conflict_detector.py**: Detects API conflicts between sources
4. **merge_sources.py**: Implements rule-based and Claude-enhanced merging
5. **unified_scraper.py**: Main orchestrator
6. **unified_skill_builder.py**: Generates final skill structure
7. **skill_seeker_mcp/server.py**: MCP integration with auto-detection
### Data Flow
```
Unified Config
ConfigValidator (validates format)
UnifiedScraper.run()
┌────────────────────────────────────┐
│ Phase 1: Scrape All Sources │
│ - Documentation → doc_scraper │
│ - GitHub → github_scraper │
│ - PDF → pdf_scraper │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ Phase 2: Detect Conflicts │
│ - ConflictDetector │
│ - Compare docs APIs vs code APIs │
│ - Classify by type and severity │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ Phase 3: Merge Sources │
│ - RuleBasedMerger (fast) │
│ - OR ClaudeEnhancedMerger (AI) │
│ - Create unified API reference │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ Phase 4: Build Skill │
│ - UnifiedSkillBuilder │
│ - Generate SKILL.md with conflicts│
│ - Create reference structure │
│ - Generate conflicts report │
└────────────────────────────────────┘
Unified Skill (.zip ready)
```
## Best Practices
### 1. Start with Rule-Based Merge
Rule-based is fast and works well for most cases. Only use Claude-enhanced if you need human oversight.
### 2. Use Surface-Level Code Analysis
`code_analysis_depth: "surface"` is usually sufficient. Deep analysis is expensive and rarely needed.
### 3. Limit GitHub Issues
`max_issues: 100` is a good default. More than 200 issues rarely adds value.
### 4. Be Specific with File Patterns
```json
"file_patterns": [
"src/**/*.js", // Good: specific paths
"lib/**/*.ts"
]
// Not recommended:
"file_patterns": ["**/*.js"] // Too broad, slow
```
### 5. Monitor Conflict Reports
Always review `references/conflicts.md` to understand discrepancies between sources.
## Troubleshooting
### No Conflicts Detected
**Possible causes**:
- `extract_api: false` in documentation source
- `include_code: false` in GitHub source
- Code analysis found no APIs (check `code_analysis_depth`)
**Solution**: Ensure both sources have API extraction enabled
### Too Many Conflicts
**Possible causes**:
- Fuzzy matching threshold too strict
- Documentation uses different naming conventions
- Old documentation version
**Solution**: Review conflicts manually and adjust merge strategy
### Merge Takes Too Long
**Possible causes**:
- Using `code_analysis_depth: "full"` (very slow)
- Too many file patterns
- Large repository
**Solution**:
- Use `"surface"` or `"deep"` analysis
- Narrow file patterns
- Increase `rate_limit`
## Future Enhancements
Planned features:
- [ ] Automated conflict resolution strategies
- [ ] Conflict trend analysis across versions
- [ ] Multi-version comparison (docs v1 vs v2)
- [ ] Custom merge rules DSL
- [ ] Conflict confidence scores
## Support
For issues, questions, or suggestions:
- GitHub Issues: https://github.com/yusufkaraaslan/Skill_Seekers/issues
- Documentation: https://github.com/yusufkaraaslan/Skill_Seekers/docs
## Changelog
**v2.0 (October 2025)**: Unified multi-source scraping feature complete
- ✅ Config validation for unified format
- ✅ Deep code analysis with AST parsing
- ✅ Conflict detection (4 types, 3 severity levels)
- ✅ Rule-based merging
- ✅ Claude-enhanced merging
- ✅ Unified skill builder with inline conflict warnings
- ✅ MCP integration with auto-detection
- ✅ Backward compatibility with legacy configs
- ✅ Comprehensive tests and documentation
@@ -0,0 +1,351 @@
# How to Upload Skills to Claude
## Quick Answer
**You have 3 options to upload the `.zip` file:**
### Option 1: Automatic Upload (Recommended for CLI)
```bash
# Set your API key (one-time setup)
export ANTHROPIC_API_KEY=sk-ant-...
# Package and upload automatically
python3 cli/package_skill.py output/react/ --upload
# OR upload existing .zip
python3 cli/upload_skill.py output/react.zip
```
**Fully automatic** | No manual steps | Requires API key
### Option 2: Manual Upload (No API Key)
```bash
# Package the skill
python3 cli/package_skill.py output/react/
# This will:
# 1. Create output/react.zip
# 2. Open output/ folder automatically
# 3. Show clear upload instructions
# Then upload manually to https://claude.ai/skills
```
**No API key needed** | Works for everyone | Simple
### Option 3: Claude Code MCP (Easiest)
```
In Claude Code, just say:
"Package and upload the React skill"
# Automatically packages and uploads!
```
**Natural language** | Fully automatic | Best UX
---
## What's Inside the Zip?
The `.zip` file contains:
```
steam-economy.zip
├── SKILL.md ← Main skill file (Claude reads this first)
└── references/ ← Reference documentation
├── index.md ← Category index
├── api_reference.md ← API docs
├── pricing.md ← Pricing docs
├── trading.md ← Trading docs
└── ... ← Other categorized docs
```
**Note:** The zip only includes what Claude needs. It excludes:
- `.backup` files
- Build artifacts
- Temporary files
## What Does package_skill.py Do?
The package script:
1. **Finds your skill directory** (e.g., `output/steam-economy/`)
2. **Validates SKILL.md exists** (required!)
3. **Creates a .zip file** with the same name
4. **Includes all files** except backups
5. **Saves to** `output/` directory
**Example:**
```bash
python3 cli/package_skill.py output/steam-economy/
📦 Packaging skill: steam-economy
Source: output/steam-economy
Output: output/steam-economy.zip
+ SKILL.md
+ references/api_reference.md
+ references/pricing.md
+ references/trading.md
+ ...
✅ Package created: output/steam-economy.zip
Size: 14,290 bytes (14.0 KB)
```
## Complete Workflow
### Step 1: Scrape & Build
```bash
python3 cli/doc_scraper.py --config configs/steam-economy.json
```
**Output:**
- `output/steam-economy_data/` (raw scraped data)
- `output/steam-economy/` (skill directory)
### Step 2: Enhance (Recommended)
```bash
python3 cli/enhance_skill_local.py output/steam-economy/
```
**What it does:**
- Analyzes reference files
- Creates comprehensive SKILL.md
- Backs up original to SKILL.md.backup
**Output:**
- `output/steam-economy/SKILL.md` (enhanced)
- `output/steam-economy/SKILL.md.backup` (original)
### Step 3: Package
```bash
python3 cli/package_skill.py output/steam-economy/
```
**Output:**
- `output/steam-economy.zip`**THIS IS WHAT YOU UPLOAD**
### Step 4: Upload to Claude
1. Go to Claude (claude.ai)
2. Click "Add Skill" or skill upload button
3. Select `output/steam-economy.zip`
4. Done!
## What Files Are Required?
**Minimum required structure:**
```
your-skill/
└── SKILL.md ← Required! Claude reads this first
```
**Recommended structure:**
```
your-skill/
├── SKILL.md ← Main skill file (required)
└── references/ ← Reference docs (highly recommended)
├── index.md
└── *.md ← Category files
```
**Optional (can add manually):**
```
your-skill/
├── SKILL.md
├── references/
├── scripts/ ← Helper scripts
│ └── *.py
└── assets/ ← Templates, examples
└── *.txt
```
## File Size Limits
The package script shows size after packaging:
```
✅ Package created: output/steam-economy.zip
Size: 14,290 bytes (14.0 KB)
```
**Typical sizes:**
- Small skill: 5-20 KB
- Medium skill: 20-100 KB
- Large skill: 100-500 KB
Claude has generous size limits, so most documentation-based skills fit easily.
## Quick Reference
### Package a Skill
```bash
python3 cli/package_skill.py output/steam-economy/
```
### Package Multiple Skills
```bash
# Package all skills in output/
for dir in output/*/; do
if [ -f "$dir/SKILL.md" ]; then
python3 cli/package_skill.py "$dir"
fi
done
```
### Check What's in a Zip
```bash
unzip -l output/steam-economy.zip
```
### Test a Packaged Skill Locally
```bash
# Extract to temp directory
mkdir temp-test
unzip output/steam-economy.zip -d temp-test/
cat temp-test/SKILL.md
```
## Troubleshooting
### "SKILL.md not found"
```bash
# Make sure you scraped and built first
python3 cli/doc_scraper.py --config configs/steam-economy.json
# Then package
python3 cli/package_skill.py output/steam-economy/
```
### "Directory not found"
```bash
# Check what skills are available
ls output/
# Use correct path
python3 cli/package_skill.py output/YOUR-SKILL-NAME/
```
### Zip is Too Large
Most skills are small, but if yours is large:
```bash
# Check size
ls -lh output/steam-economy.zip
# If needed, check what's taking space
unzip -l output/steam-economy.zip | sort -k1 -rn | head -20
```
Reference files are usually small. Large sizes often mean:
- Many images (skills typically don't need images)
- Large code examples (these are fine, just be aware)
## What Does Claude Do With the Zip?
When you upload a skill zip:
1. **Claude extracts it**
2. **Reads SKILL.md first** - This tells Claude:
- When to activate this skill
- What the skill does
- Quick reference examples
- How to navigate the references
3. **Indexes reference files** - Claude can search through:
- `references/*.md` files
- Find specific APIs, examples, concepts
4. **Activates automatically** - When you ask about topics matching the skill
## Example: Using the Packaged Skill
After uploading `steam-economy.zip`:
**You ask:** "How do I implement microtransactions in my Steam game?"
**Claude:**
- Recognizes this matches steam-economy skill
- Reads SKILL.md for quick reference
- Searches references/microtransactions.md
- Provides detailed answer with code examples
## API-Based Automatic Upload
### Setup (One-Time)
```bash
# Get your API key from https://console.anthropic.com/
export ANTHROPIC_API_KEY=sk-ant-...
# Add to your shell profile to persist
echo 'export ANTHROPIC_API_KEY=sk-ant-...' >> ~/.bashrc # or ~/.zshrc
```
### Usage
```bash
# Upload existing .zip
python3 cli/upload_skill.py output/react.zip
# OR package and upload in one command
python3 cli/package_skill.py output/react/ --upload
```
### How It Works
The upload tool uses the Anthropic `/v1/skills` API endpoint to:
1. Read your .zip file
2. Authenticate with your API key
3. Upload to Claude's skill storage
4. Verify upload success
### Troubleshooting
**"ANTHROPIC_API_KEY not set"**
```bash
# Check if set
echo $ANTHROPIC_API_KEY
# If empty, set it
export ANTHROPIC_API_KEY=sk-ant-...
```
**"Authentication failed"**
- Verify your API key is correct
- Check https://console.anthropic.com/ for valid keys
**"Upload timed out"**
- Check your internet connection
- Try again or use manual upload
**Upload fails with error**
- Falls back to showing manual upload instructions
- You can still upload via https://claude.ai/skills
---
## Summary
**What you need to do:**
### With API Key (Automatic):
1. ✅ Scrape: `python3 cli/doc_scraper.py --config configs/YOUR-CONFIG.json`
2. ✅ Enhance: `python3 cli/enhance_skill_local.py output/YOUR-SKILL/`
3. ✅ Package & Upload: `python3 cli/package_skill.py output/YOUR-SKILL/ --upload`
4. ✅ Done! Skill is live in Claude
### Without API Key (Manual):
1. ✅ Scrape: `python3 cli/doc_scraper.py --config configs/YOUR-CONFIG.json`
2. ✅ Enhance: `python3 cli/enhance_skill_local.py output/YOUR-SKILL/`
3. ✅ Package: `python3 cli/package_skill.py output/YOUR-SKILL/`
4. ✅ Upload: Go to https://claude.ai/skills and upload the `.zip`
**What you upload:**
- The `.zip` file from `output/` directory
- Example: `output/steam-economy.zip`
**What's in the zip:**
- `SKILL.md` (required)
- `references/*.md` (recommended)
- Any scripts/assets you added (optional)
That's it! 🚀
@@ -0,0 +1,811 @@
# Complete Usage Guide for Skill Seeker
Comprehensive reference for all commands, options, and workflows.
## Table of Contents
- [Quick Reference](#quick-reference)
- [Main Tool: doc_scraper.py](#main-tool-doc_scraperpy)
- [Estimator: estimate_pages.py](#estimator-estimate_pagespy)
- [Enhancement Tools](#enhancement-tools)
- [Packaging Tool](#packaging-tool)
- [Testing Tools](#testing-tools)
- [Available Configs](#available-configs)
- [Common Workflows](#common-workflows)
- [Troubleshooting](#troubleshooting)
---
## Quick Reference
```bash
# 1. Estimate pages (fast, 1-2 min)
python3 cli/estimate_pages.py configs/react.json
# 2. Scrape documentation (20-40 min)
python3 cli/doc_scraper.py --config configs/react.json
# 3. Enhance with Claude Code (60 sec)
python3 cli/enhance_skill_local.py output/react/
# 4. Package to .zip (instant)
python3 cli/package_skill.py output/react/
# 5. Test everything (1 sec)
python3 cli/run_tests.py
```
---
## Main Tool: doc_scraper.py
### Full Help
```
usage: doc_scraper.py [-h] [--interactive] [--config CONFIG] [--name NAME]
[--url URL] [--description DESCRIPTION] [--skip-scrape]
[--dry-run] [--enhance] [--enhance-local]
[--api-key API_KEY]
Convert documentation websites to Claude skills
options:
-h, --help Show this help message and exit
--interactive, -i Interactive configuration mode
--config, -c CONFIG Load configuration from file (e.g., configs/godot.json)
--name NAME Skill name
--url URL Base documentation URL
--description, -d DESCRIPTION
Skill description
--skip-scrape Skip scraping, use existing data
--dry-run Preview what will be scraped without actually scraping
--enhance Enhance SKILL.md using Claude API after building
(requires API key)
--enhance-local Enhance SKILL.md using Claude Code in new terminal
(no API key needed)
--api-key API_KEY Anthropic API key for --enhance (or set ANTHROPIC_API_KEY)
```
### Usage Examples
**1. Use Preset Config (Recommended)**
```bash
python3 cli/doc_scraper.py --config configs/godot.json
python3 cli/doc_scraper.py --config configs/react.json
python3 cli/doc_scraper.py --config configs/vue.json
python3 cli/doc_scraper.py --config configs/django.json
python3 cli/doc_scraper.py --config configs/fastapi.json
```
**2. Interactive Mode**
```bash
python3 cli/doc_scraper.py --interactive
# Wizard walks you through:
# - Skill name
# - Base URL
# - Description
# - Selectors (optional)
# - URL patterns (optional)
# - Rate limit
# - Max pages
```
**3. Quick Mode (Minimal)**
```bash
python3 cli/doc_scraper.py \
--name react \
--url https://react.dev/ \
--description "React framework for building UIs"
```
**4. Dry-Run (Preview)**
```bash
python3 cli/doc_scraper.py --config configs/react.json --dry-run
# Shows what will be scraped without downloading data
# No directories created
# Fast validation
```
**5. Skip Scraping (Use Cached Data)**
```bash
python3 cli/doc_scraper.py --config configs/godot.json --skip-scrape
# Uses existing output/godot_data/
# Fast rebuild (1-3 minutes)
# Useful for testing changes
```
**6. With Local Enhancement**
```bash
python3 cli/doc_scraper.py --config configs/react.json --enhance-local
# Scrapes + enhances in one command
# Opens new terminal for Claude Code
# No API key needed
```
**7. With API Enhancement**
```bash
export ANTHROPIC_API_KEY=sk-ant-...
python3 cli/doc_scraper.py --config configs/react.json --enhance
# Or with inline API key:
python3 cli/doc_scraper.py --config configs/react.json --enhance --api-key sk-ant-...
```
### Output Structure
```
output/
├── {name}_data/ # Scraped raw data (cached)
│ ├── pages/
│ │ ├── page_0.json
│ │ ├── page_1.json
│ │ └── ...
│ └── summary.json # Scraping stats
└── {name}/ # Built skill directory
├── SKILL.md # Main skill file
├── SKILL.md.backup # Backup (if enhanced)
├── references/ # Categorized docs
│ ├── index.md
│ ├── getting_started.md
│ ├── api.md
│ └── ...
├── scripts/ # Empty (user scripts)
└── assets/ # Empty (user assets)
```
---
## Estimator: estimate_pages.py
### Full Help
```
usage: estimate_pages.py [-h] [--max-discovery MAX_DISCOVERY]
[--timeout TIMEOUT]
config
Estimate page count for Skill Seeker configs
positional arguments:
config Path to config JSON file
options:
-h, --help Show this help message and exit
--max-discovery, -m MAX_DISCOVERY
Maximum pages to discover (default: 1000)
--timeout, -t TIMEOUT
HTTP request timeout in seconds (default: 30)
```
### Usage Examples
**1. Quick Estimate (100 pages)**
```bash
python3 cli/estimate_pages.py configs/react.json --max-discovery 100
# Time: ~30-60 seconds
# Good for: Quick validation
```
**2. Standard Estimate (1000 pages - default)**
```bash
python3 cli/estimate_pages.py configs/godot.json
# Time: ~1-2 minutes
# Good for: Most use cases
```
**3. Deep Estimate (2000 pages)**
```bash
python3 cli/estimate_pages.py configs/vue.json --max-discovery 2000
# Time: ~3-5 minutes
# Good for: Large documentation sites
```
**4. Custom Timeout**
```bash
python3 cli/estimate_pages.py configs/django.json --timeout 60
# Useful for slow servers
```
### Output Example
```
🔍 Estimating pages for: react
📍 Base URL: https://react.dev/
🎯 Start URLs: 6
⏱️ Rate limit: 0.5s
🔢 Max discovery: 1000
⏳ Discovered: 180 pages (1.3 pages/sec)
======================================================================
📊 ESTIMATION RESULTS
======================================================================
Config: react
Base URL: https://react.dev/
✅ Pages Discovered: 180
⏳ Pages Pending: 50
📈 Estimated Total: 230
⏱️ Time Elapsed: 140.5s
⚡ Discovery Rate: 1.28 pages/sec
======================================================================
💡 RECOMMENDATIONS
======================================================================
✅ Current max_pages (300) is sufficient
⏱️ Estimated full scrape time: 1.9 minutes
(Based on rate_limit: 0.5s)
```
**What It Shows:**
- Estimated total pages to scrape
- Whether current `max_pages` is sufficient
- Recommended `max_pages` value
- Estimated scraping time
- Discovery rate (pages/sec)
---
## Enhancement Tools
### enhance_skill_local.py (Recommended)
**No API key needed - uses Claude Code Max plan**
```bash
# Usage
python3 cli/enhance_skill_local.py output/react/
python3 cli/enhance_skill_local.py output/godot/
# What it does:
# 1. Reads SKILL.md and references/
# 2. Opens new terminal with Claude Code
# 3. Claude enhances SKILL.md
# 4. Backs up original to SKILL.md.backup
# 5. Saves enhanced version
# Time: ~60 seconds
# Cost: Free (uses your Claude Code Max plan)
```
### enhance_skill.py (Alternative)
**Requires Anthropic API key**
```bash
# Install dependency first
pip3 install anthropic
# Usage with environment variable
export ANTHROPIC_API_KEY=sk-ant-...
python3 cli/enhance_skill.py output/react/
# Usage with inline API key
python3 cli/enhance_skill.py output/godot/ --api-key sk-ant-...
# What it does:
# 1. Reads SKILL.md and references/
# 2. Calls Claude API (Sonnet 4)
# 3. Enhances SKILL.md
# 4. Backs up original to SKILL.md.backup
# 5. Saves enhanced version
# Time: ~30-60 seconds
# Cost: ~$0.01-0.10 per skill (depending on size)
```
---
## Packaging Tool
### package_skill.py
```bash
# Usage
python3 cli/package_skill.py output/react/
python3 cli/package_skill.py output/godot/
# What it does:
# 1. Validates SKILL.md exists
# 2. Creates .zip with all skill files
# 3. Saves to output/{name}.zip
# Output:
# output/react.zip
# output/godot.zip
# Time: Instant
```
---
## Testing Tools
### run_tests.py
```bash
# Run all tests (default)
python3 cli/run_tests.py
# 71 tests, ~1 second
# Verbose output
python3 cli/run_tests.py -v
python3 cli/run_tests.py --verbose
# Quiet output
python3 cli/run_tests.py -q
python3 cli/run_tests.py --quiet
# Stop on first failure
python3 cli/run_tests.py -f
python3 cli/run_tests.py --failfast
# Run specific test suite
python3 cli/run_tests.py --suite config
python3 cli/run_tests.py --suite features
python3 cli/run_tests.py --suite integration
# List all tests
python3 cli/run_tests.py --list
```
### Individual Tests
```bash
# Run single test file
python3 -m unittest tests.test_config_validation
python3 -m unittest tests.test_scraper_features
python3 -m unittest tests.test_integration
# Run single test class
python3 -m unittest tests.test_config_validation.TestConfigValidation
# Run single test method
python3 -m unittest tests.test_config_validation.TestConfigValidation.test_valid_complete_config
```
---
## Available Configs
### Preset Configs (Ready to Use)
| Config | Framework | Pages | Description |
|--------|-----------|-------|-------------|
| `godot.json` | Godot Engine | ~500 | Game engine documentation |
| `react.json` | React | ~300 | React framework docs |
| `vue.json` | Vue.js | ~250 | Vue.js framework docs |
| `django.json` | Django | ~400 | Django web framework |
| `fastapi.json` | FastAPI | ~200 | FastAPI Python framework |
| `steam-economy-complete.json` | Steam | ~100 | Steam Economy API docs |
### View Config Details
```bash
# List all configs
ls configs/
# View config content
cat configs/react.json
python3 -m json.tool configs/godot.json
```
### Config Structure
```json
{
"name": "react",
"base_url": "https://react.dev/",
"description": "React - JavaScript library for building UIs",
"start_urls": [
"https://react.dev/learn",
"https://react.dev/reference/react",
"https://react.dev/reference/react-dom"
],
"selectors": {
"main_content": "article",
"title": "h1",
"code_blocks": "pre code"
},
"url_patterns": {
"include": ["/learn/", "/reference/"],
"exclude": ["/blog/", "/community/"]
},
"categories": {
"getting_started": ["learn", "tutorial", "intro"],
"api": ["reference", "api", "hooks"],
"guides": ["guide"]
},
"rate_limit": 0.5,
"max_pages": 300
}
```
---
## Common Workflows
### Workflow 1: Use Preset (Fastest)
```bash
# 1. Estimate (optional, 1-2 min)
python3 cli/estimate_pages.py configs/react.json
# 2. Scrape with local enhancement (25 min)
python3 cli/doc_scraper.py --config configs/react.json --enhance-local
# 3. Package (instant)
python3 cli/package_skill.py output/react/
# Result: output/react.zip
# Upload to Claude!
```
### Workflow 2: Custom Documentation
```bash
# 1. Create config
cat > configs/my-docs.json << 'EOF'
{
"name": "my-docs",
"base_url": "https://docs.example.com/",
"description": "My documentation site",
"rate_limit": 0.5,
"max_pages": 200
}
EOF
# 2. Estimate
python3 cli/estimate_pages.py configs/my-docs.json
# 3. Dry-run test
python3 cli/doc_scraper.py --config configs/my-docs.json --dry-run
# 4. Full scrape
python3 cli/doc_scraper.py --config configs/my-docs.json
# 5. Enhance
python3 cli/enhance_skill_local.py output/my-docs/
# 6. Package
python3 cli/package_skill.py output/my-docs/
```
### Workflow 3: Interactive Mode
```bash
# 1. Start interactive wizard
python3 cli/doc_scraper.py --interactive
# 2. Answer prompts:
# - Name: my-framework
# - URL: https://framework.dev/
# - Description: My favorite framework
# - Selectors: (uses defaults)
# - Rate limit: 0.5
# - Max pages: 100
# 3. Enhance
python3 cli/enhance_skill_local.py output/my-framework/
# 4. Package
python3 cli/package_skill.py output/my-framework/
```
### Workflow 4: Quick Mode
```bash
python3 cli/doc_scraper.py \
--name vue \
--url https://vuejs.org/ \
--description "Vue.js framework" \
--enhance-local
```
### Workflow 5: Rebuild from Cache
```bash
# Already scraped once?
# Skip re-scraping, just rebuild
python3 cli/doc_scraper.py --config configs/godot.json --skip-scrape
# Try new enhancement
python3 cli/enhance_skill_local.py output/godot/
# Re-package
python3 cli/package_skill.py output/godot/
```
### Workflow 6: Testing New Config
```bash
# 1. Create test config with low max_pages
cat > configs/test.json << 'EOF'
{
"name": "test-site",
"base_url": "https://docs.test.com/",
"max_pages": 20,
"rate_limit": 0.1
}
EOF
# 2. Estimate
python3 cli/estimate_pages.py configs/test.json --max-discovery 50
# 3. Dry-run
python3 cli/doc_scraper.py --config configs/test.json --dry-run
# 4. Small scrape
python3 cli/doc_scraper.py --config configs/test.json
# 5. Validate output
ls output/test-site/
ls output/test-site/references/
# 6. If good, increase max_pages and re-run
```
---
## Troubleshooting
### Issue: "Rate limit exceeded"
```bash
# Increase rate_limit in config
# Default: 0.5 seconds
# Conservative: 1.0 seconds
# Very conservative: 2.0 seconds
# Edit config:
{
"rate_limit": 1.0
}
```
### Issue: "Too many pages"
```bash
# Estimate first
python3 cli/estimate_pages.py configs/my-config.json
# Set max_pages based on estimate
# Add buffer: estimated + 50
# Edit config:
{
"max_pages": 350 # for 300 estimated
}
```
### Issue: "No content extracted"
```bash
# Wrong selectors
# Test selectors manually:
curl -s https://docs.example.com/ | grep -i 'article\|main\|content'
# Common selectors:
"main_content": "article"
"main_content": "main"
"main_content": ".content"
"main_content": "#main-content"
"main_content": "div[role=\"main\"]"
# Update config with correct selector
```
### Issue: "Tests failing"
```bash
# Run specific failing test
python3 -m unittest tests.test_config_validation.TestConfigValidation.test_name -v
# Check error message
# Verify expectations match implementation
```
### Issue: "Enhancement fails"
```bash
# Local enhancement:
# Make sure Claude Code is running
# Check terminal output
# API enhancement:
# Verify API key is set:
echo $ANTHROPIC_API_KEY
# Or use inline:
python3 cli/enhance_skill.py output/react/ --api-key sk-ant-...
```
### Issue: "Package fails"
```bash
# Verify SKILL.md exists
ls output/my-skill/SKILL.md
# If missing, build first:
python3 cli/doc_scraper.py --config configs/my-skill.json --skip-scrape
```
### Issue: "Can't find output"
```bash
# Check output directory
ls output/
# Skill data (cached):
ls output/{name}_data/
# Built skill:
ls output/{name}/
# Packaged skill:
ls output/{name}.zip
```
---
## Advanced Usage
### Custom Selectors
```json
{
"selectors": {
"main_content": "div.documentation",
"title": "h1.page-title",
"code_blocks": "pre.highlight code",
"navigation": "nav.sidebar"
}
}
```
### URL Pattern Filtering
```json
{
"url_patterns": {
"include": [
"/docs/",
"/guide/",
"/api/",
"/tutorial/"
],
"exclude": [
"/blog/",
"/news/",
"/community/",
"/showcase/"
]
}
}
```
### Custom Categories
```json
{
"categories": {
"getting_started": ["intro", "tutorial", "quickstart", "installation"],
"core_concepts": ["concept", "fundamental", "architecture"],
"api": ["reference", "api", "method", "function"],
"guides": ["guide", "how-to", "example"],
"advanced": ["advanced", "expert", "performance"]
}
}
```
### Multiple Start URLs
```json
{
"start_urls": [
"https://docs.example.com/getting-started/",
"https://docs.example.com/api/",
"https://docs.example.com/guides/",
"https://docs.example.com/examples/"
]
}
```
---
## Performance Tips
1. **Estimate first**: Save 20-40 minutes by validating config
2. **Use dry-run**: Test selectors before full scrape
3. **Cache data**: Use `--skip-scrape` for fast rebuilds
4. **Adjust rate_limit**: Balance speed vs politeness
5. **Set appropriate max_pages**: Don't scrape more than needed
6. **Use start_urls**: Target specific documentation sections
7. **Filter URLs**: Use include/exclude patterns
8. **Run tests**: Catch issues early
---
## Environment Variables
```bash
# Anthropic API key (for API enhancement)
export ANTHROPIC_API_KEY=sk-ant-...
# Optional: Set custom output directory
export SKILL_SEEKER_OUTPUT_DIR=/path/to/output
```
---
## Exit Codes
- `0`: Success
- `1`: Error (general)
- `2`: Warning (estimation hit limit)
---
## File Locations
```
Skill_Seekers/
├── doc_scraper.py # Main tool
├── estimate_pages.py # Estimator
├── enhance_skill.py # API enhancement
├── enhance_skill_local.py # Local enhancement
├── package_skill.py # Packager
├── run_tests.py # Test runner
├── configs/ # Preset configs
├── tests/ # Test suite
├── docs/ # Documentation
└── output/ # Generated output
```
---
## Getting Help
```bash
# Tool-specific help
python3 cli/doc_scraper.py --help
python3 cli/estimate_pages.py --help
python3 cli/run_tests.py --help
# Documentation
cat CLAUDE.md # Quick reference for Claude Code
cat docs/CLAUDE.md # Detailed technical docs
cat docs/TESTING.md # Testing guide
cat docs/USAGE.md # This file
cat docs/ENHANCEMENT.md # Enhancement guide
cat docs/UPLOAD_GUIDE.md # Upload instructions
cat README.md # Project overview
```
---
## Summary
**Essential Commands:**
```bash
python3 cli/estimate_pages.py configs/react.json # Estimate
python3 cli/doc_scraper.py --config configs/react.json # Scrape
python3 cli/enhance_skill_local.py output/react/ # Enhance
python3 cli/package_skill.py output/react/ # Package
python3 cli/run_tests.py # Test
```
**Quick Start:**
```bash
pip3 install requests beautifulsoup4
python3 cli/doc_scraper.py --config configs/react.json --enhance-local
python3 cli/package_skill.py output/react/
# Upload output/react.zip to Claude!
```
Happy skill creating! 🚀
@@ -0,0 +1,867 @@
# Active Skills Design - Demand-Driven Documentation Loading
**Date:** 2025-10-24
**Type:** Architecture Design
**Status:** Phase 1 Implemented ✅
**Author:** Edgar + Claude (Brainstorming Session)
---
## Executive Summary
Transform Skill_Seekers from creating **passive documentation dumps** into **active, intelligent skills** that load documentation on-demand. This eliminates context bloat (300k → 5-10k per query) while maintaining full access to complete documentation.
**Key Innovation:** Skills become lightweight routers with heavy tools in `scripts/`, not documentation repositories.
---
## Problem Statement
### Current Architecture: Passive Skills
**What happens today:**
```
Agent: "How do I use Hono middleware?"
Skill: *Claude loads 203k llms-txt.md into context*
Agent: *answers using loaded docs*
Result: Context bloat, slower performance, hits limits
```
**Issues:**
1. **Context Bloat**: 319k llms-full.txt loaded entirely into context
2. **Wasted Resources**: Agent needs 5k but gets 319k
3. **Truncation Loss**: 36% of content lost (319k → 203k) due to size limits
4. **File Extension Bug**: llms.txt files stored as .txt instead of .md
5. **Single Variant**: Only downloads one file (usually llms-full.txt)
### Current File Structure
```
output/hono/
├── SKILL.md ──────────► Documentation dump + instructions
├── references/
│ └── llms-txt.md ───► 203k (36% truncated from 319k original)
├── scripts/ ──────────► EMPTY (placeholder only!)
└── assets/ ───────────► EMPTY (placeholder only!)
```
---
## Proposed Architecture: Active Skills
### Core Concept
**Skills = Routers + Tools**, not documentation dumps.
**New workflow:**
```
Agent: "How do I use Hono middleware?"
Skill: *runs scripts/search.py "middleware"*
Script: *loads llms-full.md, extracts middleware section, returns 8k*
Agent: *answers using ONLY 8k* (CLEAN CONTEXT!)
Result: 40x less context, no truncation, full access to docs
```
### Benefits
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Context per query | 203k | 5-10k | **20-40x reduction** |
| Content loss | 36% truncated | 0% (no truncation) | **Full fidelity** |
| Variants available | 1 | 3 | **User choice** |
| File format | .txt (wrong) | .md (correct) | **Fixed** |
| Agent workflow | Passive read | Active tools | **Autonomous** |
---
## Design Components
### Component 1: Multi-Variant Download
**Change:** Download ALL 3 variants, not just one.
**File naming (FIXED):**
- `https://hono.dev/llms-full.txt``llms-full.md`
- `https://hono.dev/llms.txt``llms.md`
- `https://hono.dev/llms-small.txt``llms-small.md`
**Sizes (Hono example):**
- `llms-full.md` - 319k (complete documentation)
- `llms-small.md` - 176k (curated essentials)
- `llms.md` - 5.4k (quick reference)
**Storage:**
```
output/hono/references/
├── llms-full.md # 319k - everything (RENAMED from .txt)
├── llms-small.md # 176k - curated (RENAMED from .txt)
├── llms.md # 5.4k - quick ref (RENAMED from .txt)
└── catalog.json # Generated index (NEW)
```
**Implementation in `_try_llms_txt()`:**
```python
def _try_llms_txt(self) -> bool:
"""Download ALL llms.txt variants for active skills"""
# 1. Detect all available variants
detector = LlmsTxtDetector(self.base_url)
variants = detector.detect_all() # NEW method
downloaded = {}
for variant_info in variants:
url = variant_info['url'] # https://hono.dev/llms-full.txt
variant = variant_info['variant'] # 'full', 'standard', 'small'
downloader = LlmsTxtDownloader(url)
content = downloader.download()
if content:
# ✨ FIX: Rename .txt → .md immediately
clean_name = f"llms-{variant}.md"
downloaded[variant] = {
'content': content,
'filename': clean_name
}
# 2. Save ALL variants (not just one)
for variant, data in downloaded.items():
path = os.path.join(self.skill_dir, "references", data['filename'])
with open(path, 'w', encoding='utf-8') as f:
f.write(data['content'])
# 3. Generate catalog from smallest variant
if 'small' in downloaded:
self._generate_catalog(downloaded['small']['content'])
return True
```
---
### Component 2: The Catalog System
**Purpose:** Lightweight index of what exists, not the content itself.
**File:** `assets/catalog.json`
**Structure:**
```json
{
"metadata": {
"framework": "hono",
"version": "auto-detected",
"generated": "2025-10-24T14:30:00Z",
"total_sections": 93,
"variants": {
"quick": "llms-small.md",
"standard": "llms.md",
"complete": "llms-full.md"
}
},
"sections": [
{
"id": "routing",
"title": "Routing",
"h1_marker": "# Routing",
"topics": ["routes", "path", "params", "wildcard"],
"size_bytes": 4800,
"variants": ["quick", "complete"],
"complexity": "beginner"
},
{
"id": "middleware",
"title": "Middleware",
"h1_marker": "# Middleware",
"topics": ["cors", "auth", "logging", "compression"],
"size_bytes": 8200,
"variants": ["quick", "complete"],
"complexity": "intermediate"
}
],
"search_index": {
"cors": ["middleware"],
"routing": ["routing", "path-parameters"],
"authentication": ["middleware", "jwt"],
"context": ["context-handling"],
"streaming": ["streaming-responses"]
}
}
```
**Generation (from llms-small.md):**
```python
def _generate_catalog(self, llms_small_content):
"""Generate catalog.json from llms-small.md TOC"""
catalog = {
"metadata": {...},
"sections": [],
"search_index": {}
}
# Split by h1 headers
sections = re.split(r'\n# ', llms_small_content)
for section_text in sections[1:]:
lines = section_text.split('\n')
title = lines[0].strip()
# Extract h2 topics
topics = re.findall(r'^## (.+)$', section_text, re.MULTILINE)
topics = [t.strip().lower() for t in topics]
section_info = {
"id": title.lower().replace(' ', '-'),
"title": title,
"h1_marker": f"# {title}",
"topics": topics + [title.lower()],
"size_bytes": len(section_text),
"variants": ["quick", "complete"]
}
catalog["sections"].append(section_info)
# Build search index
for topic in section_info["topics"]:
if topic not in catalog["search_index"]:
catalog["search_index"][topic] = []
catalog["search_index"][topic].append(section_info["id"])
# Save to assets/catalog.json
catalog_path = os.path.join(self.skill_dir, "assets", "catalog.json")
with open(catalog_path, 'w', encoding='utf-8') as f:
json.dump(catalog, f, indent=2)
```
---
### Component 3: Active Scripts
**Location:** `scripts/` directory (currently empty)
#### Script 1: `scripts/search.py`
**Purpose:** Search and return only relevant documentation sections.
```python
#!/usr/bin/env python3
"""
ABOUTME: Searches framework documentation and returns relevant sections
ABOUTME: Loads only what's needed - keeps agent context clean
"""
import json
import sys
import re
from pathlib import Path
def search(query, detail="auto"):
"""
Search documentation and return relevant sections.
Args:
query: Search term (e.g., "middleware", "cors", "routing")
detail: "quick" | "standard" | "complete" | "auto"
Returns:
Markdown text of relevant sections only
"""
# Load catalog
catalog_path = Path(__file__).parent.parent / "assets" / "catalog.json"
catalog = json.load(open(catalog_path))
# 1. Find matching sections using search index
query_lower = query.lower()
matching_section_ids = set()
for keyword, section_ids in catalog["search_index"].items():
if query_lower in keyword or keyword in query_lower:
matching_section_ids.update(section_ids)
# Get section details
matches = [s for s in catalog["sections"] if s["id"] in matching_section_ids]
if not matches:
return f"❌ No sections found for '{query}'. Try: python scripts/list_topics.py"
# 2. Determine detail level
if detail == "auto":
# Use quick for overview, complete for deep dive
total_size = sum(s["size_bytes"] for s in matches)
if total_size > 50000: # > 50k
variant = "quick"
else:
variant = "complete"
else:
variant = detail
variant_file = catalog["metadata"]["variants"].get(variant, "complete")
# 3. Load documentation file
doc_path = Path(__file__).parent.parent / "references" / variant_file
doc_content = open(doc_path, 'r', encoding='utf-8').read()
# 4. Extract matched sections
results = []
for match in matches:
h1_marker = match["h1_marker"]
# Find section boundaries
start = doc_content.find(h1_marker)
if start == -1:
continue
# Find next h1 (or end of file)
next_h1 = doc_content.find("\n# ", start + len(h1_marker))
if next_h1 == -1:
section_text = doc_content[start:]
else:
section_text = doc_content[start:next_h1]
results.append({
'title': match['title'],
'size': len(section_text),
'content': section_text
})
# 5. Format output
output = [f"# Search Results for '{query}' ({len(results)} sections found)\n"]
output.append(f"**Variant used:** {variant} ({variant_file})")
output.append(f"**Total size:** {sum(r['size'] for r in results):,} bytes\n")
output.append("---\n")
for result in results:
output.append(result['content'])
output.append("\n---\n")
return '\n'.join(output)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python search.py <query> [detail]")
print("Example: python search.py middleware")
print("Example: python search.py routing --detail quick")
sys.exit(1)
query = sys.argv[1]
detail = sys.argv[2] if len(sys.argv) > 2 else "auto"
print(search(query, detail))
```
#### Script 2: `scripts/list_topics.py`
**Purpose:** Show all available documentation sections.
```python
#!/usr/bin/env python3
"""
ABOUTME: Lists all available documentation sections with sizes
ABOUTME: Helps agent discover what documentation exists
"""
import json
from pathlib import Path
def list_topics():
"""List all available documentation sections."""
catalog_path = Path(__file__).parent.parent / "assets" / "catalog.json"
catalog = json.load(open(catalog_path))
print(f"# Available Documentation Topics ({catalog['metadata']['framework']})\n")
print(f"**Total sections:** {catalog['metadata']['total_sections']}")
print(f"**Variants:** {', '.join(catalog['metadata']['variants'].keys())}\n")
print("---\n")
# Group by complexity if available
by_complexity = {}
for section in catalog["sections"]:
complexity = section.get("complexity", "general")
if complexity not in by_complexity:
by_complexity[complexity] = []
by_complexity[complexity].append(section)
for complexity in ["beginner", "intermediate", "advanced", "general"]:
if complexity not in by_complexity:
continue
sections = by_complexity[complexity]
print(f"## {complexity.title()} ({len(sections)} sections)\n")
for section in sections:
size_kb = section["size_bytes"] / 1024
topics_str = ", ".join(section["topics"][:3])
print(f"- **{section['title']}** ({size_kb:.1f}k)")
print(f" Topics: {topics_str}")
print(f" Search: `python scripts/search.py {section['id']}`\n")
if __name__ == "__main__":
list_topics()
```
#### Script 3: `scripts/get_section.py`
**Purpose:** Extract a complete section by exact title.
```python
#!/usr/bin/env python3
"""
ABOUTME: Extracts a complete documentation section by title
ABOUTME: Returns full section from llms-full.md (no truncation)
"""
import json
import sys
from pathlib import Path
def get_section(title, variant="complete"):
"""
Get a complete section by exact title.
Args:
title: Section title (e.g., "Middleware", "Routing")
variant: Which file to use (quick/standard/complete)
Returns:
Complete section content
"""
catalog_path = Path(__file__).parent.parent / "assets" / "catalog.json"
catalog = json.load(open(catalog_path))
# Find section
section = None
for s in catalog["sections"]:
if s["title"].lower() == title.lower():
section = s
break
if not section:
return f"❌ Section '{title}' not found. Try: python scripts/list_topics.py"
# Load doc
variant_file = catalog["metadata"]["variants"].get(variant, "complete")
doc_path = Path(__file__).parent.parent / "references" / variant_file
doc_content = open(doc_path, 'r', encoding='utf-8').read()
# Extract section
h1_marker = section["h1_marker"]
start = doc_content.find(h1_marker)
if start == -1:
return f"❌ Section '{title}' not found in {variant_file}"
next_h1 = doc_content.find("\n# ", start + len(h1_marker))
if next_h1 == -1:
section_text = doc_content[start:]
else:
section_text = doc_content[start:next_h1]
return section_text
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_section.py <title> [variant]")
print("Example: python get_section.py Middleware")
print("Example: python get_section.py Routing quick")
sys.exit(1)
title = sys.argv[1]
variant = sys.argv[2] if len(sys.argv) > 2 else "complete"
print(get_section(title, variant))
```
---
### Component 4: Active SKILL.md Template
**New template for llms.txt-based skills:**
```markdown
---
name: {name}
description: {description}
type: active
---
# {Name} Skill
**⚡ This is an ACTIVE skill** - Uses scripts to load documentation on-demand instead of dumping everything into context.
## 🎯 Strategy: Demand-Driven Documentation
**Traditional approach:**
- Load 300k+ documentation into context
- Agent reads everything to answer one question
- Context bloat, slower performance
**Active approach:**
- Load 5-10k of relevant sections on-demand
- Agent calls scripts to fetch what's needed
- Clean context, faster performance
## 📚 Available Documentation
This skill provides access to {num_sections} documentation sections across 3 detail levels:
- **Quick Reference** (`llms-small.md`): {small_size}k - Curated essentials
- **Standard** (`llms.md`): {standard_size}k - Core concepts
- **Complete** (`llms-full.md`): {full_size}k - Everything
## 🔧 Tools Available
### 1. Search Documentation
Find and load only relevant sections:
```bash
python scripts/search.py "middleware"
python scripts/search.py "routing" --detail quick
```
**Returns:** 5-10k of relevant content (not 300k!)
### 2. List All Topics
See what documentation exists:
```bash
python scripts/list_topics.py
```
**Returns:** Table of contents with section sizes and search hints
### 3. Get Complete Section
Extract a full section by title:
```bash
python scripts/get_section.py "Middleware"
python scripts/get_section.py "Routing" quick
```
**Returns:** Complete section from chosen variant
## 💡 Recommended Workflow
1. **Discover:** `python scripts/list_topics.py` to see what's available
2. **Search:** `python scripts/search.py "your topic"` to find relevant sections
3. **Deep Dive:** Use returned content to answer questions in detail
4. **Iterate:** Search more specific topics as needed
## ⚠️ Important
**DON'T:** Read `references/*.md` files directly into context
**DO:** Use scripts to fetch only what you need
This keeps your context clean and focused!
## 📊 Index
Complete section catalog available in `assets/catalog.json` with search mappings and size information.
## 🔄 Updating
To refresh with latest documentation:
```bash
python3 cli/doc_scraper.py --config configs/{name}.json
```
```
---
## Implementation Plan
### Phase 1: Foundation (Quick Fixes)
**Tasks:**
1. Fix `.txt``.md` renaming in downloader
2. Download all 3 variants (not just one)
3. Store all variants in `references/` with correct names
4. Remove content truncation (2500 chars → unlimited)
**Time:** 1-2 hours
**Files:** `cli/doc_scraper.py`, `cli/llms_txt_downloader.py`
### Phase 2: Catalog System
**Tasks:**
1. Implement `_generate_catalog()` method
2. Parse llms-small.md to extract sections
3. Build search index from topics
4. Generate `assets/catalog.json`
**Time:** 2-3 hours
**Files:** `cli/doc_scraper.py`
### Phase 3: Active Scripts
**Tasks:**
1. Create `scripts/search.py`
2. Create `scripts/list_topics.py`
3. Create `scripts/get_section.py`
4. Make scripts executable (`chmod +x`)
**Time:** 2-3 hours
**Files:** New scripts in `scripts/` template directory
### Phase 4: Template Updates
**Tasks:**
1. Create new active SKILL.md template
2. Update `create_enhanced_skill_md()` to use active template for llms.txt skills
3. Update documentation to explain active skills
**Time:** 1 hour
**Files:** `cli/doc_scraper.py`, `README.md`, `CLAUDE.md`
### Phase 5: Testing & Refinement
**Tasks:**
1. Test with Hono skill (has all 3 variants)
2. Test search accuracy
3. Measure context reduction
4. Document examples
**Time:** 2-3 hours
**Total Estimated Time:** 8-12 hours
---
## Migration Path
### Backward Compatibility
**Existing skills:** No changes (passive skills still work)
**New llms.txt skills:** Automatically use active architecture
**User choice:** Can disable via config flag
### Config Option
```json
{
"name": "hono",
"llms_txt_url": "https://hono.dev/llms-full.txt",
"active_skill": true, // NEW: Enable active architecture (default: true)
"base_url": "https://hono.dev/docs"
}
```
### Detection Logic
```python
# In _try_llms_txt()
active_mode = self.config.get('active_skill', True) # Default true
if active_mode:
# Download all variants, generate catalog, create scripts
self._build_active_skill(downloaded)
else:
# Traditional: single file, no scripts
self._build_passive_skill(downloaded)
```
---
## Benefits Analysis
### Context Efficiency
| Scenario | Passive Skill | Active Skill | Improvement |
|----------|---------------|--------------|-------------|
| Simple query | 203k loaded | 5k loaded | **40x reduction** |
| Multi-topic query | 203k loaded | 15k loaded | **13x reduction** |
| Deep dive | 203k loaded | 30k loaded | **6x reduction** |
### Data Fidelity
| Aspect | Passive | Active |
|--------|---------|--------|
| Content truncation | 36% lost | 0% lost |
| Code truncation | 600 chars max | Unlimited |
| Variants available | 1 | 3 |
### Agent Capabilities
**Passive Skills:**
- ❌ Cannot choose detail level
- ❌ Cannot search efficiently
- ❌ Must read entire context
- ❌ Limited by context window
**Active Skills:**
- ✅ Chooses appropriate detail level
- ✅ Searches catalog efficiently
- ✅ Loads only what's needed
- ✅ Unlimited documentation access
---
## Trade-offs
### Advantages
1. **Massive context reduction** (20-40x less per query)
2. **No content loss** (all 3 variants preserved)
3. **Correct file format** (.md not .txt)
4. **Agent autonomy** (tools to fetch docs)
5. **Scalable** (works with 1MB+ docs)
### Disadvantages
1. **Complexity** (scripts + catalog vs simple files)
2. **Initial overhead** (catalog generation)
3. **Agent learning curve** (must learn to use scripts)
4. **Dependency** (Python required to run scripts)
### Risk Mitigation
**Risk:** Scripts don't work in Claude's sandbox
**Mitigation:** Test thoroughly, provide fallback to passive mode
**Risk:** Catalog generation fails
**Mitigation:** Graceful degradation to single-file mode
**Risk:** Agent doesn't use scripts
**Mitigation:** Clear SKILL.md instructions, examples in quick reference
---
## Success Metrics
### Technical Metrics
- ✅ Context per query < 20k (down from 203k)
- ✅ All 3 variants downloaded and named correctly
- ✅ 0% content truncation
- ✅ Catalog generation < 5 seconds
- ✅ Search script < 1 second response time
### User Experience Metrics
- ✅ Agent successfully uses scripts without prompting
- ✅ Answers are equally or more accurate than passive mode
- ✅ Agent can handle queries about all documentation sections
- ✅ No "context limit exceeded" errors
---
## Future Enhancements
### Phase 6: Smart Caching
Cache frequently accessed sections in SKILL.md quick reference:
```python
# Track access frequency in catalog.json
"sections": [
{
"id": "middleware",
"access_count": 47, # NEW: Track usage
"last_accessed": "2025-10-24T14:30:00Z"
}
]
# Include top 10 most-accessed sections directly in SKILL.md
```
### Phase 7: Semantic Search
Use embeddings for better search:
```python
# Generate embeddings for each section
"sections": [
{
"id": "middleware",
"embedding": [...], # NEW: Vector embedding
"topics": ["cors", "auth"]
}
]
# In search.py: Use cosine similarity for better matches
```
### Phase 8: Progressive Loading
Load increasingly detailed docs:
```python
# First: Load llms.md (5.4k - overview)
# If insufficient: Load llms-small.md section (15k)
# If still insufficient: Load llms-full.md section (30k)
```
---
## Conclusion
Active skills represent a fundamental shift from **documentation repositories** to **documentation routers**. By treating skills as intelligent intermediaries rather than static dumps, we can:
1. **Eliminate context bloat** (40x reduction)
2. **Preserve full fidelity** (0% truncation)
3. **Enable agent autonomy** (tools to fetch docs)
4. **Scale indefinitely** (no size limits)
This design maintains backward compatibility while unlocking new capabilities for modern, LLM-optimized documentation sources like llms.txt.
**Recommendation:** Implement in phases, starting with foundation fixes, then catalog system, then active scripts. Test thoroughly with Hono before making it the default for all llms.txt-based skills.
---
## References
- Original brainstorming session: 2025-10-24
- llms.txt convention: https://llmstxt.org/
- Hono example: https://hono.dev/llms-full.txt
- Skill_Seekers repository: Current project
---
## Appendix: Example Workflows
### Example 1: Agent Searches for "Middleware"
```bash
# Agent runs:
python scripts/search.py "middleware"
# Script returns ~8k of middleware documentation from llms-full.md
# Agent uses that 8k to answer the question
# Total context used: 8k (not 319k!)
```
### Example 2: Agent Explores Documentation
```bash
# 1. Agent lists topics
python scripts/list_topics.py
# Returns: Table of contents (2k)
# 2. Agent picks a topic
python scripts/get_section.py "Routing"
# Returns: Complete Routing section (5k)
# 3. Agent searches related topics
python scripts/search.py "path parameters"
# Returns: Routing + Path section (7k)
# Total context used across 3 queries: 14k (not 3 × 319k = 957k!)
```
### Example 3: Agent Needs Quick Answer
```bash
# Agent uses quick variant for overview
python scripts/search.py "cors" --detail quick
# Returns: Short CORS explanation from llms-small.md (2k)
# If insufficient, agent can follow up with:
python scripts/get_section.py "Middleware" # Full section from llms-full.md
```
---
**Document Status:** Ready for review and implementation planning.
@@ -0,0 +1,682 @@
# Active Skills Phase 1: Foundation Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Fix fundamental issues in llms.txt handling: rename .txt→.md, download all 3 variants, remove truncation.
**Architecture:** Modify existing llms.txt download/parse/build workflow to handle multiple variants correctly, store with proper extensions, and preserve complete content without truncation.
**Tech Stack:** Python 3.10+, requests, BeautifulSoup4, existing Skill_Seekers architecture
---
## Task 1: Add Multi-Variant Detection
**Files:**
- Modify: `cli/llms_txt_detector.py`
- Test: `tests/test_llms_txt_detector.py`
**Step 1: Write failing test for detect_all() method**
```python
# tests/test_llms_txt_detector.py (add new test)
def test_detect_all_variants():
"""Test detecting all llms.txt variants"""
from unittest.mock import patch, Mock
detector = LlmsTxtDetector("https://hono.dev/docs")
with patch('cli.llms_txt_detector.requests.head') as mock_head:
# Mock responses for different variants
def mock_response(url, **kwargs):
response = Mock()
# All 3 variants exist for Hono
if 'llms-full.txt' in url or 'llms.txt' in url or 'llms-small.txt' in url:
response.status_code = 200
else:
response.status_code = 404
return response
mock_head.side_effect = mock_response
variants = detector.detect_all()
assert len(variants) == 3
assert any(v['variant'] == 'full' for v in variants)
assert any(v['variant'] == 'standard' for v in variants)
assert any(v['variant'] == 'small' for v in variants)
assert all('url' in v for v in variants)
```
**Step 2: Run test to verify it fails**
Run: `source .venv/bin/activate && pytest tests/test_llms_txt_detector.py::test_detect_all_variants -v`
Expected: FAIL with "AttributeError: 'LlmsTxtDetector' object has no attribute 'detect_all'"
**Step 3: Implement detect_all() method**
```python
# cli/llms_txt_detector.py (add new method)
def detect_all(self) -> List[Dict[str, str]]:
"""
Detect all available llms.txt variants.
Returns:
List of dicts with 'url' and 'variant' keys for each found variant
"""
found_variants = []
for filename, variant in self.VARIANTS:
parsed = urlparse(self.base_url)
root_url = f"{parsed.scheme}://{parsed.netloc}"
url = f"{root_url}/{filename}"
if self._check_url_exists(url):
found_variants.append({
'url': url,
'variant': variant
})
return found_variants
```
**Step 4: Add import for List and Dict at top of file**
```python
# cli/llms_txt_detector.py (add to imports)
from typing import Optional, Dict, List
```
**Step 5: Run test to verify it passes**
Run: `source .venv/bin/activate && pytest tests/test_llms_txt_detector.py::test_detect_all_variants -v`
Expected: PASS
**Step 6: Commit**
```bash
git add cli/llms_txt_detector.py tests/test_llms_txt_detector.py
git commit -m "feat: add detect_all() for multi-variant detection"
```
---
## Task 2: Add File Extension Renaming to Downloader
**Files:**
- Modify: `cli/llms_txt_downloader.py`
- Test: `tests/test_llms_txt_downloader.py`
**Step 1: Write failing test for get_proper_filename() method**
```python
# tests/test_llms_txt_downloader.py (add new test)
def test_get_proper_filename():
"""Test filename conversion from .txt to .md"""
downloader = LlmsTxtDownloader("https://hono.dev/llms-full.txt")
filename = downloader.get_proper_filename()
assert filename == "llms-full.md"
assert not filename.endswith('.txt')
def test_get_proper_filename_standard():
"""Test standard variant naming"""
downloader = LlmsTxtDownloader("https://hono.dev/llms.txt")
filename = downloader.get_proper_filename()
assert filename == "llms.md"
def test_get_proper_filename_small():
"""Test small variant naming"""
downloader = LlmsTxtDownloader("https://hono.dev/llms-small.txt")
filename = downloader.get_proper_filename()
assert filename == "llms-small.md"
```
**Step 2: Run test to verify it fails**
Run: `source .venv/bin/activate && pytest tests/test_llms_txt_downloader.py::test_get_proper_filename -v`
Expected: FAIL with "AttributeError: 'LlmsTxtDownloader' object has no attribute 'get_proper_filename'"
**Step 3: Implement get_proper_filename() method**
```python
# cli/llms_txt_downloader.py (add new method)
def get_proper_filename(self) -> str:
"""
Extract filename from URL and convert .txt to .md
Returns:
Proper filename with .md extension
Examples:
https://hono.dev/llms-full.txt -> llms-full.md
https://hono.dev/llms.txt -> llms.md
https://hono.dev/llms-small.txt -> llms-small.md
"""
# Extract filename from URL
from urllib.parse import urlparse
parsed = urlparse(self.url)
filename = parsed.path.split('/')[-1]
# Replace .txt with .md
if filename.endswith('.txt'):
filename = filename[:-4] + '.md'
return filename
```
**Step 4: Run test to verify it passes**
Run: `source .venv/bin/activate && pytest tests/test_llms_txt_downloader.py::test_get_proper_filename -v`
Expected: PASS (all 3 tests)
**Step 5: Commit**
```bash
git add cli/llms_txt_downloader.py tests/test_llms_txt_downloader.py
git commit -m "feat: add get_proper_filename() for .txt to .md conversion"
```
---
## Task 3: Update _try_llms_txt() to Download All Variants
**Files:**
- Modify: `cli/doc_scraper.py:337-384` (_try_llms_txt method)
- Test: `tests/test_integration.py`
**Step 1: Write failing test for multi-variant download**
```python
# tests/test_integration.py (add to TestFullLlmsTxtWorkflow class)
def test_multi_variant_download(self):
"""Test downloading all 3 llms.txt variants"""
from unittest.mock import patch, Mock
import tempfile
import os
config = {
'name': 'test-multi-variant',
'base_url': 'https://hono.dev/docs'
}
# Mock all 3 variants
sample_full = "# Full\n" + "x" * 1000
sample_standard = "# Standard\n" + "x" * 200
sample_small = "# Small\n" + "x" * 500
with tempfile.TemporaryDirectory() as tmpdir:
with patch('cli.llms_txt_detector.requests.head') as mock_head, \
patch('cli.llms_txt_downloader.requests.get') as mock_get:
# Mock detection (all exist)
mock_head_response = Mock()
mock_head_response.status_code = 200
mock_head.return_value = mock_head_response
# Mock downloads
def mock_download(url, **kwargs):
response = Mock()
response.status_code = 200
if 'llms-full.txt' in url:
response.text = sample_full
elif 'llms-small.txt' in url:
response.text = sample_small
else: # llms.txt
response.text = sample_standard
return response
mock_get.side_effect = mock_download
# Run scraper
scraper = DocumentationScraper(config, dry_run=False)
result = scraper._try_llms_txt()
# Verify all 3 files created
refs_dir = os.path.join(scraper.skill_dir, 'references')
assert os.path.exists(os.path.join(refs_dir, 'llms-full.md'))
assert os.path.exists(os.path.join(refs_dir, 'llms.md'))
assert os.path.exists(os.path.join(refs_dir, 'llms-small.md'))
# Verify content not truncated
with open(os.path.join(refs_dir, 'llms-full.md')) as f:
content = f.read()
assert len(content) == len(sample_full)
```
**Step 2: Run test to verify it fails**
Run: `source .venv/bin/activate && pytest tests/test_integration.py::TestFullLlmsTxtWorkflow::test_multi_variant_download -v`
Expected: FAIL - only one file created, not all 3
**Step 3: Modify _try_llms_txt() to use detect_all()**
```python
# cli/doc_scraper.py (replace _try_llms_txt method, lines 337-384)
def _try_llms_txt(self) -> bool:
"""
Try to use llms.txt instead of HTML scraping.
Downloads ALL available variants and stores with .md extension.
Returns:
True if llms.txt was found and processed successfully
"""
print(f"\n🔍 Checking for llms.txt at {self.base_url}...")
# Check for explicit config URL first
explicit_url = self.config.get('llms_txt_url')
if explicit_url:
print(f"\n📌 Using explicit llms_txt_url from config: {explicit_url}")
downloader = LlmsTxtDownloader(explicit_url)
content = downloader.download()
if content:
# Save with proper .md extension
filename = downloader.get_proper_filename()
filepath = os.path.join(self.skill_dir, "references", filename)
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f" 💾 Saved {filename} ({len(content)} chars)")
# Parse and save pages
parser = LlmsTxtParser(content)
pages = parser.parse()
if pages:
for page in pages:
self.save_page(page)
self.pages.append(page)
self.llms_txt_detected = True
self.llms_txt_variant = 'explicit'
return True
# Auto-detection: Find ALL variants
detector = LlmsTxtDetector(self.base_url)
variants = detector.detect_all()
if not variants:
print("️ No llms.txt found, using HTML scraping")
return False
print(f"✅ Found {len(variants)} llms.txt variant(s)")
# Download ALL variants
downloaded = {}
for variant_info in variants:
url = variant_info['url']
variant = variant_info['variant']
print(f" 📥 Downloading {variant}...")
downloader = LlmsTxtDownloader(url)
content = downloader.download()
if content:
filename = downloader.get_proper_filename()
downloaded[variant] = {
'content': content,
'filename': filename,
'size': len(content)
}
print(f" ✓ {filename} ({len(content)} chars)")
if not downloaded:
print("⚠️ Failed to download any variants, falling back to HTML scraping")
return False
# Save ALL variants to references/
os.makedirs(os.path.join(self.skill_dir, "references"), exist_ok=True)
for variant, data in downloaded.items():
filepath = os.path.join(self.skill_dir, "references", data['filename'])
with open(filepath, 'w', encoding='utf-8') as f:
f.write(data['content'])
print(f" 💾 Saved {data['filename']}")
# Parse LARGEST variant for skill building
largest = max(downloaded.items(), key=lambda x: x[1]['size'])
print(f"\n📄 Parsing {largest[1]['filename']} for skill building...")
parser = LlmsTxtParser(largest[1]['content'])
pages = parser.parse()
if not pages:
print("⚠️ Failed to parse llms.txt, falling back to HTML scraping")
return False
print(f" ✓ Parsed {len(pages)} sections")
# Save pages for skill building
for page in pages:
self.save_page(page)
self.pages.append(page)
self.llms_txt_detected = True
self.llms_txt_variants = list(downloaded.keys())
return True
```
**Step 4: Add llms_txt_variants attribute to __init__**
```python
# cli/doc_scraper.py (in __init__ method, after llms_txt_variant line)
self.llms_txt_variants = [] # Track all downloaded variants
```
**Step 5: Run test to verify it passes**
Run: `source .venv/bin/activate && pytest tests/test_integration.py::TestFullLlmsTxtWorkflow::test_multi_variant_download -v`
Expected: PASS
**Step 6: Commit**
```bash
git add cli/doc_scraper.py tests/test_integration.py
git commit -m "feat: download all llms.txt variants with proper .md extension"
```
---
## Task 4: Remove Content Truncation
**Files:**
- Modify: `cli/doc_scraper.py:714-730` (create_reference_file method)
**Step 1: Write failing test for no truncation**
```python
# tests/test_integration.py (add new test)
def test_no_content_truncation():
"""Test that content is NOT truncated in reference files"""
from unittest.mock import Mock
import tempfile
import os
config = {
'name': 'test-no-truncate',
'base_url': 'https://example.com/docs'
}
# Create scraper with long content
scraper = DocumentationScraper(config, dry_run=False)
# Create page with content > 2500 chars
long_content = "x" * 5000
long_code = "y" * 1000
pages = [{
'title': 'Long Page',
'url': 'https://example.com/long',
'content': long_content,
'code_samples': [
{'code': long_code, 'language': 'python'}
],
'headings': []
}]
# Create reference file
scraper.create_reference_file('test', pages)
# Verify no truncation
ref_file = os.path.join(scraper.skill_dir, 'references', 'test.md')
with open(ref_file, 'r') as f:
content = f.read()
assert long_content in content # Full content included
assert long_code in content # Full code included
assert '[Content truncated]' not in content
assert '...' not in content or content.count('...') == 0
```
**Step 2: Run test to verify it fails**
Run: `source .venv/bin/activate && pytest tests/test_integration.py::test_no_content_truncation -v`
Expected: FAIL - content contains "[Content truncated]" or "..."
**Step 3: Remove truncation from create_reference_file()**
```python
# cli/doc_scraper.py (modify create_reference_file method, lines 712-731)
# OLD (line 714-716):
# if page.get('content'):
# content = page['content'][:2500]
# if len(page['content']) > 2500:
# content += "\n\n*[Content truncated]*"
# NEW (replace with):
if page.get('content'):
content = page['content'] # NO TRUNCATION
lines.append(content)
lines.append("")
# OLD (line 728-730):
# lines.append(code[:600])
# if len(code) > 600:
# lines.append("...")
# NEW (replace with):
lines.append(code) # NO TRUNCATION
# No "..." suffix
```
**Complete replacement of lines 712-731:**
```python
# cli/doc_scraper.py:712-731 (complete replacement)
# Content (NO TRUNCATION)
if page.get('content'):
lines.append(page['content'])
lines.append("")
# Code examples with language (NO TRUNCATION)
if page.get('code_samples'):
lines.append("**Examples:**\n")
for i, sample in enumerate(page['code_samples'][:4], 1):
lang = sample.get('language', 'unknown')
code = sample.get('code', sample if isinstance(sample, str) else '')
lines.append(f"Example {i} ({lang}):")
lines.append(f"```{lang}")
lines.append(code) # Full code, no truncation
lines.append("```\n")
```
**Step 4: Run test to verify it passes**
Run: `source .venv/bin/activate && pytest tests/test_integration.py::test_no_content_truncation -v`
Expected: PASS
**Step 5: Run full test suite to check for regressions**
Run: `source .venv/bin/activate && pytest tests/ -v`
Expected: All 201+ tests pass
**Step 6: Commit**
```bash
git add cli/doc_scraper.py tests/test_integration.py
git commit -m "feat: remove content truncation in reference files"
```
---
## Task 5: Update Documentation
**Files:**
- Modify: `docs/plans/2025-10-24-active-skills-design.md`
- Modify: `CHANGELOG.md`
**Step 1: Update design doc status**
```markdown
# docs/plans/2025-10-24-active-skills-design.md (update header)
**Status:** Phase 1 Implemented ✅
```
**Step 2: Add CHANGELOG entry**
```markdown
# CHANGELOG.md (add new section at top)
## [Unreleased]
### Added - Phase 1: Active Skills Foundation
- Multi-variant llms.txt detection: downloads all 3 variants (full, standard, small)
- Automatic .txt → .md file extension conversion
- No content truncation: preserves complete documentation
- `detect_all()` method for finding all llms.txt variants
- `get_proper_filename()` for correct .md naming
### Changed
- `_try_llms_txt()` now downloads all available variants instead of just one
- Reference files now contain complete content (no 2500 char limit)
- Code samples now include full code (no 600 char limit)
### Fixed
- File extension bug: llms.txt files now saved as .md
- Content loss: 0% truncation (was 36%)
```
**Step 3: Commit**
```bash
git add docs/plans/2025-10-24-active-skills-design.md CHANGELOG.md
git commit -m "docs: update status for Phase 1 completion"
```
---
## Task 6: Manual Verification
**Files:**
- None (manual testing)
**Step 1: Test with Hono config**
Run: `source .venv/bin/activate && python3 cli/doc_scraper.py --config configs/hono.json`
**Expected output:**
```
🔍 Checking for llms.txt at https://hono.dev/docs...
📌 Using explicit llms_txt_url from config: https://hono.dev/llms-full.txt
💾 Saved llms-full.md (319000 chars)
📄 Parsing llms-full.md for skill building...
✓ Parsed 93 sections
✅ Used llms.txt (explicit) - skipping HTML scraping
```
**Step 2: Verify all 3 files exist with correct extensions**
Run: `ls -lah output/hono/references/llms*.md`
Expected:
```
llms-full.md 319k
llms.md 5.4k
llms-small.md 176k
```
**Step 3: Verify no truncation in reference files**
Run: `grep -c "Content truncated" output/hono/references/*.md`
Expected: 0 matches (no truncation messages)
**Step 4: Check file sizes are correct**
Run: `wc -c output/hono/references/llms-full.md`
Expected: Should match original download size (~319k), not reduced to 203k
**Step 5: Verify all tests still pass**
Run: `source .venv/bin/activate && pytest tests/ -v`
Expected: All tests pass (201+)
---
## Completion Checklist
- [ ] Task 1: Multi-variant detection (detect_all)
- [ ] Task 2: File extension renaming (get_proper_filename)
- [ ] Task 3: Download all variants (_try_llms_txt)
- [ ] Task 4: Remove truncation (create_reference_file)
- [ ] Task 5: Update documentation
- [ ] Task 6: Manual verification
- [ ] All tests passing
- [ ] No regressions in existing functionality
---
## Success Criteria
**Technical:**
- ✅ All 3 variants downloaded when available
- ✅ Files saved with .md extension (not .txt)
- ✅ 0% content truncation (was 36%)
- ✅ All existing tests pass
- ✅ New tests cover all changes
**User Experience:**
- ✅ Hono skill has all 3 files: llms-full.md, llms.md, llms-small.md
- ✅ Reference files contain complete documentation
- ✅ No "[Content truncated]" messages in output
---
## Related Skills
- @superpowers:test-driven-development - Used throughout for TDD approach
- @superpowers:verification-before-completion - Used in Task 6 for manual verification
---
## Notes
- This plan implements Phase 1 from `docs/plans/2025-10-24-active-skills-design.md`
- Phase 2 (Catalog System) and Phase 3 (Active Scripts) will be separate plans
- All changes maintain backward compatibility with existing HTML scraping
- File extension fix (.txt → .md) is critical for proper skill functionality
---
## Estimated Time
- Task 1: 15 minutes
- Task 2: 15 minutes
- Task 3: 30 minutes
- Task 4: 20 minutes
- Task 5: 10 minutes
- Task 6: 15 minutes
**Total: ~1.5 hours**