Contributing to Untether
March 8, 2026 · View on GitHub
Thanks for your interest in contributing to Untether! This guide covers everything you need to get started.
Code of Conduct
This project follows the Contributor Covenant. By participating, you agree to uphold a welcoming, inclusive environment.
Getting started
Prerequisites
- 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
Development setup
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 passes
For an editable install (changes take effect immediately):
pipx install -e .
Running Untether locally
untether # starts with your ~/.untether/untether.toml
untether --debug # verbose logging to debug.log
untether doctor # validate config and connectivity
Making changes
Branch naming
Use conventional branch names:
feature/<description>— new featuresfix/<description>— bug fixesdocs/<description>— documentation changesrefactor/<description>— code improvements
Commit messages
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
Code style
- 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)
Testing
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 report
Coverage 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
Linting
uv run ruff check src tests # lint
uv run ruff format src tests # auto-format
Architecture overview
Telegram ←→ 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.
Adding a new engine
- 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
Submitting changes
- 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
Pull request guidelines
- 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
Issue tracking
- 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
Changelog
- 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
Reporting issues
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
Questions?
- Open a discussion on GitHub
- Join the Telegram group