Contributing
Thank you for your interest in contributing to pydantic-ai-skills!
Ways to Contribute
- Report bugs - Open an issue describing the problem
- Suggest features - Share ideas for new functionality
- Improve documentation - Fix typos, clarify explanations, add examples
- Share skills - Contribute useful skill examples
- Submit code - Fix bugs or implement features
Development Setup
1. Fork and Clone
git clone https://github.com/YOUR_USERNAME/pydantic-ai-skills.git
cd pydantic-ai-skills
2. Create Virtual Environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
3. Install Development Dependencies
pip install -e ".[dev]"
4. Install Pre-commit Hooks
pre-commit install
Making Changes
1. Create a Branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix
2. Make Your Changes
- Follow existing code style
- Add tests for new functionality
- Update documentation as needed
- Keep commits focused and atomic
3. Run Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=pydantic_ai_skills
# Run specific test
pytest tests/test_capability.py::test_each_skill_becomes_one_deferred_capability
4. Check Code Quality
# Run pre-commit checks
pre-commit run --all-files
# Or run individually
ruff check .
ruff format .
mypy pydantic_ai_skills
Coding Standards
Python Style
- Follow PEP 8
- Use type hints for all functions
- Maximum line length: 120 characters
- Use Ruff for linting and formatting
Documentation
- Add docstrings to all public functions/classes
- Use Google-style docstring format
- Include examples in docstrings when helpful
Example Docstring
def index_libraries(
libraries: Sequence[str | Path],
*,
script_executor: SkillScriptExecutor | None = None,
) -> dict[str, SkillPackage]:
"""Index the bundled files of every skill package in `libraries`.
Scans the immediate child directories of each library for a `SKILL.md`, exactly as
harness's `Skills` does, so the keys of the returned mapping line up with the `id`
of each deferred capability harness produces.
Args:
libraries: Skill-library directories. Non-existent entries are skipped.
script_executor: Executor used for the discovered scripts.
Returns:
Mapping of NFKC-normalized skill name to its `SkillPackage`.
"""
Testing
Writing Tests
- Place tests in
tests/directory - Use pytest for testing
- Aim for high code coverage
- Test edge cases and error conditions
Test Structure
import pytest
from pydantic_ai_skills import SkillsCapability
def test_a_library_is_exposed_as_a_catalog(tmp_path):
"""Each skill package becomes one entry the model can load."""
write_skill(tmp_path, 'demo-skill')
assert SkillsCapability(tmp_path).skill_names == ['demo-skill']
def test_unknown_include_name_is_rejected(tmp_path):
"""A typo in a selection is a configuration error, not a silent no-op."""
write_skill(tmp_path, 'demo-skill')
with pytest.raises(ValueError, match='Unknown skill in include'):
SkillsCapability(tmp_path, include=['nope'])
Every test function needs a docstring — Ruff enforces D103 on tests/ as well as on the package.
Write the docstring to say why the behaviour matters, not to restate the assertion.
Testing against a model
Use FunctionModel to script exact tool calls. TestModel does not work with deferred
capabilities: it calls every tool with synthesized arguments, including load_capability with an id
that does not exist, which exhausts the retry budget. See tests/test_capability.py for worked
examples.
The harness compatibility guard
tests/test_harness_compat.py pins the pydantic-ai-harness behaviour this package depends on —
that Skills.apply() yields Capability leaves, that a leaf's id is its directory name, that
instructions come back as plain strings, and that bundled files are not loaded. harness is on 0.x
and documents that its API may change between minor releases, so keep this file in sync with what
capability.py actually relies on. The same applies to tests/test_pydantic_ai_compat.py for the
private pydantic-ai symbols.
Pull Request Process
1. Update Documentation
- Update README.md if needed
- Add/update docstrings
- Update relevant docs/ pages
2. Label the PR for the release notes
There is no CHANGELOG.md. Release notes are generated from merged PR titles, grouped by
label, as configured in .github/release.yml.
So two things matter:
- The PR title is the changelog entry. Write it for someone reading the releases page, not for the diff.
- Apply one label:
feature,bug, ordependencyto place it in a section;docsorchoreto leave it out of the notes entirely.
3. Create Pull Request
- Write clear PR title and description
- Reference related issues
- Ensure all checks pass
- Request review
PR Template
GitHub fills this in for you from
.github/pull_request_template.md
when you open the PR. Work through the checklist it gives you rather than copying one from here.
One item on it is worth calling out: if you used an AI coding agent, you are attesting that you reviewed its output line by line and stand behind it. Generated code is welcome; unread generated code is not.
Reporting Issues
Open an issue from the issue templates. The bug and feature-request forms prompt for everything needed to act on the report — versions, a minimal reproducible example, expected versus actual behavior for bugs; use case, proposal, and alternatives considered for features.
Usage questions belong in Discussions instead.
Security vulnerabilities are the exception: do not open a public issue. Report them
privately as described in
SECURITY.md.
Community Guidelines
- Be respectful and inclusive
- Follow the Code of Conduct
- Help others learn and grow
- Credit contributors
Questions?
- Open a Discussion
- Join community channels (if available)
- Check existing issues and PRs
License
By contributing, you agree that your contributions will be licensed under the MIT License.