Thanks for your interest in contributing!
- Create a virtual environment (e.g.
python -m venv .venv) and activate it. - Install the project in editable mode with dev dependencies:
python -m pip install -e ".[dev]"Run the test suite with:
pytestTest files follow these naming patterns:
| Pattern | Use for | Examples |
|---|---|---|
test_cli_<topic>.py |
CLI command tests | test_cli_company_get.py, test_cli_error_rendering.py |
test_services_<service>.py |
Service layer tests | test_services_persons_companies_additional_coverage.py |
test_<feature>.py |
Feature/model tests | test_models.py, test_pagination_iterators.py |
test_http_client_*.py |
HTTP client tests | test_http_client_additional_coverage.py |
test_v1_only_*.py |
V1 API-specific tests | test_v1_only_services_additional_coverage.py |
test_integration_*.py |
Integration/smoke tests | test_integration_smoke.py |
For coverage gap tests, append _additional_coverage or _remaining_coverage to the base name.
If you're working on CLI commands, please review the CLI Development Guide for:
- Standard command structure and patterns
- Model serialization best practices
- Testing CLI commands
- Common pitfalls and troubleshooting
Before opening a PR, please run:
ruff format .
ruff check .
mypy affinity
pytestWe recommend enabling pre-commit hooks:
pre-commit installThe MCP server (built on the xaffinity CLI) is also available as a Claude Code plugin. For standalone MCP server usage, see the MCP documentation.
The plugin is distributed via the repository's own marketplace (.claude-plugin/marketplace.json). The plugin source files live in mcp/ but must be assembled into mcp/.claude-plugin/ before publishing.
cd mcp
make pluginThis copies the MCP server files (xaffinity-mcp.sh, tools/, prompts/, etc.) into .claude-plugin/. The copied files are git-ignored.
The mcp-plugin job in .github/workflows/ci.yml automatically builds and validates the plugin structure on every push/PR.
This repo uses PyPI trusted publishing (OIDC) via .github/workflows/release.yml.
Releases are triggered automatically when version files change on main.
- Update version in
pyproject.tomland add release notes toCHANGELOG.md. - Run quality checks locally:
ruff format --check .
ruff check .
mypy affinity
pytest- Commit and push to
main(or merge a PR):
git checkout main
git pull --ff-only
# Make version changes
git add pyproject.toml CHANGELOG.md
git commit -m "Release v0.9.1"
git push origin mainThe release workflow will:
- Detect the version change
- Build and publish to PyPI
- Create a GitHub release
- Create and push the
vX.Y.Ztag
- Update
mcp/VERSIONandmcp/CHANGELOG.md - Run pre-commit (syncs plugin.json and server.meta.json)
- Commit and push to
main
The release workflow will:
- Detect the version change
- Build the plugin and MCPB bundle
- Create a GitHub release
- Create and push the
mcp-vX.Y.Ztag
You can also trigger releases via tags:
# SDK
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z
# MCP
git tag -a mcp-vX.Y.Z -m "MCP vX.Y.Z" && git push origin mcp-vX.Y.ZNotes:
- The workflow enforces
vX.Y.Z==pyproject.tomlversion - MCP tags use
mcp-v*prefix - No PyPI API tokens are stored in GitHub; publishing relies on trusted publisher configuration