Ctrl K
ComunidadGuías…

CONTRIBUTING.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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.