docs: restructure docs into diataxis (#121)
This commit is contained in:
+17
-363
@@ -1,372 +1,26 @@
|
||||
# takopi - Developer Guide
|
||||
# Developer guide (moved)
|
||||
|
||||
This document describes the internal architecture and module responsibilities.
|
||||
See `specification.md` for the authoritative behavior spec.
|
||||
This document was split into smaller Diátaxis pages.
|
||||
|
||||
## Development Setup
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# Clone and enter the directory
|
||||
git clone https://github.com/banteg/takopi
|
||||
cd takopi
|
||||
- [Dev setup](how-to/dev-setup.md)
|
||||
|
||||
# Run directly with uv (installs deps automatically)
|
||||
uv run takopi --help
|
||||
## Understanding the codebase
|
||||
|
||||
# Or install locally from the repo to test outside the repo
|
||||
uv tool install .
|
||||
takopi --help
|
||||
- [Architecture](explanation/architecture.md)
|
||||
- [Routing & sessions](explanation/routing-and-sessions.md)
|
||||
- [Module map](explanation/module-map.md)
|
||||
|
||||
# Run tests, linting, type checking
|
||||
uv run pytest
|
||||
uv run ruff check src tests
|
||||
uv run ty check .
|
||||
## Reference
|
||||
|
||||
# Or all at once
|
||||
just check
|
||||
```
|
||||
- [Specification](reference/specification.md)
|
||||
- [Plugin API](reference/plugin-api.md)
|
||||
- [Environment variables](reference/env-vars.md)
|
||||
- [Telegram transport](reference/transports/telegram.md)
|
||||
- [Runners](reference/runners/index.md)
|
||||
|
||||
Takopi runs in **auto-router** mode by default. `default_engine` in `takopi.toml` selects
|
||||
the engine for new threads; engine subcommands override that default for the process.
|
||||
## Extending Takopi
|
||||
|
||||
## Module Responsibilities
|
||||
|
||||
### `runner_bridge.py` - Transport-agnostic orchestration
|
||||
|
||||
The core handler module containing:
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `ExecBridgeConfig` | Frozen dataclass holding transport + presenter config |
|
||||
| `IncomingMessage` | Normalized incoming message shape |
|
||||
| `handle_message()` | Per-message handler with progress updates and final render |
|
||||
| `ProgressEdits` | Throttled progress edit worker |
|
||||
| `RunningTask` | Cancellation + resume coordination for in-flight runs |
|
||||
|
||||
**Key patterns:**
|
||||
- Progress edits are best-effort and only run when new events arrive (Telegram outbox handles rate limiting/coalescing)
|
||||
- Resume tokens are runner-formatted command lines (e.g., `` `codex resume <token>` ``, `` `claude --resume <token>` ``, `` `pi --session <id>` ``)
|
||||
- Resume lines are stripped from the prompt before invoking the runner
|
||||
- Errors/cancellation render final status while preserving resume tokens when known
|
||||
|
||||
### `telegram/bridge.py` - Telegram bridge loop
|
||||
|
||||
The Telegram adapter module containing:
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `TelegramBridgeConfig` | Frozen dataclass holding bot + router + exec config |
|
||||
| `TelegramTransport` | `BotClient` → `Transport` adapter |
|
||||
| `TelegramPresenter` | `ProgressState` → `RenderedMessage` adapter |
|
||||
| `poll_updates()` | Async generator that drains backlog, long-polls updates, filters messages |
|
||||
| `run_main_loop()` | TaskGroup-based main loop that spawns per-message handlers |
|
||||
| `_handle_cancel()` | `/cancel` routing |
|
||||
|
||||
**Key patterns:**
|
||||
- Bridge schedules runs FIFO per thread to avoid concurrent progress messages; runner locks enforce per-thread serialization
|
||||
- `/cancel` routes by reply-to progress message id (accepts extra text)
|
||||
- `/{engine}` on the first line selects the engine for new threads
|
||||
- Resume parsing polls all runners via `AutoRouter.resolve_resume()` and routes to the first match
|
||||
- Bot command menu is synced on startup (`cancel` + engine + project commands, capped at 100)
|
||||
|
||||
### `transport.py` - Transport protocol
|
||||
|
||||
Defines `Transport`, `MessageRef`, `RenderedMessage`, and `SendOptions`.
|
||||
|
||||
### `presenter.py` - Presenter protocol
|
||||
|
||||
Defines a renderer that converts `ProgressState` into `RenderedMessage` outputs.
|
||||
|
||||
### `transport_runtime.py` - Transport runtime facade
|
||||
|
||||
Provides the `TransportRuntime` helper used by transport backends to resolve
|
||||
messages, select runners, and format context without depending on internal types.
|
||||
|
||||
### `transports.py` - Transport backend loading
|
||||
|
||||
Defines the transport backend protocol and entrypoint-backed loading helpers.
|
||||
|
||||
### `config_migrations.py` - Config migrations
|
||||
|
||||
Applies one-time edits to on-disk config (e.g., legacy Telegram key migration) before
|
||||
`TakopiSettings` validation runs.
|
||||
|
||||
### `telegram/backend.py` - Telegram transport backend
|
||||
|
||||
Adapter that validates Telegram config, runs onboarding, and builds/runs the Telegram bridge.
|
||||
|
||||
### `cli.py` - CLI entry point
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `run()` / `main()` | Typer CLI entry points |
|
||||
| `_run_auto_router()` | Loads settings, resolves transport + engine, builds router, delegates to transport backend |
|
||||
|
||||
### `progress.py` - Progress tracking
|
||||
|
||||
| Function/Class | Purpose |
|
||||
|----------------|---------|
|
||||
| `ProgressTracker` | Stateful reducer of takopi events into progress snapshots |
|
||||
| `ProgressState` | Snapshot of actions, resume token, and engine metadata |
|
||||
|
||||
### `markdown.py` - Markdown formatting
|
||||
|
||||
| Function/Class | Purpose |
|
||||
|----------------|---------|
|
||||
| `MarkdownFormatter` | Converts `ProgressState` into MarkdownParts |
|
||||
| `MarkdownPresenter` | `ProgressState` → `RenderedMessage` (markdown text) |
|
||||
| `MarkdownParts` | Header/body/footer building blocks for markdown output |
|
||||
| `assemble_markdown_parts()` | Join MarkdownParts into a single markdown string |
|
||||
| `render_event_cli()` | Format a takopi event for CLI logs |
|
||||
| `format_elapsed()` | Formats seconds as `Xh Ym`, `Xm Ys`, or `Xs` |
|
||||
|
||||
### `telegram/render.py` - Telegram markdown rendering
|
||||
|
||||
| Function/Class | Purpose |
|
||||
|----------------|---------|
|
||||
| `render_markdown()` | Markdown → Telegram text + entities |
|
||||
| `trim_body()` | Trim body to 3500 chars (header/footer preserved) |
|
||||
| `prepare_telegram()` | Trim + render Markdown parts for Telegram |
|
||||
|
||||
### `telegram/client.py` - Telegram API wrapper
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `BotClient` | Protocol defining the bot client interface |
|
||||
| `TelegramClient` | HTTP client for Telegram Bot API (send, edit, delete messages) |
|
||||
|
||||
See `docs/transports/telegram.md` for outbox behavior, rate limiting, and retry rules.
|
||||
|
||||
### `runners/codex.py` - Codex runner
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `CodexRunner` | Spawns `codex exec --json`, streams JSONL, emits takopi events |
|
||||
| `translate_codex_event()` | Normalizes Codex JSONL into the takopi event schema |
|
||||
| `manage_subprocess()` | Starts a new process group and kills it on cancellation (POSIX) |
|
||||
|
||||
**Key patterns:**
|
||||
- Per-resume locks (WeakValueDictionary) prevent concurrent resumes of the same session
|
||||
- Event delivery uses a single internal queue to preserve order without per-event tasks
|
||||
- Stderr is drained into a bounded tail (debug logging only)
|
||||
- Translation errors abort the run; keep event normalization defensive
|
||||
|
||||
### `runners/pi.py` - Pi runner
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `PiRunner` | Spawns `pi --print --mode json`, streams JSONL, emits takopi events |
|
||||
| `translate_pi_event()` | Normalizes Pi JSONL into the takopi event schema |
|
||||
|
||||
### `model.py` / `runner.py` - Core domain types
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `model.py` | Domain types: resume tokens, actions, events, run result |
|
||||
| `runner.py` | Runner protocol + event queue utilities |
|
||||
|
||||
### `backends.py` - Engine backend contracts
|
||||
|
||||
Defines `EngineBackend`, `SetupIssue`, and the `EngineConfig` type used by
|
||||
runner modules.
|
||||
|
||||
### `plugins.py` - Entrypoint discovery
|
||||
|
||||
Centralizes plugin discovery and lazy loading:
|
||||
|
||||
- lists IDs without importing plugin modules
|
||||
- loads a specific entrypoint on demand
|
||||
- captures load errors for diagnostics
|
||||
- filters by enabled list (distribution names)
|
||||
|
||||
### `commands.py` - Command backend loading
|
||||
|
||||
Defines the command backend protocol, command context/executor helpers, and
|
||||
entrypoint-backed loading for slash-command plugins.
|
||||
|
||||
### `ids.py` - Plugin ID validation
|
||||
|
||||
Defines the shared ID regex used for plugin IDs and Telegram command names.
|
||||
|
||||
### `api.py` - Public plugin API
|
||||
|
||||
Re-exports the supported plugin surface from `takopi.api` (stable API boundary).
|
||||
|
||||
### `engines.py` - Engine backend discovery
|
||||
|
||||
Loads engine backends via entrypoints (`takopi.engine_backends`), with lazy loading
|
||||
and enabled list support.
|
||||
|
||||
### `runners/` - Runner implementations
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `codex.py` | Codex runner (JSONL → takopi events) + per-resume locks |
|
||||
| `claude.py` | Claude runner (JSONL → takopi events) + per-resume locks |
|
||||
| `opencode.py` | OpenCode runner (JSONL → takopi events) + per-resume locks |
|
||||
| `pi.py` | Pi runner (JSONL → takopi events) + per-resume locks |
|
||||
| `mock.py` | Mock runner for tests/demos |
|
||||
|
||||
### `schemas/` - JSONL decoding schemas
|
||||
|
||||
Self-documenting msgspec schemas for decoding engine JSONL streams.
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `codex.py` | `codex exec --json` event schemas |
|
||||
| `claude.py` | `claude -p --output-format stream-json --verbose` event schemas |
|
||||
| `opencode.py` | `opencode run --format json` event schemas |
|
||||
| `pi.py` | `pi --print --mode json` event schemas |
|
||||
|
||||
### `utils/` - Utility modules
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `paths.py` | `relativize_path()`, `relativize_command()` helpers |
|
||||
| `streams.py` | `iter_bytes_lines()`, `drain_stderr()` for async stream handling |
|
||||
| `subprocess.py` | `manage_subprocess()`, `terminate_process()`, `kill_process()` |
|
||||
|
||||
### `router.py` - Auto-router
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `AutoRouter` | Resolves resume tokens by polling all runners, routes to matching engine |
|
||||
| `RunnerEntry` | Dataclass holding runner + backend metadata |
|
||||
| `RunnerUnavailableError` | Raised when requested engine is not available |
|
||||
|
||||
### `scheduler.py` - Thread scheduling
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `ThreadScheduler` | Per-thread FIFO job queuing with serialization |
|
||||
| `ThreadJob` | Dataclass representing a queued job |
|
||||
| `note_thread_known()` | Registers a thread as busy when token discovered mid-run |
|
||||
|
||||
### `events.py` - Event factory
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `EventFactory` | Helper class for creating takopi events with consistent engine/resume |
|
||||
| Builder methods | `started()`, `action()`, `action_started()`, `action_updated()`, `action_completed()`, `completed()`, `completed_ok()`, `completed_error()` |
|
||||
|
||||
### `lockfile.py` - Single-instance enforcement
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `acquire_lock()` | Acquire lock for bot token, returns `LockHandle` context manager |
|
||||
| `LockHandle` | Context manager for automatic lock release |
|
||||
| `LockInfo` | Dataclass with `pid` and `token_fingerprint` |
|
||||
| `token_fingerprint()` | SHA256 hash of bot token, truncated to 10 chars |
|
||||
|
||||
### `backends_helpers.py` - Backend utilities
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `install_issue()` | Creates `SetupIssue` with install instructions for missing CLI |
|
||||
|
||||
### `config.py` - Shared configuration errors
|
||||
|
||||
```python
|
||||
class ConfigError(RuntimeError): ...
|
||||
```
|
||||
|
||||
### `settings.py` - Settings loading
|
||||
|
||||
```python
|
||||
def load_settings(path: str | Path | None = None) -> tuple[TakopiSettings, Path]:
|
||||
# Loads ~/.takopi/takopi.toml (TOML + env), validates via pydantic-settings
|
||||
```
|
||||
|
||||
### `config_store.py` - Raw TOML read/write
|
||||
|
||||
```python
|
||||
def read_raw_toml(path: Path) -> dict:
|
||||
# Loads TOML for merge/update without clobbering extra sections
|
||||
```
|
||||
|
||||
### `logging.py` - Secure logging setup
|
||||
|
||||
```python
|
||||
def setup_logging(*, debug: bool = False) -> None:
|
||||
# Configures structlog pipeline, redaction, and output formatting.
|
||||
```
|
||||
|
||||
Environment flags:
|
||||
|
||||
- `TAKOPI_LOG_LEVEL` (default `info`, `debug` forces `debug`)
|
||||
- `TAKOPI_LOG_FORMAT` (`console` or `json`)
|
||||
- `TAKOPI_LOG_COLOR` (`1/true/yes/on` to force color, `0/false/no/off` to disable)
|
||||
- `TAKOPI_LOG_FILE` (append JSON lines to a file)
|
||||
- `TAKOPI_TRACE_PIPELINE` (log pipeline events at info instead of debug)
|
||||
- `TAKOPI_NO_INTERACTIVE` (disable interactive prompts for CI/non-TTY environments)
|
||||
- `PI_CODING_AGENT_DIR` (override Pi agent session directory base path)
|
||||
|
||||
CLI flag: `--debug` enables debug logging (overrides `TAKOPI_LOG_LEVEL`).
|
||||
CLI flag: `--transport <id>` overrides the configured transport backend.
|
||||
|
||||
### `telegram/onboarding.py` - Setup validation
|
||||
|
||||
```python
|
||||
def check_setup(backend: EngineBackend) -> SetupResult:
|
||||
# Validates engine CLI on PATH and config file
|
||||
|
||||
def render_setup_guide(result: SetupResult):
|
||||
# Displays rich panel with setup instructions
|
||||
```
|
||||
|
||||
## Adding a Runner
|
||||
|
||||
See `docs/adding-a-runner.md` for the full guide and a worked example.
|
||||
|
||||
## Data Flow
|
||||
|
||||
### New Message Flow
|
||||
|
||||
```
|
||||
Telegram Update
|
||||
↓
|
||||
telegram/bridge.poll_updates() drains backlog, long-polls, filters allowed chat ids
|
||||
↓
|
||||
telegram/bridge.run_main_loop() spawns tasks in TaskGroup
|
||||
↓
|
||||
router.resolve_resume(text, reply_text) → ResumeToken | None
|
||||
↓
|
||||
router.entry_for(resume_token) or router.entry_for_engine(override/default) → RunnerEntry
|
||||
↓
|
||||
runner_bridge.handle_message() spawned as task with selected runner
|
||||
↓
|
||||
Send initial progress message (silent)
|
||||
↓
|
||||
runner.run(prompt, resume_token)
|
||||
├── Spawns engine subprocess (e.g., codex exec --json, pi --print --mode json)
|
||||
├── Streams JSONL from stdout
|
||||
├── Normalizes JSONL -> takopi events
|
||||
├── Yields Takopi events (async iterator)
|
||||
│ ↓
|
||||
│ ProgressTracker.note_event()
|
||||
│ ↓
|
||||
│ ProgressEdits best-effort transport.edit(wait=False)
|
||||
└── Ends with completed(resume, ok, answer)
|
||||
↓
|
||||
render_final() with resume line (runner-formatted)
|
||||
↓
|
||||
transport.send()/edit() final message, delete progress if needed
|
||||
```
|
||||
|
||||
### Resume Flow
|
||||
|
||||
Same as above; auto-router polls all runners to extract resume tokens:
|
||||
- Router returns first matching token (e.g. `` `claude --resume <id>` `` routes to Claude, `` `pi --session <id>` `` routes to Pi)
|
||||
- Selected runner spawns with resume (e.g. `codex exec --json resume <token> -`, `pi --print --mode json --session <path> <prompt>`)
|
||||
- Per-token lock serializes concurrent resumes on the same thread
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| `codex exec` fails (rc != 0) | Emits a warning `action` plus `completed(ok=false, error=...)` |
|
||||
| `pi` fails (rc != 0) | Emits a warning `action` plus `completed(ok=false, error=...)` |
|
||||
| Telegram API error | Logged, edit skipped (progress continues) |
|
||||
| Cancellation | Cancel scope terminates the process group (POSIX) and renders `cancelled` |
|
||||
| Errors in handler | Final render uses `status=error` and preserves resume tokens when known |
|
||||
| No agent_message (empty answer) | Final shows `error` status |
|
||||
- [Write a plugin](how-to/write-a-plugin.md)
|
||||
- [Add a runner](how-to/add-a-runner.md)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Explanation
|
||||
|
||||
Explanation docs answer **“how does this work?”** and **“why is it designed this way?”**
|
||||
|
||||
If you want step-by-step instructions, go to **[Tutorials](../tutorials/index.md)**.
|
||||
If you want exact options and contracts, go to **[Reference](../reference/index.md)**.
|
||||
|
||||
## How Takopi works end-to-end
|
||||
|
||||
- Incoming Telegram message → resolve context (project/branch) → resolve resume token → select runner → stream events → render progress → send final + resume line.
|
||||
|
||||
Start here:
|
||||
|
||||
- [Architecture](architecture.md)
|
||||
|
||||
## Routing, sessions, and continuation
|
||||
|
||||
Takopi is stateless by default, but can provide “continuation” in multiple ways:
|
||||
|
||||
- reply-to-continue (always available)
|
||||
- per-topic resume (Telegram forum topics)
|
||||
- per-chat sessions (auto-resume)
|
||||
|
||||
- [Routing & sessions](routing-and-sessions.md)
|
||||
|
||||
## Plugins and extensibility
|
||||
|
||||
Takopi uses entrypoint-based plugins with lazy discovery so broken plugins don’t brick the CLI.
|
||||
|
||||
- [Plugin system](plugin-system.md)
|
||||
|
||||
## Codebase orientation
|
||||
|
||||
If you’re making changes, this is the “map of the territory”:
|
||||
|
||||
- [Module map](module-map.md)
|
||||
|
||||
## Where to look for hard rules
|
||||
|
||||
Explanation pages describe intent and tradeoffs. The *hard requirements* live in:
|
||||
|
||||
- [Reference: Specification](../reference/specification.md)
|
||||
- [Reference: Plugin API](../reference/plugin-api.md)
|
||||
@@ -0,0 +1,81 @@
|
||||
# Module map
|
||||
|
||||
This page is a high-level map of Takopi’s internal modules: what they do and how they fit together.
|
||||
|
||||
## Entry points
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `cli.py` | Typer CLI entry point; loads settings, selects engine/transport, runs the transport backend. |
|
||||
| `telegram/backend.py` | Telegram transport backend: validates config, runs onboarding, builds and runs the Telegram bridge. |
|
||||
|
||||
## Orchestration and routing
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `runner_bridge.py` | Transport-agnostic orchestration: per-message handler, progress updates, final render, cancellation, resume coordination. |
|
||||
| `router.py` | Auto-router: resolves resume tokens by polling runners; selects a runner for a message. |
|
||||
| `scheduler.py` | Per-thread FIFO job queueing with serialization. |
|
||||
| `transport_runtime.py` | Facade used by transports and commands to resolve messages and runners without importing internal router/project types. |
|
||||
|
||||
## Domain model and events
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `model.py` | Domain types: resume tokens, events, actions, run results. |
|
||||
| `runner.py` | Runner protocol and event queue utilities. |
|
||||
| `events.py` | Event factory helpers for building Takopi events consistently. |
|
||||
|
||||
## Rendering and progress
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `progress.py` | Progress tracking: reduces takopi events into progress snapshots. |
|
||||
| `markdown.py` | Markdown formatting for progress/final messages; includes helpers like elapsed formatting. |
|
||||
| `presenter.py` | Presenter protocol: converts `ProgressState` into transport-specific messages. |
|
||||
| `transport.py` | Transport protocol: send/edit/delete abstractions and message reference types. |
|
||||
|
||||
## Telegram implementation
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `telegram/bridge.py` | Telegram bridge loop: polls updates, filters messages, dispatches handlers, coordinates cancellation. |
|
||||
| `telegram/client.py` | Telegram API wrapper with retry/outbox semantics. |
|
||||
| `telegram/render.py` | Telegram markdown rendering and trimming. |
|
||||
| `telegram/onboarding.py` | Interactive setup and setup validation UX. |
|
||||
| `telegram/commands/*` | In-chat command handlers (`/agent`, `/file`, `/topic`, `/ctx`, `/new`, …). |
|
||||
|
||||
## Plugins
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `plugins.py` | Entrypoint discovery and lazy loading (capture load errors, filter by enabled list). |
|
||||
| `engines.py` | Engine backend discovery and loading via entrypoints. |
|
||||
| `transports.py` | Transport backend discovery and loading via entrypoints. |
|
||||
| `commands.py` | Command backend discovery and loading via entrypoints; command execution helpers. |
|
||||
| `ids.py` | Shared ID regex and collision checks for plugin ids and Telegram command names. |
|
||||
| `api.py` | Public plugin API boundary (`takopi.api` re-exports). |
|
||||
|
||||
## Runners and schemas
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `runners/*` | Engine runner implementations (Codex, Claude, OpenCode, Pi). |
|
||||
| `schemas/*` | msgspec schemas / decoders for engine JSONL streams. |
|
||||
|
||||
## Configuration and persistence
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `settings.py` | Loads `takopi.toml` (TOML + env), validates with pydantic-settings. |
|
||||
| `config_store.py` | Raw TOML read/write (merge/update without clobbering extra sections). |
|
||||
| `config_migrations.py` | One-time edits to on-disk config (e.g. legacy Telegram key migration). |
|
||||
|
||||
## Utilities
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `utils/paths.py` | Path/command relativization helpers. |
|
||||
| `utils/streams.py` | Async stream helpers (`iter_bytes_lines`, stderr draining). |
|
||||
| `utils/subprocess.py` | Subprocess management helpers (terminate/kill best-effort). |
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Plugin system
|
||||
|
||||
Takopi uses Python entrypoints to extend engines, transports, and commands.
|
||||
|
||||
## Why entrypoints
|
||||
|
||||
Entrypoints let Takopi discover plugins without hard dependencies on plugin packages.
|
||||
Installed distributions declare what they provide, and Takopi can list and load them at runtime.
|
||||
|
||||
This makes it possible to:
|
||||
|
||||
- Add new engines/transports/commands without changing Takopi itself.
|
||||
- Ship plugins independently.
|
||||
- Keep the core CLI small.
|
||||
|
||||
## Why discovery is lazy
|
||||
|
||||
Takopi lists plugin IDs **without importing plugin code**, then imports a plugin only when:
|
||||
|
||||
- it is selected by routing (engine/transport), or
|
||||
- it is invoked as a command, or
|
||||
- you explicitly request loading via `takopi plugins --load`.
|
||||
|
||||
This keeps `takopi --help` fast and prevents a broken third-party plugin from bricking the CLI.
|
||||
|
||||
## Entrypoint rules (what Takopi expects)
|
||||
|
||||
Takopi uses three entrypoint groups:
|
||||
|
||||
```toml
|
||||
[project.entry-points."takopi.engine_backends"]
|
||||
myengine = "myengine.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.transport_backends"]
|
||||
mytransport = "mytransport.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.command_backends"]
|
||||
mycommand = "mycommand.backend:BACKEND"
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- The entrypoint **name** is the plugin id.
|
||||
- The entrypoint value must resolve to a backend object:
|
||||
- engine backend: `EngineBackend`
|
||||
- transport backend: `TransportBackend`
|
||||
- command backend: `CommandBackend`
|
||||
- The backend object must have `id == entrypoint name`.
|
||||
|
||||
## Why there is an enabled list
|
||||
|
||||
Plugin visibility can be restricted via:
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
enabled = ["takopi-engine-acme", "takopi-transport-slack"]
|
||||
```
|
||||
|
||||
When set, Takopi filters by **distribution name** (package metadata), not by entrypoint name.
|
||||
This lets you:
|
||||
|
||||
- ship multiple entrypoints from one distribution, and
|
||||
- enable/disable whole plugin packages predictably.
|
||||
|
||||
## IDs and collisions
|
||||
|
||||
Entrypoint names become plugin IDs and appear in user-facing surfaces (CLI subcommands, Telegram commands, `/engine` directives).
|
||||
Takopi validates IDs and rejects collisions with reserved names.
|
||||
|
||||
Plugin IDs must match:
|
||||
|
||||
```
|
||||
^[a-z0-9_]{1,32}$
|
||||
```
|
||||
|
||||
Reserved IDs include core chat and CLI command names such as `cancel`, `init`, and `plugins`.
|
||||
|
||||
## How to debug discovery and loading
|
||||
|
||||
```sh
|
||||
takopi plugins
|
||||
takopi plugins --load
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Write a plugin](../how-to/write-a-plugin.md)
|
||||
- [Plugin API reference](../reference/plugin-api.md)
|
||||
@@ -0,0 +1,44 @@
|
||||
# Routing & sessions
|
||||
|
||||
Takopi is **stateless by default**: each message starts a new engine session unless a resume token is present.
|
||||
|
||||
## Continuation (how threads persist)
|
||||
|
||||
Takopi supports three ways to continue a thread:
|
||||
|
||||
1. **Reply-to-continue** (always available)
|
||||
- Reply to any bot message that contains a resume line in the footer.
|
||||
- Takopi extracts the resume token and resumes that engine thread.
|
||||
2. **Forum topics** (optional)
|
||||
- Topics can store resume tokens per topic and auto-resume new messages in that topic.
|
||||
- Topic state is stored in `telegram_topics_state.json`.
|
||||
- Reset with `/new`.
|
||||
3. **Chat sessions** (optional)
|
||||
- Set `session_mode = "chat"` to store one resume token per chat (per sender in groups).
|
||||
- State is stored in `telegram_chat_sessions_state.json`.
|
||||
- Reset with `/new`.
|
||||
|
||||
Reply-to-continue works even if topics or chat sessions are enabled.
|
||||
|
||||
## Routing (how Takopi picks a runner)
|
||||
|
||||
For each message, Takopi:
|
||||
|
||||
- parses directive prefixes (`/engine`, `/project`, `@branch`) from the first non-empty line
|
||||
- attempts to extract a resume token by polling available runners
|
||||
- if a resume token is found, routes to the matching runner; otherwise uses the configured default engine
|
||||
|
||||
## Serialization (why you don’t get overlapping runs)
|
||||
|
||||
Takopi allows parallel runs across **different threads**, but enforces serialization within a thread:
|
||||
|
||||
- Telegram side: jobs are queued FIFO per thread.
|
||||
- Runner side: runners enforce per-resume-token locks (so the same session can’t be resumed concurrently).
|
||||
|
||||
The precise invariants are specified in the [Specification](../reference/specification.md).
|
||||
|
||||
## Related
|
||||
|
||||
- [Commands & directives](../reference/commands-and-directives.md)
|
||||
- [Context resolution](../reference/context-resolution.md)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Dev setup
|
||||
|
||||
Set up Takopi for local development and run the checks.
|
||||
|
||||
## Clone and run
|
||||
|
||||
```bash
|
||||
git clone https://github.com/banteg/takopi
|
||||
cd takopi
|
||||
|
||||
# Run directly with uv (installs deps automatically)
|
||||
uv run takopi --help
|
||||
```
|
||||
|
||||
## Install locally (optional)
|
||||
|
||||
```bash
|
||||
uv tool install .
|
||||
takopi --help
|
||||
```
|
||||
|
||||
## Run checks
|
||||
|
||||
```bash
|
||||
uv run pytest
|
||||
uv run ruff check src tests
|
||||
uv run ty check .
|
||||
|
||||
# Or all at once
|
||||
just check
|
||||
```
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# File transfer
|
||||
|
||||
Upload files into the active repo/worktree or fetch files back into Telegram.
|
||||
|
||||
## Enable file transfer
|
||||
|
||||
```toml
|
||||
[transports.telegram.files]
|
||||
enabled = true
|
||||
auto_put = true
|
||||
auto_put_mode = "upload" # upload | prompt
|
||||
uploads_dir = "incoming"
|
||||
allowed_user_ids = [123456789]
|
||||
deny_globs = [".git/**", ".env", ".envrc", "**/*.pem", "**/.ssh/**"]
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- File transfer is **disabled by default**.
|
||||
- If `allowed_user_ids` is empty, private chats are allowed and group usage requires admin privileges.
|
||||
|
||||
## Upload a file (`/file put`)
|
||||
|
||||
Send a document with a caption:
|
||||
|
||||
```
|
||||
/file put <path>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
/file put docs/spec.pdf
|
||||
/file put /happy-gadgets @feat/camera assets/logo.png
|
||||
```
|
||||
|
||||
If you send a file **without a caption**, Takopi saves it to `incoming/<original_filename>`.
|
||||
|
||||
Use `--force` to overwrite:
|
||||
|
||||
```
|
||||
/file put --force docs/spec.pdf
|
||||
```
|
||||
|
||||
## Fetch a file (`/file get`)
|
||||
|
||||
Send:
|
||||
|
||||
```
|
||||
/file get <path>
|
||||
```
|
||||
|
||||
Directories are zipped automatically.
|
||||
|
||||
## Related
|
||||
|
||||
- [Commands & directives](../reference/commands-and-directives.md)
|
||||
- [Config reference](../reference/config.md)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# How-to guides
|
||||
|
||||
How-to guides are **goal-oriented recipes**. Pick the task you’re trying to accomplish and follow the steps.
|
||||
|
||||
If you’re learning from scratch, start with **[Tutorials](../tutorials/index.md)**.
|
||||
If you need exact options and defaults, use **[Reference](../reference/index.md)**.
|
||||
|
||||
## Daily use
|
||||
|
||||
- [Switch engines](switch-engines.md) (`/codex`, `/claude`, `/opencode`, `/pi`)
|
||||
- [Projects](projects.md) (register repos + run from anywhere)
|
||||
- [Worktrees](worktrees.md) (run work on `@branch` without switching your main checkout)
|
||||
- [Route by chat](route-by-chat.md) (dedicated chats per project)
|
||||
- [Topics](topics.md) (forum threads bound to repo/branch + auto-resume)
|
||||
- [Chat sessions](topics.md#chat-sessions) (auto-resume without replying)
|
||||
|
||||
## Messaging extras
|
||||
|
||||
- [Voice notes](voice-notes.md) (transcribe and run)
|
||||
- [File transfer](file-transfer.md) (`/file put` and `/file get`)
|
||||
- [Schedule tasks](schedule-tasks.md) (Telegram scheduled messages)
|
||||
|
||||
## Extending Takopi
|
||||
|
||||
- [Write a plugin](write-a-plugin.md) (engines, transports, commands)
|
||||
- [Add a runner](add-a-runner.md) (implement a new engine backend)
|
||||
- [Dev setup](dev-setup.md) (run from source, tests, linting, type checks)
|
||||
|
||||
## Debugging and operations
|
||||
|
||||
- [Troubleshooting](troubleshooting.md) (`--debug`, common gotchas, “why didn’t it route?”)
|
||||
|
||||
## Not sure where to go?
|
||||
|
||||
- If your question starts with “**How do I…**” → you’re in the right place.
|
||||
- If your question starts with “**What are the exact options / defaults?**” → go to **[Reference](../reference/index.md)**.
|
||||
- If your question starts with “**Why is it designed this way?**” → go to **[Explanation](../explanation/index.md)**.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Projects
|
||||
|
||||
Projects let you route messages to repos from anywhere using `/alias`.
|
||||
|
||||
## Register a repo as a project
|
||||
|
||||
```sh
|
||||
cd ~/dev/happy-gadgets
|
||||
takopi init happy-gadgets
|
||||
```
|
||||
|
||||
This adds a project to your config:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
```
|
||||
|
||||
## Target a project from chat
|
||||
|
||||
Send:
|
||||
|
||||
```
|
||||
/happy-gadgets pinky-link two threads
|
||||
```
|
||||
|
||||
## Project-specific settings
|
||||
|
||||
Projects can override global defaults:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
default_engine = "claude"
|
||||
worktrees_dir = ".worktrees"
|
||||
worktree_base = "master"
|
||||
```
|
||||
|
||||
If you expect to edit config while Takopi is running, enable hot reload:
|
||||
|
||||
```toml
|
||||
watch_config = true
|
||||
```
|
||||
|
||||
## Set a default project
|
||||
|
||||
If you mostly work in one repo:
|
||||
|
||||
```toml
|
||||
default_project = "happy-gadgets"
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Context resolution](../reference/context-resolution.md)
|
||||
- [Worktrees](worktrees.md)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Route by chat
|
||||
|
||||
Bind a Telegram chat to a project so messages in that chat automatically route to the right repo.
|
||||
|
||||
## Capture a chat id and save it to a project
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
takopi chat-id --project happy-gadgets
|
||||
```
|
||||
|
||||
Then send any message in the target chat. Takopi captures the `chat_id` and updates your config:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
chat_id = -1001234567890
|
||||
```
|
||||
|
||||
Messages from that chat now default to the project.
|
||||
|
||||
## Rules for chat ids
|
||||
|
||||
- Each `projects.*.chat_id` must be unique.
|
||||
- A project `chat_id` must not match `transports.telegram.chat_id`.
|
||||
- Telegram uses positive IDs for private chats and negative IDs for groups/supergroups.
|
||||
|
||||
## Capture a chat id without saving
|
||||
|
||||
```sh
|
||||
takopi chat-id
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Topics](topics.md)
|
||||
- [Context resolution](../reference/context-resolution.md)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Schedule tasks
|
||||
|
||||
Telegram’s native message scheduling works with Takopi.
|
||||
|
||||
In Telegram, long-press the send button and choose **Schedule Message** to run tasks at a specific time.
|
||||
You can also set up recurring schedules (daily/weekly) for automated workflows.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Switch engines
|
||||
|
||||
Run a one-off message on a specific engine, or set a persistent default for a chat/topic.
|
||||
|
||||
## Use an engine for one message
|
||||
|
||||
Prefix the first non-empty line with an engine directive:
|
||||
|
||||
```
|
||||
/codex hard reset the timeline
|
||||
/claude shrink and store artifacts forever
|
||||
/opencode hide their paper until they reply
|
||||
/pi render a diorama of this timeline
|
||||
```
|
||||
|
||||
Directives are only parsed at the start of the first non-empty line.
|
||||
|
||||
## Set a default engine for the current scope
|
||||
|
||||
Use `/agent`:
|
||||
|
||||
```
|
||||
/agent
|
||||
/agent set claude
|
||||
/agent clear
|
||||
```
|
||||
|
||||
- Inside a forum topic, `/agent set` affects that topic.
|
||||
- In normal chats, it affects the whole chat.
|
||||
- In group chats, only admins can change defaults.
|
||||
|
||||
Selection precedence (highest to lowest): resume token → `/engine` directive → topic default → chat default → project default → global default.
|
||||
|
||||
## Engine installation
|
||||
|
||||
Takopi shells out to engine CLIs. Install them and make sure they’re on your `PATH`
|
||||
(`codex`, `claude`, `opencode`, `pi`). Authentication is handled by each CLI.
|
||||
|
||||
## Related
|
||||
|
||||
- [Commands & directives](../reference/commands-and-directives.md)
|
||||
- [Config reference](../reference/config.md)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Topics
|
||||
|
||||
Topics bind Telegram forum threads to a specific project/branch context. They can also store resume tokens and a default agent per topic.
|
||||
|
||||
## Enable topics
|
||||
|
||||
```toml
|
||||
[transports.telegram.topics]
|
||||
enabled = true
|
||||
scope = "auto" # auto | main | projects | all
|
||||
```
|
||||
|
||||
Your bot needs **Manage Topics** permission in the group.
|
||||
|
||||
If any `projects.<alias>.chat_id` are configured, topics are managed in those project chats; otherwise topics are managed in the main chat.
|
||||
|
||||
## Topic commands
|
||||
|
||||
Run these inside a topic thread:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/topic <project> @branch` | Create a new topic bound to context |
|
||||
| `/ctx` | Show the current binding |
|
||||
| `/ctx set <project> @branch` | Update the binding |
|
||||
| `/ctx clear` | Remove the binding |
|
||||
| `/new` | Clear stored sessions for this topic |
|
||||
|
||||
In project chats, omit the project: `/topic @branch` or `/ctx set @branch`.
|
||||
|
||||
## Chat sessions
|
||||
|
||||
Chat sessions store one resume token per chat (per sender in groups) so new messages can auto-resume without replying.
|
||||
|
||||
Enable:
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
session_mode = "chat" # stateless | chat
|
||||
```
|
||||
|
||||
Reset the stored session with `/new`.
|
||||
|
||||
## State files
|
||||
|
||||
- Topic state: `telegram_topics_state.json`
|
||||
- Chat sessions state: `telegram_chat_sessions_state.json`
|
||||
- Chat defaults (e.g. `/agent`): `telegram_chat_prefs_state.json`
|
||||
|
||||
## Related
|
||||
|
||||
- [Switch engines](switch-engines.md)
|
||||
- [Commands & directives](../reference/commands-and-directives.md)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Troubleshooting
|
||||
|
||||
If something isn’t working, rerun with debug logging enabled:
|
||||
|
||||
```sh
|
||||
takopi --debug
|
||||
```
|
||||
|
||||
Then check `debug.log` for errors and include it when reporting issues.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Voice notes
|
||||
|
||||
Enable transcription so voice notes become normal text runs.
|
||||
|
||||
## Enable transcription
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
voice_transcription = true
|
||||
voice_transcription_model = "gpt-4o-mini-transcribe" # optional
|
||||
```
|
||||
|
||||
Set `OPENAI_API_KEY` in your environment (uses OpenAI’s transcription API).
|
||||
|
||||
To use a local OpenAI-compatible Whisper server, also set `OPENAI_BASE_URL`
|
||||
(for example, `http://localhost:8000/v1`) and a dummy `OPENAI_API_KEY` if your server ignores it.
|
||||
If your server requires a specific model name, set `voice_transcription_model` (for example, `whisper-1`).
|
||||
|
||||
## Behavior
|
||||
|
||||
When you send a voice note, Takopi transcribes it and runs the result as a normal text message.
|
||||
If transcription fails, you’ll get an error message and the run is skipped.
|
||||
|
||||
## Related
|
||||
|
||||
- [Config reference](../reference/config.md)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Worktrees
|
||||
|
||||
Use `@branch` to run tasks in a dedicated git worktree for that branch.
|
||||
|
||||
## Enable worktree-based runs for a project
|
||||
|
||||
Add a `worktrees_dir` (and optionally a base branch) to the project:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
worktrees_dir = ".worktrees" # relative to project path
|
||||
worktree_base = "master" # base branch for new worktrees
|
||||
```
|
||||
|
||||
## Run in a branch worktree
|
||||
|
||||
Send a message like:
|
||||
|
||||
```
|
||||
/happy-gadgets @feat/memory-box freeze artifacts forever
|
||||
```
|
||||
|
||||
## Ignore `.worktrees/` in git status
|
||||
|
||||
If you use the default `.worktrees/` directory inside the repo, add it to a gitignore.
|
||||
One option is a global ignore:
|
||||
|
||||
```sh
|
||||
git config --global core.excludesfile ~/.config/git/ignore
|
||||
echo ".worktrees/" >> ~/.config/git/ignore
|
||||
```
|
||||
|
||||
## Context persistence
|
||||
|
||||
When project/worktree context is active, Takopi includes a `ctx:` footer in messages.
|
||||
When you reply, this context carries forward (you usually don’t need to repeat `/project @branch`).
|
||||
|
||||
## Related
|
||||
|
||||
- [Context resolution](../reference/context-resolution.md)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Write a plugin
|
||||
|
||||
Takopi supports entrypoint-based plugins for engines, transports, and commands.
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Pick a plugin id (must match `^[a-z0-9_]{1,32}$`).
|
||||
2. Add a Python entrypoint in your package’s `pyproject.toml`.
|
||||
3. Implement a backend object (`BACKEND`) with `id == entrypoint name`.
|
||||
4. Install your package and validate with `takopi plugins --load`.
|
||||
|
||||
## Entrypoint groups
|
||||
|
||||
Takopi uses three entrypoint groups:
|
||||
|
||||
```toml
|
||||
[project.entry-points."takopi.engine_backends"]
|
||||
myengine = "myengine.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.transport_backends"]
|
||||
mytransport = "mytransport.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.command_backends"]
|
||||
mycommand = "mycommand.backend:BACKEND"
|
||||
```
|
||||
|
||||
## Engine backend plugin (runner)
|
||||
|
||||
Minimal example:
|
||||
|
||||
```py
|
||||
# myengine/backend.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from takopi.api import EngineBackend, EngineConfig, Runner
|
||||
|
||||
|
||||
def build_runner(config: EngineConfig, config_path: Path) -> Runner:
|
||||
_ = config_path
|
||||
return MyEngineRunner(config)
|
||||
|
||||
|
||||
BACKEND = EngineBackend(
|
||||
id="myengine",
|
||||
build_runner=build_runner,
|
||||
cli_cmd="myengine",
|
||||
install_cmd="pip install myengine",
|
||||
)
|
||||
```
|
||||
|
||||
Engine config is a raw table in `takopi.toml`:
|
||||
|
||||
```toml
|
||||
[myengine]
|
||||
model = "..."
|
||||
```
|
||||
|
||||
## Transport backend plugin
|
||||
|
||||
Transport plugins connect Takopi to other messaging systems (Slack, Discord, …).
|
||||
For most transports, delegate message handling to `handle_message()` from `takopi.api`.
|
||||
|
||||
## Command backend plugin
|
||||
|
||||
Command plugins add custom `/command` handlers. They only run when the message starts
|
||||
with `/<id>` and the id does not collide with engine ids, project aliases, or reserved names.
|
||||
|
||||
Minimal example:
|
||||
|
||||
```py
|
||||
# mycommand/backend.py
|
||||
from __future__ import annotations
|
||||
|
||||
from takopi.api import CommandContext, CommandResult
|
||||
|
||||
|
||||
class MyCommand:
|
||||
id = "hello"
|
||||
description = "say hello"
|
||||
|
||||
async def handle(self, ctx: CommandContext) -> CommandResult | None:
|
||||
_ = ctx
|
||||
return CommandResult(text="hello")
|
||||
|
||||
|
||||
BACKEND = MyCommand()
|
||||
```
|
||||
|
||||
### Command plugin configuration
|
||||
|
||||
Configure under `[plugins.<id>]`:
|
||||
|
||||
```toml
|
||||
[plugins.hello]
|
||||
greeting = "hello"
|
||||
```
|
||||
|
||||
The parsed dict is available as `ctx.plugin_config` in `handle()`.
|
||||
|
||||
## Enable/disable installed plugins
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
enabled = ["takopi-transport-slack", "takopi-engine-acme"]
|
||||
```
|
||||
|
||||
- `enabled = []` (default) means “load all installed plugins”.
|
||||
- If non-empty, only distributions with matching names are visible.
|
||||
|
||||
## Validate discovery and loading
|
||||
|
||||
```sh
|
||||
takopi plugins
|
||||
takopi plugins --load
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Plugin system (design)](../explanation/plugin-system.md)
|
||||
- [Plugin API reference](../reference/plugin-api.md)
|
||||
+90
-19
@@ -1,31 +1,102 @@
|
||||
# Takopi
|
||||
# Takopi documentation
|
||||
|
||||
Takopi connects agent CLIs to Telegram so you can run, monitor, and reply to long-running tasks from chat.
|
||||
It supports multiple runner backends and a pluggable transport layer, with a stable public API for extensions.
|
||||
> Telegram bridge for coding agents (Codex, Claude Code, OpenCode, Pi).
|
||||
|
||||
## What this site covers
|
||||
Takopi lets you run an engine CLI in a local repo while controlling it from Telegram: send a task, stream updates, and continue safely (reply-to-continue, topics, or sessions).
|
||||
|
||||
- How to get Takopi running end-to-end
|
||||
- Project aliases and worktree-aware workflows
|
||||
- The plugin system and stable public API surface
|
||||
- Architectural details and behavioral guarantees
|
||||
## Choose your path
|
||||
|
||||
- **I’m new / I want to get it running**
|
||||
- Start with **[Tutorials](tutorials/index.md)**:
|
||||
- [Install & onboard](tutorials/install-and-onboard.md)
|
||||
- [First run](tutorials/first-run.md)
|
||||
|
||||
- **I know what I want to do (enable a feature / fix a workflow)**
|
||||
- Use **[How-to guides](how-to/index.md)**:
|
||||
- [Projects](how-to/projects.md) and [Worktrees](how-to/worktrees.md)
|
||||
- [Topics](how-to/topics.md) and [Route by chat](how-to/route-by-chat.md)
|
||||
- [File transfer](how-to/file-transfer.md) and [Voice notes](how-to/voice-notes.md)
|
||||
|
||||
- **I need exact knobs, defaults, and contracts**
|
||||
- Go straight to **[Reference](reference/index.md)**:
|
||||
- [Commands & directives](reference/commands-and-directives.md)
|
||||
- [Configuration](reference/config.md)
|
||||
- [Specification](reference/specification.md) (normative behavior)
|
||||
|
||||
- **I’m trying to understand the design**
|
||||
- Read **[Explanation](explanation/index.md)**:
|
||||
- [Architecture](explanation/architecture.md)
|
||||
- [Routing & sessions](explanation/routing-and-sessions.md)
|
||||
- [Plugin system](explanation/plugin-system.md)
|
||||
|
||||
## Quick start
|
||||
|
||||
If you just want to see it work end-to-end:
|
||||
|
||||
```bash
|
||||
uv run takopi --help
|
||||
# Install
|
||||
uv tool install -U takopi
|
||||
|
||||
# Configure Telegram + defaults
|
||||
takopi --onboard
|
||||
|
||||
# Run in a repo
|
||||
cd /path/to/your/repo
|
||||
takopi
|
||||
```
|
||||
|
||||
## Documentation map
|
||||
Then open Telegram and send a task to your bot.
|
||||
|
||||
- Start here: [User guide](user-guide.md)
|
||||
- Projects and worktrees: [Projects](projects.md)
|
||||
- Plugin development: [Plugins](plugins.md) and [Public API](public-api.md)
|
||||
- System behavior: [Architecture](architecture.md) and [Specification](specification.md)
|
||||
- Transport details: [Telegram](transports/telegram.md)
|
||||
- Contributor notes: [Developing](developing.md)
|
||||
## Core concepts
|
||||
|
||||
## LLM entrypoints
|
||||
* **Engine**: the CLI that actually does the work (e.g. `codex`, `claude`, `opencode`, `pi`).
|
||||
* **Project**: a named alias for a repo path (so you can run from anywhere).
|
||||
* **Worktree / branch selection**: pick where work should happen (`@branch`).
|
||||
* **Continuation**: how Takopi safely “continues” a run:
|
||||
|
||||
- `/llms.txt` lists the key pages and links to their Markdown mirrors.
|
||||
- `/llms-full.txt` contains the full expanded content of those pages.
|
||||
* reply-to-continue (always available)
|
||||
* forum topics (thread-bound continuation)
|
||||
* chat sessions (auto-resume)
|
||||
* **Contract**: the stable rules (resume lines, event ordering, rendering expectations) in the
|
||||
[Specification](reference/specification.md) and runner contract tests.
|
||||
|
||||
## For plugin authors
|
||||
|
||||
Start here:
|
||||
|
||||
* [Plugin API](reference/plugin-api.md) — **stable** `takopi.api` surface for plugins
|
||||
* [Write a plugin](how-to/write-a-plugin.md)
|
||||
* [Add a runner](how-to/add-a-runner.md)
|
||||
|
||||
If you’re contributing to core:
|
||||
|
||||
* [Dev setup](how-to/dev-setup.md)
|
||||
* [Module map](explanation/module-map.md)
|
||||
|
||||
## For LLM agents
|
||||
|
||||
Takopi publishes an LLM-oriented index at:
|
||||
|
||||
* `/llms.txt` — curated entrypoints + links to Markdown mirrors
|
||||
* `/llms-full.txt` — expanded “single file” context of the most important pages
|
||||
|
||||
In the docs, start here:
|
||||
|
||||
* [Reference: For agents](reference/agents/index.md)
|
||||
* [Repo map](reference/agents/repo-map.md)
|
||||
* [Invariants](reference/agents/invariants.md)
|
||||
|
||||
## Where to look when something feels “off”
|
||||
|
||||
* “Why didn’t it route to the right repo/branch?” → [Context resolution](reference/context-resolution.md)
|
||||
* “Why didn’t it continue where I left off?” → [Commands & directives](reference/commands-and-directives.md) and [Specification](reference/specification.md)
|
||||
* “Why did Telegram messages behave weirdly?” → [Telegram transport](reference/transports/telegram.md)
|
||||
* “Why is it built this way?” → [Architecture](explanation/architecture.md)
|
||||
|
||||
## Legacy portals
|
||||
|
||||
These pages remain as curated pointers to preserve old links:
|
||||
|
||||
- [User guide](user-guide.md)
|
||||
- [Plugins](plugins.md)
|
||||
- [Developing](developing.md)
|
||||
|
||||
+11
-296
@@ -1,305 +1,20 @@
|
||||
# Plugins
|
||||
# Plugins (moved)
|
||||
|
||||
Takopi supports **entrypoint-based plugins** for:
|
||||
This page was split into smaller Diátaxis pages.
|
||||
|
||||
- **Engine backends** (new runner implementations)
|
||||
- **Transport backends** (new chat/command transports)
|
||||
- **Command backends** (custom `/command` handlers)
|
||||
## Start here
|
||||
|
||||
Plugins are **discovered lazily**: Takopi lists IDs without importing plugin code,
|
||||
and loads a plugin only when it is needed (or when you explicitly request it).
|
||||
- [Write a plugin](how-to/write-a-plugin.md)
|
||||
|
||||
This keeps `takopi --help` fast and prevents broken plugins from bricking the CLI.
|
||||
## Design
|
||||
|
||||
See `public-api.md` for the stable API surface you should depend on.
|
||||
- [Plugin system](explanation/plugin-system.md)
|
||||
|
||||
---
|
||||
## Reference
|
||||
|
||||
## Entrypoint groups
|
||||
- [Plugin API](reference/plugin-api.md)
|
||||
|
||||
Takopi uses three Python entrypoint groups:
|
||||
## Diagnostics
|
||||
|
||||
```toml
|
||||
[project.entry-points."takopi.engine_backends"]
|
||||
myengine = "myengine.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.transport_backends"]
|
||||
mytransport = "mytransport.backend:BACKEND"
|
||||
|
||||
[project.entry-points."takopi.command_backends"]
|
||||
mycommand = "mycommand.backend:BACKEND"
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
|
||||
- The entrypoint **name** is the plugin ID.
|
||||
- The entrypoint value must resolve to a **backend object**:
|
||||
- Engine backend -> `EngineBackend`
|
||||
- Transport backend -> `TransportBackend`
|
||||
- Command backend -> `CommandBackend`
|
||||
- The backend object **must** have `id == entrypoint name`.
|
||||
|
||||
Takopi validates this at load time and will report errors via `takopi plugins --load`.
|
||||
|
||||
---
|
||||
|
||||
## ID rules
|
||||
|
||||
Plugin IDs are used in the CLI and (for engines/projects) in Telegram commands.
|
||||
They must match:
|
||||
|
||||
```
|
||||
^[a-z0-9_]{1,32}$
|
||||
```
|
||||
|
||||
If an ID does not match, it is skipped and reported as an error.
|
||||
|
||||
**Reserved IDs (engines):**
|
||||
|
||||
- `cancel` (core chat command)
|
||||
- `init`, `plugins` (CLI commands)
|
||||
|
||||
Engines using these IDs are skipped and reported as errors.
|
||||
|
||||
**Reserved IDs (commands):**
|
||||
|
||||
- `cancel`, `init`, `plugins`
|
||||
- Any engine id or project alias (checked at runtime)
|
||||
|
||||
Command backends using reserved IDs are skipped and reported as errors.
|
||||
|
||||
---
|
||||
|
||||
## Enabling plugins
|
||||
|
||||
Takopi supports a simple enabled list to control which plugins are visible.
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
enabled = ["takopi-transport-slack", "takopi-engine-acme"]
|
||||
```
|
||||
|
||||
- `enabled = []` (default) -> load all installed plugins.
|
||||
- If `enabled` is non-empty, **only distributions with matching names** are visible.
|
||||
- Distribution names are taken from package metadata (case-insensitive).
|
||||
- If a plugin has no resolvable distribution name and an enabled list is set, it is hidden.
|
||||
This enabled list affects:
|
||||
|
||||
- Engine subcommands registered in the CLI
|
||||
- `takopi plugins` output
|
||||
- Runtime resolution of engines/transports/commands
|
||||
|
||||
---
|
||||
|
||||
## Discovering plugins
|
||||
|
||||
Use the CLI to inspect plugins:
|
||||
|
||||
```sh
|
||||
takopi plugins
|
||||
takopi plugins --load
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
- `takopi plugins` lists discovered entrypoints **without loading them**.
|
||||
- `--load` loads each plugin to validate type and surface import errors.
|
||||
- Errors are shown at the end, grouped by engine/transport and distribution.
|
||||
- If `[plugins] enabled` is set, entries are still listed but marked `enabled`/`disabled`.
|
||||
|
||||
---
|
||||
|
||||
## Engine backend plugins
|
||||
|
||||
Engine plugins implement a runner for a new engine CLI and expose
|
||||
an `EngineBackend` object.
|
||||
|
||||
Minimal example:
|
||||
|
||||
```py
|
||||
# myengine/backend.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from takopi.api import EngineBackend, EngineConfig, Runner
|
||||
|
||||
def build_runner(config: EngineConfig, config_path: Path) -> Runner:
|
||||
_ = config_path
|
||||
# Parse config if needed; raise ConfigError for invalid config.
|
||||
return MyEngineRunner(config)
|
||||
|
||||
BACKEND = EngineBackend(
|
||||
id="myengine",
|
||||
build_runner=build_runner,
|
||||
cli_cmd="myengine",
|
||||
install_cmd="pip install myengine",
|
||||
)
|
||||
```
|
||||
|
||||
`EngineConfig` is the raw config table (dict) from `takopi.toml`:
|
||||
|
||||
```toml
|
||||
[myengine]
|
||||
model = "..."
|
||||
```
|
||||
|
||||
Read it with `settings.engine_config("myengine", config_path=...)` in Takopi,
|
||||
or just consume the dict directly in your runner builder.
|
||||
|
||||
See `public-api.md` for the runner contract and helper classes like
|
||||
`JsonlSubprocessRunner` and `EventFactory`.
|
||||
|
||||
---
|
||||
|
||||
## Transport backend plugins
|
||||
|
||||
Transport plugins connect Takopi to new messaging systems (Slack, Discord, etc).
|
||||
|
||||
You must provide a `TransportBackend` object with:
|
||||
|
||||
- `id` and `description`
|
||||
- `check_setup()` -> returns `SetupResult` (issues + config path)
|
||||
- `interactive_setup()` -> optional interactive setup flow
|
||||
- `lock_token()` -> token fingerprinting for config locks
|
||||
- `build_and_run()` -> build transport and start the main loop
|
||||
|
||||
Minimal skeleton:
|
||||
|
||||
```py
|
||||
# mytransport/backend.py
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from takopi.api import (
|
||||
EngineBackend,
|
||||
SetupResult,
|
||||
TransportBackend,
|
||||
TransportRuntime,
|
||||
)
|
||||
|
||||
class MyTransportBackend:
|
||||
id = "mytransport"
|
||||
description = "MyTransport bot"
|
||||
|
||||
def check_setup(
|
||||
self, engine_backend: EngineBackend, *, transport_override: str | None = None
|
||||
) -> SetupResult:
|
||||
_ = engine_backend, transport_override
|
||||
return SetupResult(issues=[], config_path=Path("takopi.toml"))
|
||||
|
||||
def interactive_setup(self, *, force: bool) -> bool:
|
||||
_ = force
|
||||
return True
|
||||
|
||||
def lock_token(
|
||||
self, *, transport_config: dict[str, object], config_path: Path
|
||||
) -> str | None:
|
||||
_ = transport_config, config_path
|
||||
return None
|
||||
|
||||
def build_and_run(
|
||||
self,
|
||||
*,
|
||||
transport_config: dict[str, object],
|
||||
config_path: Path,
|
||||
runtime: TransportRuntime,
|
||||
final_notify: bool,
|
||||
default_engine_override: str | None,
|
||||
) -> None:
|
||||
_ = (
|
||||
transport_config,
|
||||
config_path,
|
||||
runtime,
|
||||
final_notify,
|
||||
default_engine_override,
|
||||
)
|
||||
raise NotImplementedError
|
||||
|
||||
BACKEND = MyTransportBackend()
|
||||
```
|
||||
|
||||
For most transports, you will want to call `handle_message()` from `takopi.api`
|
||||
inside your message loop. That function implements progress updates, resume handling,
|
||||
and cancellation semantics.
|
||||
|
||||
---
|
||||
|
||||
## Command backend plugins
|
||||
|
||||
Command plugins add custom `/command` handlers. A command only runs when the
|
||||
message starts with `/command` and does **not** collide with engine ids,
|
||||
project aliases, or reserved command names.
|
||||
|
||||
Minimal example:
|
||||
|
||||
```py
|
||||
# mycommand/backend.py
|
||||
from __future__ import annotations
|
||||
|
||||
from takopi.api import CommandContext, CommandResult, RunRequest
|
||||
|
||||
class MultiCommand:
|
||||
id = "multi"
|
||||
description = "run the prompt on every engine"
|
||||
|
||||
async def handle(self, ctx: CommandContext) -> CommandResult | None:
|
||||
prompt = ctx.args_text.strip()
|
||||
if not prompt:
|
||||
return CommandResult(text="usage: /multi <prompt>")
|
||||
requests = [
|
||||
RunRequest(prompt=prompt, engine=engine)
|
||||
for engine in ctx.runtime.available_engine_ids()
|
||||
]
|
||||
results = await ctx.executor.run_many(
|
||||
requests,
|
||||
mode="capture",
|
||||
parallel=True,
|
||||
)
|
||||
blocks = []
|
||||
for result in results:
|
||||
text = result.message.text if result.message else "no output"
|
||||
blocks.append(f"## {result.engine}\n{text}")
|
||||
return CommandResult(text="\n\n".join(blocks))
|
||||
|
||||
BACKEND = MultiCommand()
|
||||
```
|
||||
|
||||
### Command plugin configuration
|
||||
|
||||
Configure command plugins under `[plugins.<id>]`:
|
||||
|
||||
```toml
|
||||
[plugins.multi]
|
||||
engines = ["codex", "claude"]
|
||||
```
|
||||
|
||||
The parsed dict is available as `ctx.plugin_config` inside `handle()`.
|
||||
|
||||
---
|
||||
|
||||
## Versioning & compatibility
|
||||
|
||||
Takopi exposes a **stable plugin API** via `takopi.api`.
|
||||
|
||||
- `TAKOPI_PLUGIN_API_VERSION = 1` is the current API version.
|
||||
- Depend on a compatible Takopi version range, for example:
|
||||
|
||||
```toml
|
||||
dependencies = ["takopi>=0.14,<0.15"]
|
||||
```
|
||||
|
||||
When the plugin API changes, Takopi will bump the API version and document
|
||||
any compatibility guidance.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Common issues:
|
||||
|
||||
- **Plugin missing from CLI**: check the enabled list in `[plugins] enabled`.
|
||||
- **Plugin not listed**: verify entrypoint group and ID regex.
|
||||
- **Load failures**: run `takopi plugins --load` and inspect errors.
|
||||
- **ID mismatch**: ensure `BACKEND.id == entrypoint name`.
|
||||
- `takopi plugins` lists discovered entrypoints without loading them.
|
||||
- `takopi plugins --load` loads each plugin to surface import errors.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# For agents
|
||||
|
||||
These pages are **high-signal reference** for LLM agents (and humans acting like one).
|
||||
|
||||
- [Repo map](repo-map.md)
|
||||
- [Invariants](invariants.md)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Invariants
|
||||
|
||||
These are the “don’t break this” rules that keep Takopi reliable.
|
||||
|
||||
## Runner contract
|
||||
|
||||
The runner contract is enforced by `tests/test_runner_contract.py`:
|
||||
|
||||
- Exactly one `StartedEvent`
|
||||
- Exactly one `CompletedEvent`
|
||||
- `CompletedEvent` is last
|
||||
- `CompletedEvent.resume == StartedEvent.resume`
|
||||
|
||||
See also the [Plugin API](../plugin-api.md) runner contract section.
|
||||
|
||||
## Per-thread serialization
|
||||
|
||||
At most one active run may operate on the same thread/session at a time.
|
||||
This is enforced both by scheduling and by per-resume-token runner locks.
|
||||
|
||||
Normative details live in the [Specification](../specification.md) (§5.2).
|
||||
|
||||
## Resume lines
|
||||
|
||||
Resume lines embedded in chat are the engine’s canonical resume command (e.g. `claude --resume <id>`).
|
||||
|
||||
- The runner is authoritative for formatting and extraction.
|
||||
- Transports/rendering must preserve the resume line reliably (even when trimming/splitting).
|
||||
|
||||
Normative details live in the [Specification](../specification.md) (§3).
|
||||
|
||||
## Local contribution hygiene
|
||||
|
||||
- Run `just check` before code commits.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Repo map
|
||||
|
||||
Quick pointers for navigating the Takopi codebase.
|
||||
|
||||
## Where things start
|
||||
|
||||
- CLI entry point: `src/takopi/cli.py`
|
||||
- Telegram backend entry point: `src/takopi/telegram/backend.py`
|
||||
- Telegram bridge loop: `src/takopi/telegram/bridge.py`
|
||||
- Transport-agnostic handler: `src/takopi/runner_bridge.py`
|
||||
|
||||
## Core concepts
|
||||
|
||||
- Domain types (resume tokens, events, actions): `src/takopi/model.py`
|
||||
- Runner protocol: `src/takopi/runner.py`
|
||||
- Router selection and resume polling: `src/takopi/router.py`
|
||||
- Per-thread scheduling: `src/takopi/scheduler.py`
|
||||
- Progress reduction and rendering: `src/takopi/progress.py`, `src/takopi/markdown.py`
|
||||
|
||||
## Engines and streaming
|
||||
|
||||
- Runner implementations: `src/takopi/runners/*`
|
||||
- JSONL decoding schemas: `src/takopi/schemas/*`
|
||||
|
||||
## Plugins
|
||||
|
||||
- Public API boundary (`takopi.api`): `src/takopi/api.py`
|
||||
- Entrypoint discovery + lazy loading: `src/takopi/plugins.py`
|
||||
- Engine/transport/command backend loading: `src/takopi/engines.py`, `src/takopi/transports.py`, `src/takopi/commands.py`
|
||||
|
||||
## Configuration
|
||||
|
||||
- Settings model + TOML/env loading: `src/takopi/settings.py`
|
||||
- Config migrations: `src/takopi/config_migrations.py`
|
||||
|
||||
## Docs and contracts
|
||||
|
||||
- Normative behavior: [Specification](../specification.md)
|
||||
- Runner invariants: `tests/test_runner_contract.py`
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Commands & directives
|
||||
|
||||
This page documents Takopi’s user-visible command surface: message directives, in-chat commands, and the CLI.
|
||||
|
||||
## Message directives
|
||||
|
||||
Takopi parses the first non-empty line of a message for a directive prefix.
|
||||
|
||||
| Directive | Example | Effect |
|
||||
|----------|---------|--------|
|
||||
| `/engine` | `/codex fix flaky test` | Select an engine for this message. |
|
||||
| `/project` | `/happy-gadgets add escape-pod` | Select a project alias. |
|
||||
| `@branch` | `@feat/happy-camera rewind to checkpoint` | Run in a worktree for the branch. |
|
||||
| Combined | `/happy-gadgets @feat/flower-pin observe unseen` | Project + branch. |
|
||||
|
||||
Notes:
|
||||
|
||||
- Directives are only parsed at the start of the first non-empty line.
|
||||
- Parsing stops at the first non-directive token.
|
||||
- If a reply contains a `ctx:` line, Takopi ignores new directives and uses the reply context.
|
||||
|
||||
See [Context resolution](context-resolution.md) for the full rules.
|
||||
|
||||
## Context footer (`ctx:`)
|
||||
|
||||
When a run has project context, Takopi appends a footer line rendered as inline code:
|
||||
|
||||
- With branch: `` `ctx: <project> @<branch>` ``
|
||||
- Without branch: `` `ctx: <project>` ``
|
||||
|
||||
This line is parsed from replies and takes precedence over new directives.
|
||||
|
||||
## Telegram in-chat commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/cancel` | Reply to the progress message to stop the current run. |
|
||||
| `/agent` | Show/set the default agent for the current scope. |
|
||||
| `/file put <path>` | Upload a document into the repo/worktree (requires file transfer enabled). |
|
||||
| `/file get <path>` | Fetch a file or directory back into Telegram. |
|
||||
| `/topic <project> @branch` | Create/bind a topic (topics enabled). |
|
||||
| `/ctx` | Show topic context binding (topics enabled). |
|
||||
| `/ctx set <project> @branch` | Update topic context binding. |
|
||||
| `/ctx clear` | Remove topic context binding. |
|
||||
| `/new` | Clear stored sessions for the current scope (topic/chat). |
|
||||
|
||||
## CLI
|
||||
|
||||
Takopi’s CLI is an auto-router by default; engine subcommands override the default engine.
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `takopi` | Start Takopi (runs onboarding if setup/config is missing and you’re in a TTY). |
|
||||
| `takopi <engine>` | Run with a specific engine (e.g. `takopi codex`). |
|
||||
| `takopi init <alias>` | Register the current repo as a project. |
|
||||
| `takopi chat-id` | Capture the current chat id. |
|
||||
| `takopi chat-id --project <alias>` | Save the captured chat id to a project. |
|
||||
| `takopi plugins` | List discovered plugins without loading them. |
|
||||
| `takopi plugins --load` | Load each plugin to validate types and surface import errors. |
|
||||
|
||||
### Common flags
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--onboard` | Force the interactive setup wizard before starting. |
|
||||
| `--transport <id>` | Override the configured transport backend id. |
|
||||
| `--debug` | Write debug logs to `debug.log`. |
|
||||
| `--final-notify/--no-final-notify` | Send the final response as a new message vs an edit. |
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Configuration
|
||||
|
||||
Takopi reads configuration from `~/.takopi/takopi.toml`.
|
||||
|
||||
If you expect to edit config while Takopi is running, set:
|
||||
|
||||
```toml
|
||||
watch_config = true
|
||||
```
|
||||
|
||||
## Top-level keys
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
|-----|------|---------|-------|
|
||||
| `watch_config` | bool | `false` | Hot-reload config changes (transport excluded). |
|
||||
| `default_engine` | string | `"codex"` | Default engine id for new threads. |
|
||||
| `default_project` | string\|null | `null` | Default project alias. |
|
||||
| `transport` | string | `"telegram"` | Transport backend id. |
|
||||
|
||||
## `transports.telegram`
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
bot_token = "..."
|
||||
chat_id = 123
|
||||
```
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
|-----|------|---------|-------|
|
||||
| `bot_token` | string | (required) | Telegram bot token from @BotFather. |
|
||||
| `chat_id` | int | (required) | Default chat id. |
|
||||
| `message_overflow` | `"trim"`\|`"split"` | `"trim"` | How to handle long final responses. |
|
||||
| `voice_transcription` | bool | `false` | Enable voice note transcription. |
|
||||
| `voice_max_bytes` | int | `10485760` | Max voice note size (bytes). |
|
||||
| `voice_transcription_model` | string | `"gpt-4o-mini-transcribe"` | OpenAI transcription model name. |
|
||||
| `session_mode` | `"stateless"`\|`"chat"` | `"stateless"` | Auto-resume mode. |
|
||||
| `show_resume_line` | bool | `true` | Show resume line in message footer. |
|
||||
|
||||
### `transports.telegram.topics`
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
|-----|------|---------|-------|
|
||||
| `enabled` | bool | `false` | Enable forum-topic features. |
|
||||
| `scope` | `"auto"`\|`"main"`\|`"projects"`\|`"all"` | `"auto"` | Where topics are managed. |
|
||||
|
||||
### `transports.telegram.files`
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
|-----|------|---------|-------|
|
||||
| `enabled` | bool | `false` | Enable `/file put` and `/file get`. |
|
||||
| `auto_put` | bool | `true` | Auto-save uploads. |
|
||||
| `auto_put_mode` | `"upload"`\|`"prompt"` | `"upload"` | Whether uploads also start a run. |
|
||||
| `uploads_dir` | string | `"incoming"` | Relative path inside the repo/worktree. |
|
||||
| `allowed_user_ids` | int[] | `[]` | Allowed senders; empty allows private chats (group usage requires admin). |
|
||||
| `deny_globs` | string[] | (defaults) | Glob denylist (e.g. `.git/**`, `**/*.pem`). |
|
||||
|
||||
File size limits (not configurable):
|
||||
|
||||
- uploads: 20 MiB
|
||||
- downloads: 50 MiB
|
||||
|
||||
## `projects.<alias>`
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
worktrees_dir = ".worktrees"
|
||||
default_engine = "claude"
|
||||
worktree_base = "master"
|
||||
chat_id = -1001234567890
|
||||
```
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
|-----|------|---------|-------|
|
||||
| `path` | string | (required) | Repo root (expands `~`). Relative paths are resolved against the config directory. |
|
||||
| `worktrees_dir` | string | `".worktrees"` | Worktree root (relative to `path` unless absolute). |
|
||||
| `default_engine` | string\|null | `null` | Per-project default engine. |
|
||||
| `worktree_base` | string\|null | `null` | Base branch for new worktrees. |
|
||||
| `chat_id` | int\|null | `null` | Bind a Telegram chat to this project. |
|
||||
|
||||
Legacy config note: top-level `bot_token` / `chat_id` are auto-migrated into `[transports.telegram]` on startup.
|
||||
|
||||
## Plugins
|
||||
|
||||
### `plugins.enabled`
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
enabled = ["takopi-transport-slack", "takopi-engine-acme"]
|
||||
```
|
||||
|
||||
- `enabled = []` (default) means “load all installed plugins”.
|
||||
- If non-empty, only distributions with matching names are visible (case-insensitive).
|
||||
|
||||
### `plugins.<id>`
|
||||
|
||||
Plugin-specific configuration lives under `[plugins.<id>]` and is passed to command plugins as `ctx.plugin_config`.
|
||||
|
||||
## Engine-specific config tables
|
||||
|
||||
Engines can have top-level config tables keyed by engine id, for example:
|
||||
|
||||
```toml
|
||||
[codex]
|
||||
model = "..."
|
||||
```
|
||||
|
||||
The shape is engine-defined.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Projects and Worktrees
|
||||
# Context resolution
|
||||
|
||||
This doc covers project aliases, worktree behavior, and how Takopi resolves run
|
||||
context from messages.
|
||||
This page documents how Takopi resolves **run context** (project, worktree/branch, engine) from messages.
|
||||
For step-by-step usage, see [Projects](../how-to/projects.md) and [Worktrees](../how-to/worktrees.md).
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -13,9 +13,10 @@ worktree-based runs via `@branch`.
|
||||
the task in that worktree.
|
||||
- Progress/final messages include a `ctx:` footer when project context is active.
|
||||
|
||||
## Config schema
|
||||
## Config schema (relevant subset)
|
||||
|
||||
All config lives in `~/.takopi/takopi.toml`.
|
||||
See [Config](config.md) for the full reference.
|
||||
|
||||
```toml
|
||||
default_engine = "codex" # optional
|
||||
@@ -0,0 +1,26 @@
|
||||
# Environment variables
|
||||
|
||||
Takopi supports a small set of environment variables for logging and runtime behavior.
|
||||
|
||||
## Logging
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `TAKOPI_LOG_LEVEL` | Minimum log level (default `info`; `--debug` forces `debug`). |
|
||||
| `TAKOPI_LOG_FORMAT` | `console` (default) or `json`. |
|
||||
| `TAKOPI_LOG_COLOR` | Force color on/off (`1/true/yes/on` or `0/false/no/off`). |
|
||||
| `TAKOPI_LOG_FILE` | Append JSON lines to a file. `--debug` defaults this to `debug.log`. |
|
||||
| `TAKOPI_TRACE_PIPELINE` | Log pipeline events at `info` instead of `debug`. |
|
||||
|
||||
## CLI behavior
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `TAKOPI_NO_INTERACTIVE` | Disable interactive prompts (useful for CI / non-TTY). |
|
||||
|
||||
## Engine-specific
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PI_CODING_AGENT_DIR` | Override Pi agent session directory base path. |
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# Reference
|
||||
|
||||
Reference docs are **authoritative and exact**. Use these when you need stable facts, schemas, and contracts.
|
||||
|
||||
If you’re trying to achieve a goal (“enable topics”, “fetch a file”), use **[How-to](../how-to/index.md)**.
|
||||
If you’re trying to understand the *why*, use **[Explanation](../explanation/index.md)**.
|
||||
|
||||
## Most-used reference pages
|
||||
|
||||
- [Commands & directives](commands-and-directives.md)
|
||||
- Message prefixes like `/engine`, `/project`, and `@branch`
|
||||
- In-chat commands like `/cancel`, `/new`, `/ctx`, `/file …`, `/topic …`
|
||||
- [Configuration](config.md)
|
||||
- `takopi.toml` options and defaults
|
||||
- Telegram transport options (sessions, topics, files, voice transcription)
|
||||
|
||||
## Normative behavior
|
||||
|
||||
- [Specification](specification.md)
|
||||
The normative (“MUST/SHOULD/MAY”) contract for:
|
||||
- resume tokens + resume lines
|
||||
- event model
|
||||
- progress/final message semantics
|
||||
- per-thread serialization rules
|
||||
|
||||
## Plugins and extension contracts
|
||||
|
||||
- [Plugin API](plugin-api.md)
|
||||
The **only** supported import surface for plugins: `takopi.api`
|
||||
- [Context resolution](context-resolution.md)
|
||||
How Takopi resolves project + worktree context from directives, replies, and chat ids.
|
||||
|
||||
## Transport reference
|
||||
|
||||
- [Telegram transport](transports/telegram.md)
|
||||
Rate limits, outbox behavior, retries, message editing rules.
|
||||
|
||||
## Runner reference
|
||||
|
||||
These are “engine adapter” implementation details: JSONL formats, mapping rules, and emitted events.
|
||||
|
||||
- [Runners overview](runners/index.md)
|
||||
- Claude:
|
||||
- [runner.md](runners/claude/runner.md)
|
||||
- [stream-json-cheatsheet.md](runners/claude/stream-json-cheatsheet.md)
|
||||
- [takopi-events.md](runners/claude/takopi-events.md)
|
||||
- Codex:
|
||||
- [exec-json-cheatsheet.md](runners/codex/exec-json-cheatsheet.md)
|
||||
- [takopi-events.md](runners/codex/takopi-events.md)
|
||||
- OpenCode:
|
||||
- [runner.md](runners/opencode/runner.md)
|
||||
- [stream-json-cheatsheet.md](runners/opencode/stream-json-cheatsheet.md)
|
||||
- [takopi-events.md](runners/opencode/takopi-events.md)
|
||||
- Pi:
|
||||
- [runner.md](runners/pi/runner.md)
|
||||
- [stream-json-cheatsheet.md](runners/pi/stream-json-cheatsheet.md)
|
||||
- [takopi-events.md](runners/pi/takopi-events.md)
|
||||
|
||||
## For LLM agents
|
||||
|
||||
If you’re an LLM agent contributing to Takopi, start here:
|
||||
|
||||
- [Agent entrypoint](agents/index.md)
|
||||
- [Repo map](agents/repo-map.md)
|
||||
- [Invariants](agents/invariants.md) (runner contract, resume handling, “don’t break this” rules)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Public Plugin API
|
||||
# Plugin API
|
||||
|
||||
Takopi's **public plugin API** is exported from:
|
||||
Takopi’s **public plugin API** is exported from:
|
||||
|
||||
```
|
||||
takopi.api
|
||||
+1
-1
@@ -199,7 +199,7 @@ Claude runner implementation summary (no Takopi domain model changes):
|
||||
2. [x] Define `BACKEND` in `takopi/runners/claude.py`:
|
||||
- `install_cmd`: install command for the `claude` binary
|
||||
- `build_runner`: read `[claude]` config + construct runner
|
||||
3. [x] Add new docs (this file + `claude-stream-json-cheatsheet.md`).
|
||||
3. [x] Add new docs (this file + `stream-json-cheatsheet.md`).
|
||||
4. [x] Add fixtures in `tests/fixtures/` (see below).
|
||||
5. [x] Add unit tests mirroring `tests/test_codex_*` but for Claude translation
|
||||
and resume parsing (recommended, not required for initial handoff).
|
||||
@@ -0,0 +1,9 @@
|
||||
# Runners
|
||||
|
||||
Runner docs describe the **engine-specific** behavior: event shapes, JSON streaming, and integration notes.
|
||||
|
||||
- Claude: [Runner](claude/runner.md), [Stream JSON cheatsheet](claude/stream-json-cheatsheet.md), [Takopi events](claude/takopi-events.md)
|
||||
- Codex: [Exec JSON cheatsheet](codex/exec-json-cheatsheet.md), [Takopi events](codex/takopi-events.md)
|
||||
- OpenCode: [Runner](opencode/runner.md), [Stream JSON cheatsheet](opencode/stream-json-cheatsheet.md), [Takopi events](opencode/takopi-events.md)
|
||||
- Pi: [Runner](pi/runner.md), [Stream JSON cheatsheet](pi/stream-json-cheatsheet.md), [Takopi events](pi/takopi-events.md)
|
||||
|
||||
@@ -44,4 +44,4 @@ OpenCode outputs JSON events with the following types:
|
||||
| `step_finish` | End of a step (reason: "stop" or "tool-calls" when present) |
|
||||
| `error` | Error event |
|
||||
|
||||
See [opencode-stream-json-cheatsheet.md](./opencode-stream-json-cheatsheet.md) for detailed event format documentation.
|
||||
See [stream-json-cheatsheet.md](./stream-json-cheatsheet.md) for detailed event format documentation.
|
||||
@@ -0,0 +1,32 @@
|
||||
# First run
|
||||
|
||||
You’ll run Takopi in a repo, send a task from Telegram, and learn the basic controls.
|
||||
|
||||
## 1) Start Takopi in a repo
|
||||
|
||||
```sh
|
||||
cd ~/dev/your-repo
|
||||
takopi
|
||||
```
|
||||
|
||||
## 2) Send a message to your bot
|
||||
|
||||
Takopi streams progress in chat and posts a final message when the engine finishes.
|
||||
|
||||
If you want to override the default engine for a single message, prefix the first line:
|
||||
|
||||
```
|
||||
/codex explain this code
|
||||
/claude refactor this module
|
||||
```
|
||||
|
||||
## 3) Continue or cancel
|
||||
|
||||
- Continue the same thread by **replying** to any bot message that contains a resume line in the footer.
|
||||
- Cancel an in-flight run by clicking the cancel button or replying to the progress message with `/cancel`.
|
||||
|
||||
## Next
|
||||
|
||||
- [Projects](../how-to/projects.md) and [Worktrees](../how-to/worktrees.md)
|
||||
- [Commands & directives](../reference/commands-and-directives.md)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Tutorials
|
||||
|
||||
Tutorials are **step-by-step learning paths**. If you’re new, start here and follow them in order.
|
||||
|
||||
If you already know what you want to do (e.g. “enable topics” or “fetch a file”), jump to **How-to**.
|
||||
|
||||
## Before you start
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A Telegram account + a bot token
|
||||
- Python **3.14+** and `uv`
|
||||
- At least one engine CLI on your `PATH`: `codex`, `claude`, `opencode`, or `pi`
|
||||
|
||||
## 1) Install and onboard
|
||||
|
||||
Get Takopi installed, create a bot token, capture your chat id, and pick a default engine.
|
||||
|
||||
- [Install & onboard](install-and-onboard.md)
|
||||
|
||||
## 2) Your first run
|
||||
|
||||
Run Takopi in a repo, send your first task from Telegram, and learn the two core controls:
|
||||
|
||||
- reply-to-continue (resume)
|
||||
- cancel a run
|
||||
|
||||
- [First run](first-run.md)
|
||||
|
||||
## What to do next
|
||||
|
||||
Once you can run tasks end-to-end, you’ll usually want:
|
||||
|
||||
- Register a repo as a **project alias**: [How-to: Projects](../how-to/projects.md)
|
||||
- Run work on a branch in a dedicated **worktree**: [How-to: Worktrees](../how-to/worktrees.md)
|
||||
- Keep long-running work organized in threads: [How-to: Topics](../how-to/topics.md)
|
||||
|
||||
When you need exact knobs and defaults:
|
||||
|
||||
- [Reference: Configuration](../reference/config.md)
|
||||
- [Reference: Commands & directives](../reference/commands-and-directives.md)
|
||||
@@ -0,0 +1,56 @@
|
||||
# Install & onboard
|
||||
|
||||
You’ll install Takopi, connect it to Telegram, and generate a working `takopi.toml`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Telegram account
|
||||
- Python 3.14+ and `uv`
|
||||
- At least one supported engine CLI on your `PATH` (`codex`, `claude`, `opencode`, or `pi`)
|
||||
|
||||
## 1) Install Takopi
|
||||
|
||||
```sh
|
||||
uv tool install -U takopi
|
||||
```
|
||||
|
||||
## 2) Run onboarding
|
||||
|
||||
Start Takopi:
|
||||
|
||||
```sh
|
||||
takopi
|
||||
```
|
||||
|
||||
If you want to re-run onboarding later:
|
||||
|
||||
```sh
|
||||
takopi --onboard
|
||||
```
|
||||
|
||||
The wizard walks you through:
|
||||
|
||||
1. Creating a bot token via [@BotFather](https://t.me/BotFather)
|
||||
2. Capturing your `chat_id` (it listens for a message from you)
|
||||
3. Choosing a default engine
|
||||
|
||||
Your configuration lives at `~/.takopi/takopi.toml`.
|
||||
|
||||
## 3) Verify minimal config
|
||||
|
||||
After onboarding you should have something like:
|
||||
|
||||
```toml
|
||||
default_engine = "codex"
|
||||
transport = "telegram"
|
||||
|
||||
[transports.telegram]
|
||||
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
chat_id = 123456789
|
||||
```
|
||||
|
||||
## Next
|
||||
|
||||
- [First run](first-run.md)
|
||||
- [Config reference](../reference/config.md)
|
||||
|
||||
+19
-539
@@ -1,546 +1,26 @@
|
||||
# Takopi User Guide
|
||||
# Takopi User Guide (moved)
|
||||
|
||||
Takopi is a command-line tool that lets you control coding agents—like Codex, Claude, and others—through Telegram. Send a message, and takopi runs the agent in your repo, streaming progress back to your chat. It supports multi-repo workflows, git worktrees, and per-project routing.
|
||||
This guide has been reorganized into smaller Diátaxis pages.
|
||||
|
||||
This guide starts simple and layers on features as you go. Jump to any section or read straight through.
|
||||
## Start here
|
||||
|
||||
## Prerequisites
|
||||
- [Install & onboard](tutorials/install-and-onboard.md)
|
||||
- [First run](tutorials/first-run.md)
|
||||
|
||||
Before you begin, make sure you have:
|
||||
## Common tasks
|
||||
|
||||
- A Telegram account
|
||||
- Python 3.14+ and `uv` installed
|
||||
- At least one supported agent CLI installed and on your `PATH` (codex, claude, opencode, pi)
|
||||
- Basic familiarity with git (especially if you plan to use worktrees)
|
||||
- [Switch engines](how-to/switch-engines.md)
|
||||
- [Projects](how-to/projects.md)
|
||||
- [Worktrees](how-to/worktrees.md)
|
||||
- [Route by chat](how-to/route-by-chat.md)
|
||||
- [Topics](how-to/topics.md)
|
||||
- [Voice notes](how-to/voice-notes.md)
|
||||
- [File transfer](how-to/file-transfer.md)
|
||||
- [Schedule tasks](how-to/schedule-tasks.md)
|
||||
- [Troubleshooting](how-to/troubleshooting.md)
|
||||
|
||||
## Key concepts
|
||||
## Reference
|
||||
|
||||
A few terms you'll see throughout:
|
||||
|
||||
| Term | Meaning |
|
||||
|------|---------|
|
||||
| **Engine** | A coding agent backend (Codex, Claude, opencode, pi) |
|
||||
| **Project** | A repo/workdir alias used for routing (can set default engine, worktrees, chat ID) |
|
||||
| **Worktree** | A git feature that lets you check out multiple branches simultaneously in separate directories |
|
||||
| **Topic** | A Telegram forum thread bound to a project/branch; stores per-topic resume tokens |
|
||||
| **Resume token** | State that allows an engine to continue from where it left off |
|
||||
|
||||
---
|
||||
|
||||
## How conversations work
|
||||
|
||||
Takopi is **stateless by default**. Each message starts a new engine session unless a resume token is present.
|
||||
|
||||
To continue a session:
|
||||
|
||||
- **Reply** to a bot message. Takopi reads the resume token from the footer and resumes that session.
|
||||
- **Forum topics** (optional) store resume tokens per topic and auto-resume for new messages in that topic.
|
||||
Reset with `/new`.
|
||||
- **Chat sessions** (optional) store one resume token per chat (per sender in groups) so new messages
|
||||
auto-resume without replying. Enable with `session_mode = "chat"` and reset with `/new`.
|
||||
State is stored in `telegram_chat_sessions_state.json`.
|
||||
You can hide resume lines by setting `[transports.telegram].show_resume_line = false`
|
||||
when auto-resume is available and a project context is resolved.
|
||||
|
||||
Reply-to-continue always works, even if chat sessions or topics are enabled.
|
||||
|
||||
---
|
||||
|
||||
## 1. Installation and setup
|
||||
|
||||
Install takopi with:
|
||||
|
||||
```sh
|
||||
uv tool install -U takopi
|
||||
```
|
||||
|
||||
Run it once to start the onboarding wizard:
|
||||
|
||||
```sh
|
||||
takopi
|
||||
```
|
||||
|
||||
The wizard walks you through:
|
||||
|
||||
1. Creating a Telegram bot token via [@BotFather](https://t.me/BotFather)
|
||||
2. Capturing your `chat_id` (the wizard listens for a message from you)
|
||||
3. Choosing a default engine
|
||||
|
||||
To re-run onboarding later, use `takopi --onboard`.
|
||||
|
||||
Your configuration is stored at `~/.takopi/takopi.toml`.
|
||||
|
||||
### Minimal configuration
|
||||
|
||||
After onboarding, your config looks something like this:
|
||||
|
||||
```toml
|
||||
default_engine = "codex"
|
||||
transport = "telegram"
|
||||
|
||||
[transports.telegram]
|
||||
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
chat_id = 123456789
|
||||
```
|
||||
|
||||
Optional: split long final responses instead of trimming them:
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
message_overflow = "split" # trim | split
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Your first handoff
|
||||
|
||||
The simplest workflow:
|
||||
|
||||
1. `cd` into any git repository
|
||||
2. Run `takopi`
|
||||
3. Send a message to your bot
|
||||
|
||||
Takopi streams progress in the chat and sends a final response when the agent finishes.
|
||||
|
||||
### Basic controls
|
||||
|
||||
- **Reply** to a bot message to continue the session (takopi reads the resume token in the footer)
|
||||
- **Cancel** a run by clicking the cancel button or replying to the progress message with `/cancel`
|
||||
|
||||
---
|
||||
|
||||
## 3. Switching engines
|
||||
|
||||
Prefix your message with an engine directive to override the default:
|
||||
|
||||
```
|
||||
/codex hard reset the timeline
|
||||
/claude shrink and store artifacts forever
|
||||
/opencode hide their paper until they reply
|
||||
/pi render a diorama of this timeline
|
||||
```
|
||||
|
||||
Directives are only parsed at the start of the first non-empty line.
|
||||
|
||||
### Default agent per chat or topic
|
||||
|
||||
Use `/agent` to view or set a persistent default for the current scope:
|
||||
|
||||
```
|
||||
/agent
|
||||
/agent set claude
|
||||
/agent clear
|
||||
```
|
||||
|
||||
- Inside a forum topic, `/agent set` affects that topic.
|
||||
- In normal chats, it affects the whole chat.
|
||||
- In group chats, only admins can change defaults.
|
||||
|
||||
Precedence (highest to lowest): resume token → `/engine` directive → topic default → chat default → project default → global default.
|
||||
|
||||
### Setting up engines
|
||||
|
||||
Takopi shells out to the agent CLIs. Install them and make sure they're on your `PATH`
|
||||
(codex, claude, opencode, pi). Authentication is handled by each CLI (login,
|
||||
config files, or environment variables).
|
||||
|
||||
---
|
||||
|
||||
## 4. Projects
|
||||
|
||||
For repos you work with often, register them as projects:
|
||||
|
||||
```sh
|
||||
cd ~/dev/happy-gadgets
|
||||
takopi init happy-gadgets
|
||||
```
|
||||
|
||||
This adds a project entry to your config (for example):
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
```
|
||||
|
||||
Now you can target it from anywhere using the `/project` directive:
|
||||
|
||||
```
|
||||
/happy-gadgets pinky-link two threads
|
||||
```
|
||||
|
||||
If you expect to add or edit projects while takopi is running, enable config
|
||||
watching so changes are picked up automatically:
|
||||
|
||||
```toml
|
||||
watch_config = true
|
||||
```
|
||||
|
||||
### Project-specific settings
|
||||
|
||||
Projects can override global defaults:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
default_engine = "claude"
|
||||
worktrees_dir = ".worktrees"
|
||||
worktree_base = "master"
|
||||
```
|
||||
|
||||
### Setting a default project
|
||||
|
||||
If you mostly work in one repo:
|
||||
|
||||
```toml
|
||||
default_project = "happy-gadgets"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Worktrees
|
||||
|
||||
Worktrees let you work on multiple branches without switching back and forth. Use `@branch` to run a task in a dedicated worktree:
|
||||
|
||||
```
|
||||
/happy-gadgets @feat/memory-box freeze artifacts forever
|
||||
```
|
||||
|
||||
Takopi creates (or reuses) a worktree at:
|
||||
|
||||
```
|
||||
<worktrees_root>/<branch>
|
||||
```
|
||||
|
||||
`worktrees_root` is `<project.path>/<worktrees_dir>` unless `worktrees_dir` is an
|
||||
absolute path. If the branch matches the repo's current branch, Takopi runs in the
|
||||
main repo instead of creating a new worktree.
|
||||
|
||||
### Worktree configuration
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
worktrees_dir = ".worktrees" # relative to project path
|
||||
worktree_base = "master" # base branch for new worktrees
|
||||
```
|
||||
|
||||
To avoid `.worktrees/` showing up as untracked, add it to your global gitignore:
|
||||
|
||||
```sh
|
||||
git config --global core.excludesfile ~/.config/git/ignore
|
||||
echo ".worktrees/" >> ~/.config/git/ignore
|
||||
```
|
||||
|
||||
### Context persistence
|
||||
|
||||
Takopi adds a `ctx:` footer to messages with project and branch info. When you reply, this context carries forward—no need to repeat `/project @branch` each time.
|
||||
|
||||
---
|
||||
|
||||
## 6. Per-project chat routing
|
||||
|
||||
Give each project its own Telegram chat:
|
||||
|
||||
```sh
|
||||
takopi chat-id --project happy-gadgets
|
||||
```
|
||||
|
||||
Send any message in the target chat. Takopi captures the `chat_id` and updates your config:
|
||||
|
||||
```toml
|
||||
[projects.happy-gadgets]
|
||||
path = "~/dev/happy-gadgets"
|
||||
chat_id = -1001234567890
|
||||
```
|
||||
|
||||
Messages from that chat automatically route to the project.
|
||||
|
||||
### Rules for chat IDs
|
||||
|
||||
- Each `projects.*.chat_id` must be unique
|
||||
- Project chat IDs must not match `transports.telegram.chat_id`
|
||||
- Telegram uses positive IDs for private chats and negative IDs for groups/supergroups
|
||||
|
||||
### Capture a chat ID without saving
|
||||
|
||||
To see a chat ID without writing to config:
|
||||
|
||||
```sh
|
||||
takopi chat-id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Topics
|
||||
|
||||
Topics bind Telegram forum threads to specific project/branch contexts. They also preserve resume tokens and can store a default agent per topic.
|
||||
|
||||
### Enabling topics
|
||||
|
||||
```toml
|
||||
[transports.telegram.topics]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
Your bot needs **Manage Topics** permission in the group.
|
||||
|
||||
If any `projects.<alias>.chat_id` are configured, topics are managed in those
|
||||
project chats; otherwise topics are managed in the main chat.
|
||||
|
||||
### Topic behavior
|
||||
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│ takopi projects │
|
||||
├────────────────────────────┤
|
||||
│ takopi @master │
|
||||
│ takopi @feat/topics │
|
||||
│ happy-gadgets @master │
|
||||
│ happy-gadgets @feat/camera │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
Each project can have its own forum-enabled supergroup. Topics still
|
||||
include the project name for consistency, but the project is inferred from the
|
||||
chat. Regular messages in that chat also infer the project, so `/project` is
|
||||
usually optional.
|
||||
|
||||
```
|
||||
┌────────────────────────────────┐ ┌───────────────────────────────────┐
|
||||
│ takopi │ │ happy-gadgets │
|
||||
├────────────────────────────────┤ ├───────────────────────────────────┤
|
||||
│ takopi @master │ │ happy-gadgets @master │
|
||||
│ takopi @feat/topics │ │ happy-gadgets @feat/happy-camera │
|
||||
│ takopi @feat/voice │ │ happy-gadgets @feat/memory-box │
|
||||
└────────────────────────────────┘ └───────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Topic commands
|
||||
|
||||
Run these inside a topic thread:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/topic <project> @branch` | Create a new topic bound to context |
|
||||
| `/ctx` | Show the current binding |
|
||||
| `/ctx set <project> @branch` | Update the binding |
|
||||
| `/ctx clear` | Remove the binding |
|
||||
| `/new` | Clear resume tokens for this topic |
|
||||
|
||||
In project chats, omit the project: `/topic @branch` or `/ctx set @branch`.
|
||||
|
||||
### Configuration examples
|
||||
|
||||
**Main chat only:**
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
chat_id = -1001234567890
|
||||
# show_resume_line = false
|
||||
|
||||
[transports.telegram.topics]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
**Project chats:**
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
chat_id = 123456789 # main chat (private, for non-project messages)
|
||||
# show_resume_line = false
|
||||
|
||||
[transports.telegram.topics]
|
||||
enabled = true
|
||||
|
||||
[projects.takopi]
|
||||
path = "~/dev/takopi"
|
||||
chat_id = -1001111111111 # forum-enabled group
|
||||
```
|
||||
|
||||
Topic state is stored in `telegram_topics_state.json` next to your config file. Chat defaults live in `telegram_chat_prefs_state.json`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Voice notes
|
||||
|
||||
Dictate tasks instead of typing:
|
||||
|
||||
```toml
|
||||
[transports.telegram]
|
||||
voice_transcription = true
|
||||
voice_transcription_model = "gpt-4o-mini-transcribe" # optional
|
||||
```
|
||||
|
||||
Set `OPENAI_API_KEY` in your environment (uses OpenAI's transcription API with the
|
||||
`gpt-4o-mini-transcribe` model by default). To use a local OpenAI-compatible
|
||||
Whisper server, also set `OPENAI_BASE_URL` (for example, `http://localhost:8000/v1`)
|
||||
and a dummy `OPENAI_API_KEY` if your server ignores it. If your server requires a
|
||||
specific model name, set `voice_transcription_model` accordingly (for example,
|
||||
`whisper-1`).
|
||||
|
||||
When you send a voice note, takopi transcribes it and runs the result as a normal text message. If transcription fails, you'll get an error message and the run is skipped.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 9. File transfer
|
||||
|
||||
Upload files into the active repo/worktree or fetch files back into Telegram.
|
||||
|
||||
### Upload a file
|
||||
|
||||
Send a document with a caption:
|
||||
|
||||
```
|
||||
/file put <path>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
/file put docs/spec.pdf
|
||||
/file put /happy-gadgets @feat/camera assets/logo.png
|
||||
```
|
||||
|
||||
If you send a file **without a caption**, takopi saves it to:
|
||||
|
||||
```
|
||||
incoming/<original_filename>
|
||||
```
|
||||
|
||||
If you set `auto_put_mode = "prompt"`, a caption/question will start a run
|
||||
immediately after upload, with the prompt annotated with the uploaded path.
|
||||
|
||||
Use `--force` to overwrite an existing file:
|
||||
|
||||
```
|
||||
/file put --force docs/spec.pdf
|
||||
```
|
||||
|
||||
### Fetch a file
|
||||
|
||||
Send:
|
||||
|
||||
```
|
||||
/file get <path>
|
||||
```
|
||||
|
||||
Directories are zipped automatically.
|
||||
|
||||
### File transfer config
|
||||
|
||||
```toml
|
||||
[transports.telegram.files]
|
||||
enabled = true
|
||||
auto_put = true
|
||||
auto_put_mode = "upload"
|
||||
uploads_dir = "incoming"
|
||||
allowed_user_ids = [123456789]
|
||||
deny_globs = [".git/**", ".env", ".envrc", "**/*.pem", "**/.ssh/**"]
|
||||
```
|
||||
|
||||
Notes:
|
||||
- File transfer is **disabled by default**.
|
||||
- If `allowed_user_ids` is empty, private chats are allowed and group usage requires admin privileges.
|
||||
|
||||
---
|
||||
|
||||
## 10. Configuration reference
|
||||
|
||||
Full example with all options:
|
||||
|
||||
```toml
|
||||
# Global defaults
|
||||
default_engine = "codex"
|
||||
default_project = "takopi"
|
||||
transport = "telegram"
|
||||
watch_config = true # hot-reload on config changes (except transport)
|
||||
|
||||
[transports.telegram]
|
||||
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
chat_id = 123456789
|
||||
voice_transcription = true
|
||||
voice_transcription_model = "gpt-4o-mini-transcribe"
|
||||
session_mode = "stateless" # or "chat" for auto-resume per chat
|
||||
show_resume_line = true
|
||||
|
||||
[transports.telegram.files]
|
||||
enabled = true
|
||||
auto_put = true
|
||||
auto_put_mode = "upload"
|
||||
uploads_dir = "incoming"
|
||||
allowed_user_ids = [123456789]
|
||||
deny_globs = [".git/**", ".env", ".envrc", "**/*.pem", "**/.ssh/**"]
|
||||
|
||||
[transports.telegram.topics]
|
||||
enabled = true
|
||||
scope = "auto"
|
||||
|
||||
# Project definitions
|
||||
[projects.takopi]
|
||||
path = "~/dev/takopi"
|
||||
default_engine = "codex"
|
||||
worktrees_dir = ".worktrees"
|
||||
worktree_base = "master"
|
||||
# chat_id = -1001234567890 # optional: dedicated chat
|
||||
|
||||
[projects.happy-planet]
|
||||
path = "~/dev/happy-planet"
|
||||
default_engine = "claude"
|
||||
worktrees_dir = "~/.takopi/worktrees/happy-planet"
|
||||
worktree_base = "develop"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Command cheatsheet
|
||||
|
||||
### Message directives
|
||||
|
||||
| Directive | Example | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `/engine` | `/codex make threads resolve their differences` | Use a specific engine |
|
||||
| `/project` | `/happy-gadgets add escape-pod` | Target a project |
|
||||
| `@branch` | `@feat/happy-camera rewind to checkpoint` | Run in a worktree |
|
||||
| Combined | `/happy-gadgets @feat/flower-pin observe unseen` | Project + branch |
|
||||
|
||||
### In-chat commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/cancel` | Reply to the progress message to stop the current run |
|
||||
| `/file put <path>` | Upload a document into the repo/worktree |
|
||||
| `/file get <path>` | Fetch a file (directories are zipped) |
|
||||
| `/agent` | Show/set the default agent for the current scope |
|
||||
| `/topic <project> @branch` | Create/bind a topic |
|
||||
| `/ctx` | Show current context |
|
||||
| `/ctx set <project> @branch` | Update context binding |
|
||||
| `/ctx clear` | Remove context binding |
|
||||
| `/new` | Clear stored resume tokens (topic or chat session) |
|
||||
|
||||
### CLI commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `takopi` | Start the bot (runs onboarding if first time) |
|
||||
| `takopi --onboard` | Re-run onboarding wizard |
|
||||
| `takopi init <alias>` | Register current directory as a project |
|
||||
| `takopi chat-id` | Capture a chat ID |
|
||||
| `takopi chat-id --project <alias>` | Set a project's chat ID |
|
||||
| `takopi --debug` | Write debug logs to `debug.log` |
|
||||
|
||||
---
|
||||
|
||||
## 12. Tips
|
||||
|
||||
### Schedule tasks with Telegram
|
||||
|
||||
Telegram has a native message scheduling feature that works seamlessly with takopi. Long-press the send button and choose "Schedule Message" to run tasks at a specific time. You can also set up recurring schedules (daily, weekly, etc.) for automated workflows.
|
||||
|
||||
---
|
||||
|
||||
## 13. Troubleshooting
|
||||
|
||||
If something isn't working, rerun with `takopi --debug` and check `debug.log`
|
||||
for errors. Include it when reporting issues.
|
||||
- [Configuration](reference/config.md)
|
||||
- [Commands & directives](reference/commands-and-directives.md)
|
||||
- [Context resolution](reference/context-resolution.md)
|
||||
|
||||
+99
-27
@@ -34,6 +34,25 @@ markdown_extensions:
|
||||
|
||||
plugins:
|
||||
- search
|
||||
- redirects:
|
||||
redirect_maps:
|
||||
architecture.md: explanation/architecture.md
|
||||
specification.md: reference/specification.md
|
||||
public-api.md: reference/plugin-api.md
|
||||
transports/telegram.md: reference/transports/telegram.md
|
||||
adding-a-runner.md: how-to/add-a-runner.md
|
||||
projects.md: reference/context-resolution.md
|
||||
runner/claude/claude-runner.md: reference/runners/claude/runner.md
|
||||
runner/claude/claude-stream-json-cheatsheet.md: reference/runners/claude/stream-json-cheatsheet.md
|
||||
runner/claude/claude-takopi-events.md: reference/runners/claude/takopi-events.md
|
||||
runner/codex/exec-json-cheatsheet.md: reference/runners/codex/exec-json-cheatsheet.md
|
||||
runner/codex/codex-takopi-events.md: reference/runners/codex/takopi-events.md
|
||||
runner/opencode/opencode-runner.md: reference/runners/opencode/runner.md
|
||||
runner/opencode/opencode-stream-json-cheatsheet.md: reference/runners/opencode/stream-json-cheatsheet.md
|
||||
runner/opencode/opencode-takopi-events.md: reference/runners/opencode/takopi-events.md
|
||||
runner/pi/pi-runner.md: reference/runners/pi/runner.md
|
||||
runner/pi/pi-stream-json-cheatsheet.md: reference/runners/pi/stream-json-cheatsheet.md
|
||||
runner/pi/pi-takopi-events.md: reference/runners/pi/takopi-events.md
|
||||
- mkdocstrings:
|
||||
handlers:
|
||||
python:
|
||||
@@ -42,36 +61,89 @@ plugins:
|
||||
show_source: false
|
||||
- llmstxt:
|
||||
markdown_description: |
|
||||
Takopi is a Telegram bridge for coding agents. Use this index to find the most relevant docs pages.
|
||||
For LLM ingestion, prefer the linked `.md` pages.
|
||||
Takopi is a Telegram bridge for coding agents.
|
||||
For LLM ingestion, prefer the linked `.md` pages (Diátaxis: Tutorials / How-to / Reference / Explanation).
|
||||
full_output: llms-full.txt
|
||||
sections:
|
||||
Getting started:
|
||||
- index.md: Overview + quick start
|
||||
- user-guide.md: How to use Takopi end-to-end
|
||||
- projects.md: Project aliases + worktrees
|
||||
Plugin development:
|
||||
- plugins.md: Plugin system overview
|
||||
- public-api.md: Stable plugin API surface
|
||||
- adding-a-runner.md: Engine runner integration
|
||||
Internals:
|
||||
- architecture.md: Core architecture
|
||||
- specification.md: Behavior spec
|
||||
- developing.md: Developer guide
|
||||
Start here:
|
||||
- index.md: Overview + doc map
|
||||
- tutorials/install-and-onboard.md: Install Takopi + onboarding
|
||||
- tutorials/first-run.md: First run
|
||||
- how-to/projects.md: Projects (routing)
|
||||
- how-to/worktrees.md: Worktrees (@branch)
|
||||
- how-to/topics.md: Telegram topics
|
||||
- how-to/file-transfer.md: File transfer
|
||||
Reference:
|
||||
- reference/commands-and-directives.md: Commands, directives, and CLI flags
|
||||
- reference/config.md: Config schema and defaults
|
||||
- reference/specification.md: Normative behavior spec
|
||||
- reference/plugin-api.md: Stable plugin API surface
|
||||
- reference/context-resolution.md: Directive parsing + worktree rules
|
||||
- reference/transports/telegram.md: Telegram transport details
|
||||
Contributors & agents:
|
||||
- how-to/dev-setup.md: Dev setup + checks
|
||||
- explanation/module-map.md: Module responsibilities
|
||||
- reference/env-vars.md: Environment variables
|
||||
- reference/agents/invariants.md: Critical invariants
|
||||
- reference/agents/repo-map.md: Codebase entrypoints
|
||||
Optional:
|
||||
- transports/telegram.md: Telegram transport details
|
||||
- reference/runners/index.md: Runner docs index
|
||||
- reference/runners/claude/stream-json-cheatsheet.md: Claude stream-json notes
|
||||
- reference/runners/codex/exec-json-cheatsheet.md: Codex exec --json notes
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- User guide: user-guide.md
|
||||
- Projects: projects.md
|
||||
- Plugins:
|
||||
- Plugin system: plugins.md
|
||||
- Public API: public-api.md
|
||||
- Adding a runner: adding-a-runner.md
|
||||
- Internals:
|
||||
- Architecture: architecture.md
|
||||
- Specification: specification.md
|
||||
- Developing: developing.md
|
||||
- Transports:
|
||||
- Telegram: transports/telegram.md
|
||||
- Tutorials:
|
||||
- Overview: tutorials/index.md
|
||||
- Install & onboard: tutorials/install-and-onboard.md
|
||||
- First run: tutorials/first-run.md
|
||||
- How-to:
|
||||
- Overview: how-to/index.md
|
||||
- Switch engines: how-to/switch-engines.md
|
||||
- Projects: how-to/projects.md
|
||||
- Worktrees: how-to/worktrees.md
|
||||
- Route by chat: how-to/route-by-chat.md
|
||||
- Topics: how-to/topics.md
|
||||
- Voice notes: how-to/voice-notes.md
|
||||
- File transfer: how-to/file-transfer.md
|
||||
- Schedule tasks: how-to/schedule-tasks.md
|
||||
- Troubleshooting: how-to/troubleshooting.md
|
||||
- Write a plugin: how-to/write-a-plugin.md
|
||||
- Add a runner: how-to/add-a-runner.md
|
||||
- Dev setup: how-to/dev-setup.md
|
||||
- Reference:
|
||||
- Overview: reference/index.md
|
||||
- Commands & directives: reference/commands-and-directives.md
|
||||
- Configuration: reference/config.md
|
||||
- Environment variables: reference/env-vars.md
|
||||
- Specification: reference/specification.md
|
||||
- Plugin API: reference/plugin-api.md
|
||||
- Context resolution: reference/context-resolution.md
|
||||
- Telegram transport: reference/transports/telegram.md
|
||||
- Runners:
|
||||
- Overview: reference/runners/index.md
|
||||
- Claude:
|
||||
- Runner: reference/runners/claude/runner.md
|
||||
- Stream JSON cheatsheet: reference/runners/claude/stream-json-cheatsheet.md
|
||||
- Takopi events: reference/runners/claude/takopi-events.md
|
||||
- Codex:
|
||||
- Exec JSON cheatsheet: reference/runners/codex/exec-json-cheatsheet.md
|
||||
- Takopi events: reference/runners/codex/takopi-events.md
|
||||
- OpenCode:
|
||||
- Runner: reference/runners/opencode/runner.md
|
||||
- Stream JSON cheatsheet: reference/runners/opencode/stream-json-cheatsheet.md
|
||||
- Takopi events: reference/runners/opencode/takopi-events.md
|
||||
- Pi:
|
||||
- Runner: reference/runners/pi/runner.md
|
||||
- Stream JSON cheatsheet: reference/runners/pi/stream-json-cheatsheet.md
|
||||
- Takopi events: reference/runners/pi/takopi-events.md
|
||||
- For agents:
|
||||
- Agent entrypoint: reference/agents/index.md
|
||||
- Repo map: reference/agents/repo-map.md
|
||||
- Invariants: reference/agents/invariants.md
|
||||
- Explanation:
|
||||
- Overview: explanation/index.md
|
||||
- Architecture: explanation/architecture.md
|
||||
- Routing & sessions: explanation/routing-and-sessions.md
|
||||
- Plugin system: explanation/plugin-system.md
|
||||
- Module map: explanation/module-map.md
|
||||
|
||||
@@ -63,6 +63,7 @@ docs = [
|
||||
"mkdocs>=1.6",
|
||||
"mkdocs-llmstxt>=0.5.0",
|
||||
"mkdocs-material>=9.7",
|
||||
"mkdocs-redirects>=1.2.2",
|
||||
"mkdocstrings[python]>=0.29",
|
||||
]
|
||||
|
||||
|
||||
@@ -46,17 +46,17 @@ register a project with `takopi init happy-gadgets`, then target it from anywher
|
||||
|
||||
mention a branch to run an agent in a dedicated worktree `/happy-gadgets @feat/memory-box freeze artifacts forever`.
|
||||
|
||||
see [`docs/user-guide.md`](docs/user-guide.md) for configuration, worktrees, topics, file transfer, and more.
|
||||
see [`docs/index.md`](docs/index.md) (or [`docs/user-guide.md`](docs/user-guide.md)) for configuration, worktrees, topics, file transfer, and more.
|
||||
|
||||
## plugins
|
||||
|
||||
takopi supports entrypoint-based plugins for engines, transports, and commands.
|
||||
|
||||
see [`docs/plugins.md`](docs/plugins.md) and [`docs/public-api.md`](docs/public-api.md).
|
||||
see [`docs/how-to/write-a-plugin.md`](docs/how-to/write-a-plugin.md) and [`docs/reference/plugin-api.md`](docs/reference/plugin-api.md).
|
||||
|
||||
## development
|
||||
|
||||
see [`docs/specification.md`](docs/specification.md) and [`docs/developing.md`](docs/developing.md).
|
||||
see [`docs/reference/specification.md`](docs/reference/specification.md) and [`docs/developing.md`](docs/developing.md).
|
||||
|
||||
## community
|
||||
|
||||
|
||||
@@ -544,6 +544,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-redirects"
|
||||
version = "1.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mkdocs" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/a8/6d44a6cf07e969c7420cb36ab287b0669da636a2044de38a7d2208d5a758/mkdocs_redirects-1.2.2.tar.gz", hash = "sha256:3094981b42ffab29313c2c1b8ac3969861109f58b2dd58c45fc81cd44bfa0095", size = 7162, upload-time = "2024-11-07T14:57:21.109Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/ec/38443b1f2a3821bbcb24e46cd8ba979154417794d54baf949fefde1c2146/mkdocs_redirects-1.2.2-py3-none-any.whl", hash = "sha256:7dbfa5647b79a3589da4401403d69494bd1f4ad03b9c15136720367e1f340ed5", size = 6142, upload-time = "2024-11-07T14:57:19.143Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocstrings"
|
||||
version = "1.0.0"
|
||||
@@ -1029,6 +1041,7 @@ docs = [
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-llmstxt" },
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "mkdocs-redirects" },
|
||||
{ name = "mkdocstrings", extra = ["python"] },
|
||||
]
|
||||
|
||||
@@ -1062,6 +1075,7 @@ docs = [
|
||||
{ name = "mkdocs", specifier = ">=1.6" },
|
||||
{ name = "mkdocs-llmstxt", specifier = ">=0.5.0" },
|
||||
{ name = "mkdocs-material", specifier = ">=9.7" },
|
||||
{ name = "mkdocs-redirects", specifier = ">=1.2.2" },
|
||||
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.29" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user