chore: ignore .bkit/, add CLAUDE.md and update-service.sh

This commit is contained in:
2026-06-04 21:58:36 -04:00
parent b3f7e26675
commit 7e3bc363f9
3 changed files with 103 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# Run all checks (format, lint, type, tests)
just check
# Individual checks
uv run ruff format --check src tests
uv run ruff check src tests
uv run ty check src tests
uv run pytest
# Single test
uv run pytest tests/test_runner_contract.py
# Run directly without install
uv run takopi --help
# Mutation testing
uv run mutmut run
# Docs
just docs-serve
```
## Architecture
Takopi is a Telegram bridge for agent CLIs (Claude Code, Codex, OpenCode, Pi). It polls Telegram, routes messages to agent runners, streams progress back, and embeds resume tokens so sessions can continue.
### Layer stack (top → bottom)
| Layer | Key modules |
|-------|-------------|
| **CLI** | `cli.py` — entry point, config, lock |
| **Plugins** | `plugins.py`, `engines.py`, `transports.py`, `commands.py`, `api.py` |
| **Orchestration** | `router.py` (auto-route by resume token), `scheduler.py` (per-thread FIFO queue), `transport_runtime.py` |
| **Bridge** | `telegram/bridge.py` (poll loop, parse directives), `runner_bridge.py` (progress + final render) |
| **Runner** | `runner.py` (protocol + `JsonlSubprocessRunner`), `runners/*.py`, `schemas/*.py` |
| **Transport** | `transport.py`, `presenter.py`, `telegram/client.py`, `telegram/render.py` |
| **Domain** | `model.py`, `events.py`, `progress.py` |
### Plugin system
Engines, transports, and commands are discovered via Python entrypoints:
- `takopi.engine_backends` — runner backends (id must match entrypoint name)
- `takopi.transport_backends` — transport backends
- `takopi.command_backends` — in-chat command handlers
Public API surface for plugin authors: `takopi.api`. Internal modules should not be imported directly from plugins.
### Runner contract
Every runner must yield events satisfying (enforced by `tests/test_runner_contract.py`):
1. Exactly one `StartedEvent` (first)
2. Exactly one `CompletedEvent` (last)
3. `CompletedEvent.resume == StartedEvent.resume`
Runners extend `JsonlSubprocessRunner` + `ResumeTokenMixin` and implement:
- `build_args(...)` / `stdin_payload(...)` — build subprocess command
- `decode_jsonl(...)` — parse one JSONL line via msgspec
- `translate(...)` — pure function converting engine events to `TakopiEvent`s
- `format_resume()` / `extract_resume()` / `is_resume_line()` — resume codec
### Resume flow
Resume tokens are embedded as inline code in the final Telegram message (e.g., `` `claude --resume abc123` ``). When a user replies to that message, the bridge extracts the token and passes it to the matching runner. Each runner owns its own resume regex and format.
### Thread scheduling
`ThreadScheduler` maintains per-thread FIFO queues. Same-thread jobs run sequentially; different threads run in parallel. The "thread" key is the Telegram message thread / reply chain.
### Adding a runner
See `docs/how-to/add-a-runner.md`. Short version: add `src/takopi/runners/<engine>.py` + `src/takopi/schemas/<engine>.py`, expose `BACKEND = EngineBackend(...)`, add entrypoint in `pyproject.toml`. No changes to bridge or CLI needed.
## Key invariants
- `StartedEvent` must be emitted as soon as the session ID is known (enables early lock acquisition).
- Runners must not invent new event types — translate everything into `StartedEvent`, `ActionEvent`, `CompletedEvent`.
- Schema decoding uses **msgspec** (not pydantic); decoders live in `schemas/`.
- Config lives in `~/.takopi/takopi.toml`; loaded via pydantic-settings.
- Coverage threshold: 81% (`pyproject.toml` → `--cov-fail-under=81`).