git clone <repo-url>
cd otorepair
uv pip install -e ".[dev]"pytestTests use pytest with pytest-asyncio (async mode is set to auto in pyproject.toml). All tests run without network access or external CLIs.
src/otorepair/
├── cli.py CLI entry point and argument parsing
├── loop.py Main async event loop (monitor → detect → fix → reload)
├── runner.py Subprocess lifecycle management
├── detector.py Two-tier error detection (regex + LLM triage)
├── fixer.py Agent CLI invocation and output streaming
├── backends.py Backend definitions (Claude Code, Cursor Agent)
├── circuit_breaker.py Consecutive failure tracking
├── history.py Persistent fix history (.otorepair/history.json)
├── patterns.py Regex patterns for heuristic matching
└── log.py Verbosity and status output
- Zero external dependencies — stdlib only at runtime. This keeps installation trivial and avoids version conflicts.
- Async throughout — the main loop, process monitoring, and agent invocation all use
asyncioto avoid blocking. - Two-tier detection — cheap regex first, LLM triage only when needed. Keeps latency low and avoids unnecessary API calls.
- Backend-agnostic — the core loop doesn't know which agent CLI it's talking to. Backends define their own commands and output formats.
- Add a new class in
backends.pyimplementing the same interface asClaudeBackend/CursorBackend - Register it in
get_backend() - Add the backend ID to the
--backendchoices incli.py - Add tests in
tests/test_backends.py
- Type hints on all function signatures
- No external linters or formatters are configured — keep code consistent with the existing style
- Prefer
asyncio.subprocessoversubprocessfor anything that runs during the main loop
- Fork the repository and create a feature branch
- Make your changes with clear, focused commits
- Ensure all tests pass (
pytest) - Open a pull request with a description of what changed and why