Thanks for your interest in contributing to Untether! This guide covers everything you need to get started.
This project follows the Contributor Covenant. By participating, you agree to uphold a welcoming, inclusive environment.
- Python 3.12+ —
uv python install 3.14 - uv —
curl -LsSf https://astral.sh/uv/install.sh | sh - At least one agent CLI on PATH for integration testing:
claude,codex,opencode,pi,gemini, oramp
git clone https://github.com/littlebearapps/untether.git
cd untether
uv sync --dev # install with dev dependencies
uv run pytest # verify tests pass
uv run ruff check src tests # verify lint passesFor an editable install (changes take effect immediately):
pipx install -e .untether # starts with your ~/.untether/untether.toml
untether --debug # verbose logging to debug.log
untether doctor # validate config and connectivityUse conventional branch names:
feature/<description>— new featuresfix/<description>— bug fixesdocs/<description>— documentation changesrefactor/<description>— code improvements
Follow Conventional Commits:
feat: add voice note transcription for Pi runner
fix: correct PTY cleanup on session timeout
docs: add OpenCode provider guide
refactor: extract event factory from claude runner
test: add cost tracker budget alert tests
- Python 3.12+ features are encouraged (match/case, type unions with
|, etc.) - Ruff for linting and formatting — run
uv run ruff check src testsbefore committing - Australian English in user-facing text (realise, colour, behaviour, licence)
- Type hints on all public functions
- structlog for logging
- msgspec for JSONL parsing
- anyio for async code (not raw asyncio)
uv run pytest # run all tests
uv run pytest tests/test_claude_control.py -x # run specific test file
uv run pytest -k "test_cost" # run tests matching pattern
uv run pytest --cov # run with coverage reportCoverage threshold: 80% (enforced in pyproject.toml). New features should include tests.
Key test patterns:
- Use stub subprocess runners with fake CLI scripts for engine tests
- Use
FakeTransportprotocol doubles instead of real Telegram clients - Verify the 3-event contract:
StartedEvent→ActionEvent(s)→CompletedEvent - Use pytest + anyio for async tests
uv run ruff check src tests # lint
uv run ruff format src tests # auto-formatTelegram ←→ TelegramPresenter ←→ RunnerBridge ←→ Runner (claude/codex/opencode/pi/gemini/amp)
|
ProgressTracker
- Runners (
src/untether/runners/) — engine-specific subprocess managers - RunnerBridge (
src/untether/runner_bridge.py) — connects runners to the transport - TelegramPresenter (
src/untether/telegram/bridge.py) — renders progress and inline keyboards - Commands (
src/untether/telegram/commands/) — in-chat command handlers
See Architecture for the full breakdown.
- Create
src/untether/runners/myengine.pyextendingJsonlSubprocessRunner - Create
src/untether/schemas/myengine.pywith msgspec structs - Override:
command(),build_args(),translate(),new_state() - Export
BACKEND = EngineBackend(id="myengine", build_runner=..., cli_cmd="myengine") - Register in
pyproject.tomlentry points - Add reference docs in
docs/reference/runners/myengine/ - Add tests mirroring existing runner test patterns
- Fork the repository
- Create a feature branch from
master - Make your changes with tests
- Verify:
uv run pytest && uv run ruff check src tests - Push and open a pull request
- Keep PRs focused — one feature or fix per PR
- Include tests for new functionality
- Update docs if you change user-facing behaviour
- Reference any related issues in the PR description
- Every bug fix PR should reference a GitHub issue
- Create issues for bugs you find, even if you fix them immediately
- Use labels:
bug,enhancement,documentation - Link issues in your PR description:
Fixes #NorCloses #N
- Update
CHANGELOG.mdin your PR if it changes user-facing behaviour - Reference the GitHub issue in the changelog entry:
[#N](https://github.com/littlebearapps/untether/issues/N) - Follow the existing format: version heading, dated, categorised sections
Use GitHub Issues to report bugs or request features. Include:
- Untether version (
untether --version) - Engine and version (e.g.,
claude --version) - Relevant config (redact your bot token!)
- Steps to reproduce
- Expected vs actual behaviour
- Open a discussion on GitHub
- Join the Telegram group