CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing to Jiro Search
Thank you for your interest in contributing to Jiro Search! This guide covers how to contribute engine plugins, report issues, and submit pull requests.
## Quick Start
1. **Fork the repository**
2. **Create a feature branch**: `git checkout -b feat/my-engine`
3. **Make your changes**
4. **Run tests**: `pytest -m "not network"`
5. **Submit a PR**
## Engine Plugin Development
Jiro Search uses a plugin architecture for search engines. You can add new engines without modifying core code.
### Creating a New Engine Plugin
```bash
# Scaffold a new engine
jiro plugins create myengine --author "Your Name"
```
This creates `myengine.py` with a template. Edit it to implement your engine's parsing logic.
### Plugin Structure
```python
class MyEngine(BaseEngine):
name = "myengine"
types = ["web", "images"] # Supported search types
version = "1.0.0"
author = "Your Name"
description = "MyEngine search parser"
homepage = "https://github.com/you/jiro-myengine"
license = "MIT"
min_jiro_version = "0.1.0"
# JSON Schema for engine-specific config
config_schema = {
"type": "object",
"properties": {
"api_key": {"type": "string"},
"region": {"type": "string", "enum": ["us", "eu", "asia"]}
}
}
async def search(self, req: SearchRequest) -> SearchResponse:
# 1. Build request params
params = {"q": req.q, "num": str(min(req.num, 100))}
# 2. Make HTTP request
html, _ = await self.client.get(
"https://myengine.com/search",
engine=self.name,
params=params,
)
# 3. Parse with selectolax
tree = parse_html(html)
blocks = self._first_many(tree, RESULT_BLOCKS)
# 4. Extract results
organic = []
for block in blocks:
result = self._parse_result(block, position)
if result:
organic.append(result)
# 5. Return structured response
return SearchResponse(
search_metadata=self.metadata(req, engine=self.name, cached=False, total_time=0.0),
search_information={"query_displayed": req.q, "organic_results_count": len(organic)},
organic_results=organic,
)
```
### Self-Healing Selectors
Use multiple selector candidates for resilience:
```python
RESULT_BLOCKS = [
"div.result", # Primary
"div.item", # Fallback
"article.post", # Fallback 2
]
TITLE_SELECTORS = ["h3 a", "h2.title a", ".title"]
SNIPPET_SELECTORS = [".snippet", ".summary", "p.description"]
```
The `_first_many()` helper tries each selector until one returns results.
### Testing Your Plugin
```bash
# Test with a live query
jiro search web "test query" --engine myengine
# Validate config
jiro plugins validate myengine --config myengine-config.json
```
### Distributing Plugins
1. **Publish to PyPI**: `pip install jiro-myengine`
2. **GitHub Release**: Users can download and place in `~/.jiro/plugins/`
3. **Jiro Plugin Registry** (future): Submit to the community marketplace
## Code Style
- **Python**: 3.11+ with type hints
- **Formatter**: `ruff format` (line length 100)
- **Linter**: `ruff check`
- **Type Check**: `pyright` or `mypy`
```bash
# Install dev dependencies
pip install -e ".[dev]"
# Run checks
ruff check .
ruff format .
pytest -m "not network"
```
## Pull Request Guidelines
### Before Submitting
- [ ] Tests pass (`pytest -m "not network"`)
- [ ] Code formatted (`ruff format .`)
- [ ] No lint errors (`ruff check .`)
- [ ] Type hints added for new functions
- [ ] Docstrings for public APIs
- [ ] CHANGELOG.md updated (if applicable)
### PR Title Format
```
feat(engine): add support for Brave News search
fix(scraper): handle 429 rate limit on Google
docs: update plugin development guide
refactor(registry): add plugin discovery
```
### Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):
```
type(scope): subject
body (optional)
footer (optional)
```
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`
## Engine Plugin Checklist
When submitting a new engine plugin:
- [ ] Implements `BaseEngine` interface
- [ ] Supports at least `web` search type
- [ ] Uses self-healing selectors
- [ ] Handles pagination (`start`, `num`)
- [ ] Handles `time_range`, `safe`, `language`, `location` params
- [ ] Includes `parser_version` for cache invalidation
- [ ] Has `config_schema` for engine-specific settings
- [ ] Tests with `pytest -m "network"` (if network access available)
- [ ] Documents any special requirements (proxies, API keys, etc.)
## Reporting Issues
### Bug Reports
Include:
- Jiro version (`jiro --version`)
- Python version
- Engine(s) affected
- Minimal reproduction steps
- Expected vs actual behavior
- Error logs (with `--log-level debug`)
### Feature Requests
- Clear use case description
- Why this benefits users
- Implementation approach (if known)
### Security Issues
Report privately via GitHub Security Advisories or email security@jiro-search.dev
## Community
- **GitHub Discussions**: [DevAnimecx/jiro/discussions](https://github.com/DevAnimecx/jiro/discussions) — questions & ideas
- **Issues**: [DevAnimecx/jiro/issues](https://github.com/DevAnimecx/jiro/issues)
- **Security**: GitHub Security Advisories or `security@blackvault.dev`
## License
By contributing, you agree that your contributions will be licensed under the MIT License.
---
**Maintainers**: See [MAINTAINERS.md](MAINTAINERS.md) for release process and governance.