chore: import upstream snapshot with attribution
Test Vector Database Adaptors / Test MCP Vector DB Tools (push) Has been cancelled
Tests / Code Quality (Ruff & Mypy) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.12) (push) Has been cancelled
Tests / Tests (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers CLI - Convert documentation to AI skills dockerfile:Dockerfile name:skill-seekers]) (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers MCP Server - 25 tools for AI assistants dockerfile:Dockerfile.mcp name:skill-seekers-mcp]) (push) Has been cancelled
Docker Publish / Test Docker Images (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.10) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / Serial / Integration / E2E Tests (push) Has been cancelled
Tests / MCP Server Tests (push) Has been cancelled
Test Vector Database Adaptors / Test chroma Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test faiss Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test qdrant Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test weaviate Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test MCP Vector DB Tools (push) Has been cancelled
Tests / Code Quality (Ruff & Mypy) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.12) (push) Has been cancelled
Tests / Tests (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers CLI - Convert documentation to AI skills dockerfile:Dockerfile name:skill-seekers]) (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers MCP Server - 25 tools for AI assistants dockerfile:Dockerfile.mcp name:skill-seekers-mcp]) (push) Has been cancelled
Docker Publish / Test Docker Images (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.10) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / Serial / Integration / E2E Tests (push) Has been cancelled
Tests / MCP Server Tests (push) Has been cancelled
Test Vector Database Adaptors / Test chroma Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test faiss Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test qdrant Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test weaviate Adaptor (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,898 @@
|
||||
# API Reference - Programmatic Usage
|
||||
|
||||
**Version:** 3.7.0
|
||||
**Last Updated:** 2026-06-11
|
||||
**Status:** ✅ Verified against v3.7.0 (every import and signature in this document was checked by importing it)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Skill Seekers can be used programmatically for integration into other tools, automation scripts, and CI/CD pipelines. This guide covers the Python APIs available for developers who want to embed Skill Seekers functionality into their own applications.
|
||||
|
||||
> **Stability note — read this first**
|
||||
>
|
||||
> The **stable, supported interface of the PyPI package is the `skill-seekers` CLI** (and the MCP server). The Python API documented here is real and importable — it is the same code the CLI runs — but it tracks the implementation: module paths, signatures, and config-dict keys may change between minor releases. **Semver guarantees do not extend to these internals.** If you import these modules, pin an exact version (`skill-seekers==3.7.0`) and re-verify on upgrade.
|
||||
|
||||
**Use Cases:**
|
||||
- Automated documentation skill generation in CI/CD
|
||||
- Batch processing multiple documentation sources
|
||||
- Custom skill generation workflows
|
||||
- Integration with internal tooling
|
||||
- Automated skill updates on documentation changes
|
||||
|
||||
Each example below is marked **[offline]** (no network, no AI), **[network]** (fetches remote content), or **[AI]** (calls an LLM API or spawns a local agent).
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Basic Installation
|
||||
|
||||
```bash
|
||||
pip install skill-seekers
|
||||
```
|
||||
|
||||
### With Platform Dependencies
|
||||
|
||||
```bash
|
||||
# Google Gemini support
|
||||
pip install skill-seekers[gemini]
|
||||
|
||||
# OpenAI ChatGPT support
|
||||
pip install skill-seekers[openai]
|
||||
|
||||
# All LLM platform support
|
||||
pip install skill-seekers[all-llms]
|
||||
|
||||
# Everything (all source types + platforms, except video-full)
|
||||
pip install skill-seekers[all]
|
||||
```
|
||||
|
||||
### Development Installation
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yusufkaraaslan/Skill_Seekers.git
|
||||
cd Skill_Seekers
|
||||
pip install -e ".[all-llms]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core APIs
|
||||
|
||||
### 1. Skill Conversion API (`get_converter`)
|
||||
|
||||
The primary programmatic entry point mirrors the `skill-seekers create` command: a factory returns a `SkillConverter` for any of the 18 source types, and `run()` executes the full extract → build pipeline.
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.skill_converter import get_converter, CONVERTER_REGISTRY
|
||||
|
||||
# get_converter(source_type: str, config: dict[str, Any]) -> SkillConverter
|
||||
# SkillConverter.run() -> int (0 = success, non-zero = failure)
|
||||
|
||||
print(sorted(CONVERTER_REGISTRY))
|
||||
# ['asciidoc', 'chat', 'config', 'confluence', 'epub', 'github', 'html',
|
||||
# 'jupyter', 'local', 'manpage', 'notion', 'openapi', 'pdf', 'pptx',
|
||||
# 'rss', 'video', 'web', 'word']
|
||||
```
|
||||
|
||||
#### Basic Usage — web documentation **[network]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.skill_converter import get_converter
|
||||
|
||||
config = {
|
||||
"name": "django",
|
||||
"description": "Use when working with Django web framework",
|
||||
"base_url": "https://docs.djangoproject.com/en/5.0/",
|
||||
"selectors": {"main_content": "article", "title": "h1", "code_blocks": "pre code"},
|
||||
"url_patterns": {"include": ["/en/5.0/"], "exclude": []},
|
||||
"max_pages": 50,
|
||||
"rate_limit": 0.5,
|
||||
"output_dir": "output/django",
|
||||
}
|
||||
|
||||
converter = get_converter("web", config)
|
||||
exit_code = converter.run() # scrapes, then builds output/django/SKILL.md
|
||||
print("ok" if exit_code == 0 else "failed")
|
||||
```
|
||||
|
||||
#### Template method contract
|
||||
|
||||
`run()` is a template method on the `SkillConverter` base class:
|
||||
|
||||
1. `extract()` — source-specific extraction (scrape, parse, clone, …)
|
||||
2. `build_skill()` — categorize content and write `SKILL.md` + `references/`
|
||||
|
||||
`run()` **returns an exit code instead of raising**: exceptions from `extract()`/`build_skill()` are logged and converted to return value `1`. Check the return value, not a `try/except`.
|
||||
|
||||
```python
|
||||
converter = get_converter("pdf", {"name": "manual", "pdf_path": "manual.pdf"})
|
||||
|
||||
# Reuse existing on-disk extracted data (skip extraction, rebuild only):
|
||||
converter.skip_scrape = True # run() checks this attribute
|
||||
converter.run()
|
||||
```
|
||||
|
||||
#### Factory errors **[offline]**
|
||||
|
||||
- `ValueError` — unknown source type (message lists supported types)
|
||||
- `RuntimeError` — the source type's optional dependency is not installed (message includes the `pip install` hint)
|
||||
|
||||
#### Unified config through the factory **[offline construction]**
|
||||
|
||||
The `"config"` source type wraps the multi-source `UnifiedScraper` (section 4) behind the same factory. It takes the **factory-shaped dict** — only `config_path` is required:
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.skill_converter import get_converter
|
||||
|
||||
scraper = get_converter("config", {
|
||||
"config_path": "configs/unified/react-unified.json",
|
||||
"output_dir": "output/react-complete", # optional override
|
||||
"merge_mode": "rule-based", # optional: 'rule-based' | 'claude-enhanced'
|
||||
"dry_run": True, # optional: preview sources, write nothing
|
||||
})
|
||||
scraper.run()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Source Detection API
|
||||
|
||||
`SourceDetector` is what `skill-seekers create` uses to auto-detect the source type from a raw input string. It returns a `SourceInfo` dataclass.
|
||||
|
||||
#### Basic Usage **[offline]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.source_detector import SourceDetector
|
||||
|
||||
detector = SourceDetector()
|
||||
|
||||
# detect(source: str) -> SourceInfo
|
||||
info = detector.detect("https://docs.djangoproject.com/")
|
||||
print(info.type) # 'web'
|
||||
print(info.parsed) # {'url': 'https://docs.djangoproject.com/'}
|
||||
print(info.suggested_name) # 'djangoproject'
|
||||
print(info.raw_input) # original input string
|
||||
|
||||
detector.detect("fastapi/fastapi").type # 'github' -> parsed: {'repo': 'fastapi/fastapi'}
|
||||
detector.detect("./manual.pdf").type # 'pdf' -> parsed: {'file_path': './manual.pdf'}
|
||||
detector.detect("./my-project").type # 'local' -> parsed: {'directory': '/abs/path/my-project'}
|
||||
detector.detect("configs/react.json").type # 'config' -> parsed: {'config_path': 'configs/react.json'}
|
||||
```
|
||||
|
||||
`SourceInfo` fields: `type`, `parsed` (dict, shape depends on `type`), `suggested_name`, `raw_input`.
|
||||
|
||||
Note: local-directory detection requires the path to exist on disk — a non-existent `./name` falls through to other detectors (e.g. `owner/repo` GitHub shorthand).
|
||||
|
||||
#### Detect-then-convert pipeline **[network for web/github]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.source_detector import SourceDetector
|
||||
from skill_seekers.cli.skill_converter import get_converter
|
||||
|
||||
info = SourceDetector().detect("./manual.pdf")
|
||||
|
||||
config = {
|
||||
"name": info.suggested_name,
|
||||
"pdf_path": info.parsed["file_path"],
|
||||
"output_dir": f"output/{info.suggested_name}",
|
||||
}
|
||||
get_converter(info.type, config).run()
|
||||
```
|
||||
|
||||
(The CLI's `create_command.py:_build_config()` is the canonical mapping from `SourceInfo.parsed` to each converter's config keys.)
|
||||
|
||||
---
|
||||
|
||||
### 3. Direct Converter Construction
|
||||
|
||||
Every converter class can be constructed directly with a config dict (the factory does nothing more than registry lookup + optional-dependency check). The config keys below are read by each converter's `__init__` and are verified against v3.7.0.
|
||||
|
||||
#### PDF — `PDFToSkillConverter` **[offline — local file processing]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.pdf_scraper import PDFToSkillConverter
|
||||
|
||||
converter = PDFToSkillConverter({
|
||||
"name": "product-manual", # required
|
||||
"pdf_path": "manual.pdf", # path to the PDF
|
||||
"description": "Product manual reference", # optional
|
||||
"output_dir": "output/product-manual", # optional (default: output/<name>)
|
||||
"extract_options": { # optional
|
||||
"chunk_size": 10, # pages per chunk
|
||||
"min_quality": 5.0, # quality threshold for extracted text
|
||||
"extract_images": True,
|
||||
"min_image_size": 100,
|
||||
},
|
||||
"categories": {}, # optional keyword mapping
|
||||
})
|
||||
converter.run()
|
||||
```
|
||||
|
||||
#### Web — `DocToSkillConverter` **[network]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.doc_scraper import DocToSkillConverter
|
||||
|
||||
converter = DocToSkillConverter({
|
||||
"name": "react", # required
|
||||
"base_url": "https://react.dev/", # required
|
||||
"selectors": {"main_content": "article", "title": "h1", "code_blocks": "pre code"},
|
||||
"url_patterns": {"include": ["/learn", "/reference"], "exclude": ["/blog"]},
|
||||
"categories": {}, # optional; smart categorization fills the gap
|
||||
"rate_limit": 0.5, # seconds between requests
|
||||
"max_pages": 200, # -1 = unlimited
|
||||
"start_urls": [], # optional explicit seed URLs
|
||||
"llms_txt_url": None, # optional llms.txt source
|
||||
"browser": False, # Playwright rendering for JS-heavy sites
|
||||
"workers": 1, # parallel scrape workers
|
||||
"async_mode": False, # asyncio scraping (faster on large sites)
|
||||
"doc_version": "", # stamped into SKILL.md metadata
|
||||
"output_dir": "output/react",
|
||||
})
|
||||
converter.run()
|
||||
```
|
||||
|
||||
Constructor also accepts `dry_run=True` / `resume=True` keyword arguments (or the same keys in the config dict).
|
||||
|
||||
#### GitHub — `GitHubScraper` **[network — GitHub API; set `GITHUB_TOKEN` for higher rate limits]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.github_scraper import GitHubScraper
|
||||
|
||||
converter = GitHubScraper({
|
||||
"repo": "fastapi/fastapi", # required, owner/repo
|
||||
"name": "fastapi", # optional (default: repo short name)
|
||||
"local_repo_path": None, # optional local clone => unlimited analysis, no API limits
|
||||
"include_code": True,
|
||||
"include_issues": True,
|
||||
"max_issues": 100,
|
||||
"max_comments": 0,
|
||||
"issue_labels": [], # filter issues by label
|
||||
"issue_state": "all", # 'open' | 'closed' | 'all'
|
||||
"include_changelog": True,
|
||||
"include_releases": True,
|
||||
"output_dir": "output/fastapi",
|
||||
})
|
||||
converter.run()
|
||||
```
|
||||
|
||||
The remaining 15 converters follow the same pattern; see `CONVERTER_REGISTRY` in `src/skill_seekers/cli/skill_converter.py` for the module/class of each, and each class's `__init__` for its config keys (e.g. `word` reads `docx_path`, `local` reads `directory` + the C3.x `detect_patterns`/`extract_test_examples`/… toggles).
|
||||
|
||||
---
|
||||
|
||||
### 4. Unified Multi-Source Scraping API
|
||||
|
||||
`UnifiedScraper` combines multiple sources (any of the 18 supported types) into a single merged skill. It is itself a `SkillConverter` (registered as source type `"config"`).
|
||||
|
||||
#### Construction forms
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.unified_scraper import UnifiedScraper
|
||||
|
||||
# 1. Path to a unified config JSON file
|
||||
scraper = UnifiedScraper("configs/unified/react-unified.json")
|
||||
|
||||
# 2. Already-loaded unified config dict (name + description required)
|
||||
scraper = UnifiedScraper({"name": "react-complete", "description": "...", "sources": [...]})
|
||||
|
||||
# 3. Factory-shaped dict (what get_converter("config", ...) passes through)
|
||||
scraper = UnifiedScraper({"config_path": "configs/unified/react-unified.json"})
|
||||
|
||||
# Keyword overrides (win over the config file's values)
|
||||
scraper = UnifiedScraper(
|
||||
"configs/unified/react-unified.json",
|
||||
merge_mode="rule-based", # or 'claude-enhanced' (AI merge)
|
||||
output_dir="output/react-complete",
|
||||
dry_run=False,
|
||||
)
|
||||
```
|
||||
|
||||
#### Running **[network — scrapes each source; AI if merge_mode='claude-enhanced']**
|
||||
|
||||
```python
|
||||
scraper = UnifiedScraper("configs/unified/react-unified.json")
|
||||
scraper.run() # scrape all sources -> merge -> detect conflicts -> build skill
|
||||
```
|
||||
|
||||
#### Dry run preview **[offline]**
|
||||
|
||||
```python
|
||||
UnifiedScraper("configs/unified/react-unified.json", dry_run=True).run()
|
||||
# Logs the sources that WOULD be scraped and the output directory; writes nothing.
|
||||
```
|
||||
|
||||
#### Conflict detection
|
||||
|
||||
Conflict detection is a **method on the instance**, not a module-level function. It is called automatically by `run()` after merging; you can also drive the phases manually:
|
||||
|
||||
```python
|
||||
scraper = UnifiedScraper("configs/unified/react-unified.json")
|
||||
scraper.scrape_all_sources() # [network]
|
||||
merged = scraper.merge_sources()
|
||||
conflicts = scraper.detect_conflicts() # -> list of conflict records
|
||||
scraper.build_skill(merged)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Skill Packaging API
|
||||
|
||||
Package skills for different platforms using the adaptor architecture (Strategy + Factory).
|
||||
|
||||
#### Basic Packaging **[offline]**
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor, ADAPTORS
|
||||
|
||||
# get_adaptor(platform: str, config: dict = None) -> SkillAdaptor
|
||||
print(sorted(ADAPTORS))
|
||||
# ['atlas', 'chroma', 'claude', 'deepseek', 'faiss', 'fireworks', 'gemini',
|
||||
# 'haystack', 'ibm-bob', 'kimi', 'langchain', 'llama-index', 'markdown',
|
||||
# 'minimax', 'openai', 'opencode', 'openrouter', 'pinecone', 'qdrant',
|
||||
# 'qwen', 'together', 'weaviate']
|
||||
|
||||
adaptor = get_adaptor("claude")
|
||||
|
||||
# package(skill_dir: Path, output_path: Path, ...) -> Path
|
||||
package_path = adaptor.package(Path("output/react"), Path("output"))
|
||||
print(package_path) # output/react.zip
|
||||
```
|
||||
|
||||
`get_adaptor` raises `ValueError` for an unknown platform, and `ImportError` if the platform's optional dependency is missing (with an install hint).
|
||||
|
||||
#### Packaging with chunking (RAG/vector targets) **[offline]**
|
||||
|
||||
```python
|
||||
package_path = adaptor.package(
|
||||
Path("output/react"),
|
||||
Path("output"),
|
||||
enable_chunking=True, # split content into token-bounded chunks
|
||||
chunk_max_tokens=512,
|
||||
preserve_code_blocks=True, # never split inside a code fence
|
||||
chunk_overlap_tokens=50,
|
||||
)
|
||||
```
|
||||
|
||||
#### Multi-Platform Packaging **[offline]**
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
|
||||
for platform in ["claude", "gemini", "openai", "markdown"]:
|
||||
adaptor = get_adaptor(platform)
|
||||
pkg = adaptor.package(Path("output/react"), Path("output"))
|
||||
print(f"{platform}: {pkg}")
|
||||
```
|
||||
|
||||
#### Formatting and capability checks **[offline]**
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
from skill_seekers.cli.adaptors.base import SkillAdaptor, SkillMetadata
|
||||
|
||||
adaptor = get_adaptor("claude")
|
||||
|
||||
adaptor.PLATFORM # 'claude'
|
||||
adaptor.supports_upload() # True
|
||||
adaptor.supports_enhancement() # True
|
||||
adaptor.get_env_var_name() # 'ANTHROPIC_API_KEY'
|
||||
|
||||
# format_skill_md(skill_dir: Path, metadata: SkillMetadata) -> str
|
||||
meta = SkillMetadata(name="my-skill", description="When to use this skill")
|
||||
text = adaptor.format_skill_md(Path("output/my-skill"), meta)
|
||||
```
|
||||
|
||||
`SkillMetadata` fields: `name`, `description`, `version` (default `"1.0.0"`), `doc_version`, `author`, `tags`.
|
||||
|
||||
#### Shared Embedding Methods
|
||||
|
||||
The base `SkillAdaptor` class provides two shared embedding helpers inherited by all vector database adaptors (chroma, weaviate, pinecone, qdrant, faiss):
|
||||
|
||||
- `_generate_openai_embeddings(texts, model)` — generate embeddings via the OpenAI API. **[network]**
|
||||
- `_generate_st_embeddings(texts, model)` — generate embeddings using a local sentence-transformers model. **[offline]**
|
||||
|
||||
These are underscore-prefixed (internal) but shared deliberately, so vector adaptors do not re-implement embedding logic.
|
||||
|
||||
---
|
||||
|
||||
### 6. Skill Upload API
|
||||
|
||||
Upload packaged skills to LLM platforms via their APIs. Signature on the base class:
|
||||
|
||||
```python
|
||||
# upload(package_path: Path, api_key: str, **kwargs) -> dict[str, Any]
|
||||
```
|
||||
|
||||
The returned dict's keys are **platform-specific** — inspect the concrete adaptor's `upload()` (e.g. `src/skill_seekers/cli/adaptors/claude.py`) for the exact shape. Check `adaptor.supports_upload()` first: adaptors that don't support upload (e.g. `markdown`) return a result dict with `"success": False` and an explanatory `"message"` instead of uploading.
|
||||
|
||||
#### Claude AI Upload **[network — Anthropic API]**
|
||||
|
||||
```python
|
||||
import os
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
|
||||
adaptor = get_adaptor("claude")
|
||||
result = adaptor.upload(
|
||||
Path("output/react.zip"),
|
||||
api_key=os.environ["ANTHROPIC_API_KEY"],
|
||||
)
|
||||
```
|
||||
|
||||
#### Google Gemini Upload **[network — requires `pip install skill-seekers[gemini]`]**
|
||||
|
||||
```python
|
||||
adaptor = get_adaptor("gemini")
|
||||
result = adaptor.upload(Path("output/react.tar.gz"), api_key=os.environ["GOOGLE_API_KEY"])
|
||||
```
|
||||
|
||||
#### OpenAI Upload **[network — requires `pip install skill-seekers[openai]`]**
|
||||
|
||||
```python
|
||||
adaptor = get_adaptor("openai")
|
||||
result = adaptor.upload(Path("output/react-openai.zip"), api_key=os.environ["OPENAI_API_KEY"])
|
||||
```
|
||||
|
||||
Use `adaptor.get_env_var_name()` to discover which environment variable a platform conventionally reads, and `adaptor.validate_api_key(key)` for a cheap format check before uploading.
|
||||
|
||||
---
|
||||
|
||||
### 7. AI Enhancement API
|
||||
|
||||
Enhance skills with AI-powered improvements. All API-mode enhancement routes
|
||||
through the shared `AgentClient` (`skill_seekers.cli.agent_client`), which
|
||||
centralizes provider selection (Anthropic/Gemini/OpenAI/Moonshot), model and
|
||||
base-URL overrides, the truncation gate, timeout policy, and atomic
|
||||
backup-then-save of SKILL.md.
|
||||
|
||||
#### API Mode Enhancement (per-platform adaptor) **[AI — provider API call]**
|
||||
|
||||
```python
|
||||
import os
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
|
||||
adaptor = get_adaptor('claude') # also: gemini, openai, and OpenAI-compatible targets
|
||||
|
||||
# Enhance SKILL.md via the platform's API (returns True on success).
|
||||
# The original is backed up to SKILL.md.backup and the save is atomic.
|
||||
ok = adaptor.enhance(
|
||||
Path('output/react/'),
|
||||
os.getenv('ANTHROPIC_API_KEY'),
|
||||
)
|
||||
```
|
||||
|
||||
#### Direct AgentClient usage **[AI]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.agent_client import AgentClient
|
||||
|
||||
client = AgentClient(mode='api') # or mode='local' (spawns a local agent)
|
||||
reply = client.call('Summarize this skill...', timeout=600)
|
||||
```
|
||||
|
||||
`AgentClient(mode='auto'|'api'|'local', agent=None, api_key=None, provider=None, base_url=None, model=None)`; `call(prompt, max_tokens=4096, timeout=None, output_file=None, cwd=None, system=None, temperature=None) -> str | None`. Also: `is_available()`, `get_model()`, `detect_api_key()`.
|
||||
|
||||
#### LOCAL Mode Enhancement (local coding agent, free) **[AI — spawns local agent]**
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.enhance_skill_local import LocalSkillEnhancer
|
||||
|
||||
enhancer = LocalSkillEnhancer(
|
||||
'output/react/',
|
||||
agent='claude', # claude, codex, copilot, opencode, kimi, custom
|
||||
)
|
||||
enhancer.run(background=True) # or headless=True (default), daemon=True
|
||||
```
|
||||
|
||||
Monitor background runs from the CLI:
|
||||
|
||||
```bash
|
||||
skill-seekers enhance-status output/react/ --watch
|
||||
```
|
||||
|
||||
> LOCAL mode sets `SKILL_SEEKER_ENHANCE_ACTIVE=1` in the spawned agent's
|
||||
> environment and refuses to start when it is already set, preventing
|
||||
> recursive agent spawns.
|
||||
|
||||
---
|
||||
|
||||
### 8. Execution Context
|
||||
|
||||
`ExecutionContext` is the centralized, pydantic-validated settings singleton the CLI builds from argparse + config files. Converters and enhancement read from it; programmatic callers can initialize and override it.
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.execution_context import ExecutionContext
|
||||
|
||||
# Classmethods:
|
||||
# initialize(args=None, config_path=None, source_info=None) -> ExecutionContext
|
||||
# get() -> ExecutionContext (active override, else base singleton)
|
||||
# is_initialized() -> bool
|
||||
# reset() -> None (mainly for tests)
|
||||
|
||||
ExecutionContext.is_initialized() # False until initialize() is called
|
||||
ctx = ExecutionContext.initialize() # defaults when args is None
|
||||
|
||||
ctx.enhancement.level # 2
|
||||
ctx.scraping.max_pages # -1 (unlimited)
|
||||
ctx.output.output_dir # None
|
||||
ctx.analysis.depth # 'surface'
|
||||
```
|
||||
|
||||
#### Temporary overrides (context manager) **[offline]**
|
||||
|
||||
`override(**kwargs)` is a context manager; double-underscore keys address nested settings groups (`source`, `enhancement`, `output`, `scraping`, `analysis`). Overrides are **context-local** (stored in a `contextvars.ContextVar`), so concurrent asyncio tasks each see only their own override, and nested overrides stack and unwind cleanly:
|
||||
|
||||
```python
|
||||
ctx = ExecutionContext.get()
|
||||
|
||||
with ctx.override(enhancement__level=3, scraping__max_pages=100):
|
||||
active = ExecutionContext.get()
|
||||
assert active.enhancement.level == 3 # inside: overridden
|
||||
|
||||
assert ExecutionContext.get().enhancement.level == 2 # outside: restored
|
||||
```
|
||||
|
||||
Caveat: contextvars flow into asyncio tasks automatically but into worker threads only via `contextvars.copy_context().run(...)` — a bare `threading.Thread` sees the base singleton, not your override.
|
||||
|
||||
---
|
||||
|
||||
### 9. Services Layer (`skill_seekers.services`)
|
||||
|
||||
Domain logic shared by the CLI and the MCP server. Importable **without** the `[mcp]` extra. Import from the submodules:
|
||||
|
||||
```python
|
||||
from skill_seekers.services.marketplace_manager import MarketplaceManager
|
||||
from skill_seekers.services.source_manager import SourceManager
|
||||
from skill_seekers.services.config_publisher import ConfigPublisher, detect_category
|
||||
from skill_seekers.services.git_repo import GitConfigRepo
|
||||
```
|
||||
|
||||
#### Marketplace registry CRUD **[offline — local registry file]**
|
||||
|
||||
```python
|
||||
mm = MarketplaceManager() # or MarketplaceManager(config_dir="~/.skill-seekers")
|
||||
mm.list_marketplaces() # -> list[dict]; also: add/get/update/remove_marketplace
|
||||
```
|
||||
|
||||
#### Config source registry CRUD **[offline]**
|
||||
|
||||
```python
|
||||
sm = SourceManager()
|
||||
sm.list_sources() # also: add/get/update/remove_source
|
||||
```
|
||||
|
||||
#### Config category detection **[offline]**
|
||||
|
||||
```python
|
||||
detect_category({"name": "react", "description": "React frontend UI library docs"})
|
||||
# 'web-frameworks' (keyword scoring over CATEGORY_KEYWORDS)
|
||||
```
|
||||
|
||||
#### Git-backed config repositories **[network — clones/pulls]**
|
||||
|
||||
```python
|
||||
repo = GitConfigRepo() # or GitConfigRepo(cache_dir=...)
|
||||
repo.validate_git_url("https://github.com/owner/configs.git") # offline check
|
||||
path = repo.clone_or_pull("https://github.com/owner/configs.git") # [network]
|
||||
configs = repo.find_configs(path)
|
||||
```
|
||||
|
||||
`ConfigPublisher` (`ConfigPublisher(cache_dir=None)`) pushes configs to registered config-source repos; `MarketplacePublisher` publishes packaged skills to plugin-marketplace repos. Both perform git pushes **[network]**.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Objects
|
||||
|
||||
The full config-file schema (single-source and unified) is documented in **[CONFIG_FORMAT.md](CONFIG_FORMAT.md)** — that is the authoritative reference. Summary:
|
||||
|
||||
### Web (single-source) config keys
|
||||
|
||||
These are the keys `DocToSkillConverter` reads (same dict whether loaded from a `configs/*.json` file or built in code):
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `name` | string | *required* | Skill name (alphanumeric + hyphens) |
|
||||
| `base_url` | string | *required* | Documentation website URL |
|
||||
| `description` | string | generated | When to use this skill |
|
||||
| `selectors` | object | `{}` | CSS selectors (`main_content`, `title`, `code_blocks`) |
|
||||
| `url_patterns` | object | `{}` | `include` / `exclude` URL substring lists |
|
||||
| `categories` | object | `{}` | Category keywords mapping |
|
||||
| `rate_limit` | float | `0.5` | Delay between requests (seconds) |
|
||||
| `max_pages` | int | `-1` | Maximum pages to scrape (-1 = unlimited) |
|
||||
| `start_urls` | array | `[]` | Explicit seed URLs |
|
||||
| `llms_txt_url` | string | `null` | URL to llms.txt file |
|
||||
| `async_mode` | bool | `false` | Asyncio scraping (faster on large sites) |
|
||||
| `browser` | bool | `false` | Playwright rendering for JS-heavy sites |
|
||||
| `workers` | int | `1` | Parallel scrape workers |
|
||||
| `output_dir` | string | `output/<name>` | Where the skill is written |
|
||||
|
||||
### Unified Config Schema (Multi-Source)
|
||||
|
||||
Supports all 18 source types: `documentation`, `github`, `pdf`, `local`, `word`, `video`, `epub`, `jupyter`, `html`, `openapi`, `asciidoc`, `pptx`, `rss`, `manpage`, `confluence`, `notion`, `chat`, `config`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "framework-unified",
|
||||
"description": "Complete framework documentation",
|
||||
"merge_mode": "rule-based",
|
||||
"sources": [
|
||||
{
|
||||
"type": "documentation",
|
||||
"base_url": "https://docs.example.com/",
|
||||
"selectors": { "main_content": "article" }
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"repo": "org/repo",
|
||||
"include_code": true
|
||||
},
|
||||
{
|
||||
"type": "pdf",
|
||||
"path": "manual.pdf"
|
||||
},
|
||||
{
|
||||
"type": "openapi",
|
||||
"path": "specs/openapi.yaml"
|
||||
},
|
||||
{
|
||||
"type": "video",
|
||||
"url": "https://www.youtube.com/watch?v=example"
|
||||
},
|
||||
{
|
||||
"type": "jupyter",
|
||||
"path": "notebooks/examples.ipynb"
|
||||
},
|
||||
{
|
||||
"type": "confluence",
|
||||
"base_url": "https://company.atlassian.net/wiki",
|
||||
"space_key": "DOCS"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Configs are validated on load by `skill_seekers.cli.config_validator.validate_config(config_path)`, which the CLI and `UnifiedScraper` call for you.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
The Python API signals failure three different ways — match on the layer you call:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.skill_converter import get_converter
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
|
||||
# 1. Factory-time errors RAISE:
|
||||
try:
|
||||
converter = get_converter("web", config)
|
||||
except ValueError as e: # unknown source type
|
||||
print(e)
|
||||
except RuntimeError as e: # missing optional dependency (includes pip install hint)
|
||||
print(e)
|
||||
|
||||
try:
|
||||
adaptor = get_adaptor("chroma")
|
||||
except ValueError as e: # unknown platform
|
||||
print(e)
|
||||
except ImportError as e: # optional dependency not installed
|
||||
print(e)
|
||||
|
||||
# 2. Conversion errors are RETURN CODES (run() catches and logs exceptions):
|
||||
if converter.run() != 0:
|
||||
raise SystemExit("skill build failed — see log output")
|
||||
|
||||
# 3. Adaptor operations either RAISE (network/API errors during real uploads)
|
||||
# or report failure in the returned dict — gate on capability and check
|
||||
# result["success"]:
|
||||
if adaptor.supports_upload():
|
||||
result = adaptor.upload(Path("output/react.zip"), api_key=key)
|
||||
if not result.get("success"):
|
||||
print(result.get("message"))
|
||||
```
|
||||
|
||||
There is no `skill_seekers.exceptions` module — standard exceptions (`ValueError`, `RuntimeError`, `ImportError`, `FileNotFoundError`) are used throughout.
|
||||
|
||||
---
|
||||
|
||||
## Testing Your Integration
|
||||
|
||||
Use `dry_run` and small `max_pages` limits to keep tests fast and offline-friendly:
|
||||
|
||||
```python
|
||||
from skill_seekers.cli.skill_converter import get_converter
|
||||
from skill_seekers.cli.source_detector import SourceDetector
|
||||
|
||||
|
||||
def test_source_detection(): # [offline]
|
||||
info = SourceDetector().detect("https://docs.example.com/")
|
||||
assert info.type == "web"
|
||||
assert info.parsed["url"] == "https://docs.example.com/"
|
||||
|
||||
|
||||
def test_unified_dry_run(tmp_path): # [offline] — previews without scraping
|
||||
import json
|
||||
cfg = tmp_path / "unified.json"
|
||||
cfg.write_text(json.dumps({
|
||||
"name": "test",
|
||||
"description": "Test skill", # name + description are required
|
||||
"sources": [{"type": "github", "repo": "owner/repo"}],
|
||||
}))
|
||||
scraper = get_converter("config", {"config_path": str(cfg), "dry_run": True})
|
||||
assert scraper.run() == 0
|
||||
|
||||
|
||||
def test_packaging(tmp_path): # [offline]
|
||||
from pathlib import Path
|
||||
from skill_seekers.cli.adaptors import get_adaptor
|
||||
|
||||
skill = tmp_path / "skill"
|
||||
skill.mkdir()
|
||||
(skill / "SKILL.md").write_text("---\nname: t\ndescription: d\n---\n# T\n")
|
||||
pkg = get_adaptor("markdown").package(skill, tmp_path)
|
||||
assert pkg.exists()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- **Async scraping**: set `"async_mode": True` in a web config for 2–3x faster scraping on large sites; `"workers": N` parallelizes the thread-based scraper.
|
||||
- **Rebuild without re-scraping**: set `converter.skip_scrape = True` before `run()` to rebuild `SKILL.md` from existing on-disk extracted data (`output/<name>_data/`).
|
||||
- **Resume**: web configs support checkpointing — pass `resume=True` to `DocToSkillConverter` (or `"resume": True` in the config) to continue an interrupted scrape.
|
||||
- **Batch processing**: converters are independent; run several `get_converter(...).run()` calls in a `ThreadPoolExecutor`. Don't share one `ExecutionContext.override()` across plain threads (see section 8 caveat).
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration Examples
|
||||
|
||||
For pipelines, prefer the CLI — it is the stable interface:
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Generate Skills
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Daily at midnight
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
generate-skills:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Skill Seekers
|
||||
run: pip install skill-seekers[all-llms]
|
||||
|
||||
- name: Generate Skills
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
run: |
|
||||
skill-seekers install --config react --target claude
|
||||
skill-seekers install --config vue --target gemini
|
||||
|
||||
- name: Archive Skills
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: skills
|
||||
path: output/**/*.zip
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
||||
```yaml
|
||||
generate_skills:
|
||||
image: python:3.11
|
||||
script:
|
||||
- pip install skill-seekers[all-llms]
|
||||
- skill-seekers install --config react --target claude
|
||||
- skill-seekers install --config vue --target gemini --no-upload
|
||||
artifacts:
|
||||
paths:
|
||||
- output/
|
||||
only:
|
||||
- schedules
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. **Prefer the CLI for automation; pin the version for Python imports**
|
||||
```bash
|
||||
pip install skill-seekers==3.7.0 # internals can shift between minors
|
||||
```
|
||||
|
||||
### 2. **Use the factory, not hardcoded classes**
|
||||
```python
|
||||
# Good: registry-driven
|
||||
converter = get_converter(info.type, config)
|
||||
adaptor = get_adaptor(target_platform)
|
||||
|
||||
# Brittle: hardcoded imports break when modules move
|
||||
```
|
||||
|
||||
### 3. **Check run() return codes**
|
||||
```python
|
||||
if get_converter("web", config).run() != 0:
|
||||
raise SystemExit(1) # run() logs the exception; it does not re-raise
|
||||
```
|
||||
|
||||
### 4. **Cache scraped data, rebuild cheaply**
|
||||
```python
|
||||
converter = get_converter("web", config)
|
||||
converter.run() # first run: scrape + build (slow)
|
||||
|
||||
converter = get_converter("web", config)
|
||||
converter.skip_scrape = True
|
||||
converter.run() # rebuild from output/<name>_data/ (fast)
|
||||
```
|
||||
|
||||
### 5. **Probe adaptor capabilities before calling**
|
||||
```python
|
||||
adaptor = get_adaptor(platform)
|
||||
if adaptor.supports_upload():
|
||||
adaptor.upload(pkg, api_key=os.environ[adaptor.get_env_var_name()])
|
||||
```
|
||||
|
||||
### 6. **Use dry runs in tests**
|
||||
```python
|
||||
get_converter("config", {"config_path": cfg, "dry_run": True}).run()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference Summary
|
||||
|
||||
| API | Import | Use Case |
|
||||
|-----|--------|----------|
|
||||
| **Skill conversion factory** | `skill_seekers.cli.skill_converter.get_converter` | Any of the 18 source types → skill |
|
||||
| **Converter registry** | `skill_seekers.cli.skill_converter.CONVERTER_REGISTRY` | Source type → (module, class) lookup |
|
||||
| **Source detection** | `skill_seekers.cli.source_detector.SourceDetector` | Auto-detect type from raw input |
|
||||
| **Web docs** | `skill_seekers.cli.doc_scraper.DocToSkillConverter` | Documentation websites |
|
||||
| **GitHub repos** | `skill_seekers.cli.github_scraper.GitHubScraper` | Code + docs + community analysis |
|
||||
| **PDF** | `skill_seekers.cli.pdf_scraper.PDFToSkillConverter` | PDF documents |
|
||||
| **Local codebase** | `skill_seekers.cli.codebase_scraper.CodebaseAnalyzer` | Local directories (C3.x pipeline) |
|
||||
| **Multi-source** | `skill_seekers.cli.unified_scraper.UnifiedScraper` | Merge 18 source types + conflict detection |
|
||||
| **Packaging / upload / enhance** | `skill_seekers.cli.adaptors.get_adaptor` | 22 platform targets |
|
||||
| **AI enhancement** | `skill_seekers.cli.agent_client.AgentClient` | API or local-agent LLM calls |
|
||||
| **Local-agent enhancement** | `skill_seekers.cli.enhance_skill_local.LocalSkillEnhancer` | Free enhancement via coding agents |
|
||||
| **Settings singleton** | `skill_seekers.cli.execution_context.ExecutionContext` | Initialize / get / override settings |
|
||||
| **Marketplace registry** | `skill_seekers.services.marketplace_manager.MarketplaceManager` | Marketplace CRUD |
|
||||
| **Config sources** | `skill_seekers.services.source_manager.SourceManager` | Config source registry CRUD |
|
||||
| **Config publishing** | `skill_seekers.services.config_publisher` | Push configs; `detect_category()` |
|
||||
| **Git config repos** | `skill_seekers.services.git_repo.GitConfigRepo` | Clone/pull + config discovery |
|
||||
|
||||
The other 14 converter classes (word, epub, video, jupyter, html, openapi, asciidoc, pptx, rss, manpage, confluence, notion, chat) are listed in `CONVERTER_REGISTRY`.
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **[Main Documentation](../../README.md)** - Complete user guide
|
||||
- **[CLI Reference](CLI_REFERENCE.md)** - The stable command-line interface
|
||||
- **[Config Format](CONFIG_FORMAT.md)** - Authoritative config schema
|
||||
- **[MCP Setup](../guides/MCP_SETUP.md)** - MCP server integration
|
||||
- **[Multi-LLM Support](../integrations/MULTI_LLM_SUPPORT.md)** - Platform comparison
|
||||
- **[CHANGELOG](../../CHANGELOG.md)** - Version history and API changes
|
||||
|
||||
---
|
||||
|
||||
**Version:** 3.7.0
|
||||
**Last Updated:** 2026-06-11
|
||||
Reference in New Issue
Block a user