From e292c99ab09cacfb16e5fce991f7e9cd3b8dbbfe Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Tue, 13 Jan 2026 15:59:27 +0400 Subject: [PATCH] docs: restructure docs into diataxis (#121) --- docs/developing.md | 380 +----------- docs/{ => explanation}/architecture.md | 0 docs/explanation/index.md | 43 ++ docs/explanation/module-map.md | 81 +++ docs/explanation/plugin-system.md | 88 +++ docs/explanation/routing-and-sessions.md | 44 ++ .../add-a-runner.md} | 0 docs/how-to/dev-setup.md | 32 + docs/how-to/file-transfer.md | 59 ++ docs/how-to/index.md | 37 ++ docs/how-to/projects.md | 57 ++ docs/how-to/route-by-chat.md | 39 ++ docs/how-to/schedule-tasks.md | 7 + docs/how-to/switch-engines.md | 43 ++ docs/how-to/topics.md | 53 ++ docs/how-to/troubleshooting.md | 10 + docs/how-to/voice-notes.md | 27 + docs/how-to/worktrees.md | 42 ++ docs/how-to/write-a-plugin.md | 122 ++++ docs/index.md | 109 +++- docs/plugins.md | 307 +--------- docs/reference/agents/index.md | 7 + docs/reference/agents/invariants.md | 35 ++ docs/reference/agents/repo-map.md | 40 ++ docs/reference/commands-and-directives.md | 71 +++ docs/reference/config.md | 109 ++++ .../context-resolution.md} | 9 +- docs/reference/env-vars.md | 26 + docs/reference/index.md | 65 ++ .../plugin-api.md} | 4 +- .../runners/claude/runner.md} | 0 .../runners/claude/stream-json-cheatsheet.md} | 0 .../runners/claude/takopi-events.md} | 2 +- .../runners}/codex/exec-json-cheatsheet.md | 0 .../runners/codex/takopi-events.md} | 0 docs/reference/runners/index.md | 9 + .../runners/opencode/runner.md} | 2 +- .../opencode/stream-json-cheatsheet.md} | 0 .../runners/opencode/takopi-events.md} | 0 .../runners/pi/runner.md} | 0 .../runners/pi/stream-json-cheatsheet.md} | 0 .../runners/pi/takopi-events.md} | 0 docs/{ => reference}/specification.md | 0 docs/{ => reference}/transports/telegram.md | 0 docs/tutorials/first-run.md | 32 + docs/tutorials/index.md | 41 ++ docs/tutorials/install-and-onboard.md | 56 ++ docs/user-guide.md | 558 +----------------- mkdocs.yml | 126 +++- pyproject.toml | 1 + readme.md | 6 +- uv.lock | 14 + 52 files changed, 1538 insertions(+), 1255 deletions(-) rename docs/{ => explanation}/architecture.md (100%) create mode 100644 docs/explanation/index.md create mode 100644 docs/explanation/module-map.md create mode 100644 docs/explanation/plugin-system.md create mode 100644 docs/explanation/routing-and-sessions.md rename docs/{adding-a-runner.md => how-to/add-a-runner.md} (100%) create mode 100644 docs/how-to/dev-setup.md create mode 100644 docs/how-to/file-transfer.md create mode 100644 docs/how-to/index.md create mode 100644 docs/how-to/projects.md create mode 100644 docs/how-to/route-by-chat.md create mode 100644 docs/how-to/schedule-tasks.md create mode 100644 docs/how-to/switch-engines.md create mode 100644 docs/how-to/topics.md create mode 100644 docs/how-to/troubleshooting.md create mode 100644 docs/how-to/voice-notes.md create mode 100644 docs/how-to/worktrees.md create mode 100644 docs/how-to/write-a-plugin.md create mode 100644 docs/reference/agents/index.md create mode 100644 docs/reference/agents/invariants.md create mode 100644 docs/reference/agents/repo-map.md create mode 100644 docs/reference/commands-and-directives.md create mode 100644 docs/reference/config.md rename docs/{projects.md => reference/context-resolution.md} (93%) create mode 100644 docs/reference/env-vars.md create mode 100644 docs/reference/index.md rename docs/{public-api.md => reference/plugin-api.md} (99%) rename docs/{runner/claude/claude-runner.md => reference/runners/claude/runner.md} (100%) rename docs/{runner/claude/claude-stream-json-cheatsheet.md => reference/runners/claude/stream-json-cheatsheet.md} (100%) rename docs/{runner/claude/claude-takopi-events.md => reference/runners/claude/takopi-events.md} (99%) rename docs/{runner => reference/runners}/codex/exec-json-cheatsheet.md (100%) rename docs/{runner/codex/codex-takopi-events.md => reference/runners/codex/takopi-events.md} (100%) create mode 100644 docs/reference/runners/index.md rename docs/{runner/opencode/opencode-runner.md => reference/runners/opencode/runner.md} (89%) rename docs/{runner/opencode/opencode-stream-json-cheatsheet.md => reference/runners/opencode/stream-json-cheatsheet.md} (100%) rename docs/{runner/opencode/opencode-takopi-events.md => reference/runners/opencode/takopi-events.md} (100%) rename docs/{runner/pi/pi-runner.md => reference/runners/pi/runner.md} (100%) rename docs/{runner/pi/pi-stream-json-cheatsheet.md => reference/runners/pi/stream-json-cheatsheet.md} (100%) rename docs/{runner/pi/pi-takopi-events.md => reference/runners/pi/takopi-events.md} (100%) rename docs/{ => reference}/specification.md (100%) rename docs/{ => reference}/transports/telegram.md (100%) create mode 100644 docs/tutorials/first-run.md create mode 100644 docs/tutorials/index.md create mode 100644 docs/tutorials/install-and-onboard.md diff --git a/docs/developing.md b/docs/developing.md index acbfc06..2a7889a 100644 --- a/docs/developing.md +++ b/docs/developing.md @@ -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 ` ``, `` `claude --resume ` ``, `` `pi --session ` ``) -- 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 ` 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 ` `` routes to Claude, `` `pi --session ` `` routes to Pi) -- Selected runner spawns with resume (e.g. `codex exec --json resume -`, `pi --print --mode json --session `) -- 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) diff --git a/docs/architecture.md b/docs/explanation/architecture.md similarity index 100% rename from docs/architecture.md rename to docs/explanation/architecture.md diff --git a/docs/explanation/index.md b/docs/explanation/index.md new file mode 100644 index 0000000..1ed2390 --- /dev/null +++ b/docs/explanation/index.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) diff --git a/docs/explanation/module-map.md b/docs/explanation/module-map.md new file mode 100644 index 0000000..ccd185a --- /dev/null +++ b/docs/explanation/module-map.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). | + diff --git a/docs/explanation/plugin-system.md b/docs/explanation/plugin-system.md new file mode 100644 index 0000000..83badbb --- /dev/null +++ b/docs/explanation/plugin-system.md @@ -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) diff --git a/docs/explanation/routing-and-sessions.md b/docs/explanation/routing-and-sessions.md new file mode 100644 index 0000000..6490069 --- /dev/null +++ b/docs/explanation/routing-and-sessions.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) + diff --git a/docs/adding-a-runner.md b/docs/how-to/add-a-runner.md similarity index 100% rename from docs/adding-a-runner.md rename to docs/how-to/add-a-runner.md diff --git a/docs/how-to/dev-setup.md b/docs/how-to/dev-setup.md new file mode 100644 index 0000000..743c3e2 --- /dev/null +++ b/docs/how-to/dev-setup.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 +``` + diff --git a/docs/how-to/file-transfer.md b/docs/how-to/file-transfer.md new file mode 100644 index 0000000..6d404cb --- /dev/null +++ b/docs/how-to/file-transfer.md @@ -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 +``` + +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/`. + +Use `--force` to overwrite: + +``` +/file put --force docs/spec.pdf +``` + +## Fetch a file (`/file get`) + +Send: + +``` +/file get +``` + +Directories are zipped automatically. + +## Related + +- [Commands & directives](../reference/commands-and-directives.md) +- [Config reference](../reference/config.md) + diff --git a/docs/how-to/index.md b/docs/how-to/index.md new file mode 100644 index 0000000..0c403c1 --- /dev/null +++ b/docs/how-to/index.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)**. diff --git a/docs/how-to/projects.md b/docs/how-to/projects.md new file mode 100644 index 0000000..08f8a4f --- /dev/null +++ b/docs/how-to/projects.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) + diff --git a/docs/how-to/route-by-chat.md b/docs/how-to/route-by-chat.md new file mode 100644 index 0000000..3a048c7 --- /dev/null +++ b/docs/how-to/route-by-chat.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) + diff --git a/docs/how-to/schedule-tasks.md b/docs/how-to/schedule-tasks.md new file mode 100644 index 0000000..f0ebced --- /dev/null +++ b/docs/how-to/schedule-tasks.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. + diff --git a/docs/how-to/switch-engines.md b/docs/how-to/switch-engines.md new file mode 100644 index 0000000..e0fcdd7 --- /dev/null +++ b/docs/how-to/switch-engines.md @@ -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) + diff --git a/docs/how-to/topics.md b/docs/how-to/topics.md new file mode 100644 index 0000000..d39e5a6 --- /dev/null +++ b/docs/how-to/topics.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..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 @branch` | Create a new topic bound to context | +| `/ctx` | Show the current binding | +| `/ctx set @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) diff --git a/docs/how-to/troubleshooting.md b/docs/how-to/troubleshooting.md new file mode 100644 index 0000000..6dc842d --- /dev/null +++ b/docs/how-to/troubleshooting.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. + diff --git a/docs/how-to/voice-notes.md b/docs/how-to/voice-notes.md new file mode 100644 index 0000000..78f24a5 --- /dev/null +++ b/docs/how-to/voice-notes.md @@ -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) + diff --git a/docs/how-to/worktrees.md b/docs/how-to/worktrees.md new file mode 100644 index 0000000..d9bee2e --- /dev/null +++ b/docs/how-to/worktrees.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) + diff --git a/docs/how-to/write-a-plugin.md b/docs/how-to/write-a-plugin.md new file mode 100644 index 0000000..da6eb1f --- /dev/null +++ b/docs/how-to/write-a-plugin.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 `/` 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.]`: + +```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) diff --git a/docs/index.md b/docs/index.md index d9a816f..3347535 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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) diff --git a/docs/plugins.md b/docs/plugins.md index c0e9f87..655dae5 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -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 ") - 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.]`: - -```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. diff --git a/docs/reference/agents/index.md b/docs/reference/agents/index.md new file mode 100644 index 0000000..0697e12 --- /dev/null +++ b/docs/reference/agents/index.md @@ -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) + diff --git a/docs/reference/agents/invariants.md b/docs/reference/agents/invariants.md new file mode 100644 index 0000000..d5e62b3 --- /dev/null +++ b/docs/reference/agents/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 `). + +- 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. + diff --git a/docs/reference/agents/repo-map.md b/docs/reference/agents/repo-map.md new file mode 100644 index 0000000..06e9693 --- /dev/null +++ b/docs/reference/agents/repo-map.md @@ -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` + diff --git a/docs/reference/commands-and-directives.md b/docs/reference/commands-and-directives.md new file mode 100644 index 0000000..ad6befb --- /dev/null +++ b/docs/reference/commands-and-directives.md @@ -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: @` `` +- Without branch: `` `ctx: ` `` + +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 ` | Upload a document into the repo/worktree (requires file transfer enabled). | +| `/file get ` | Fetch a file or directory back into Telegram. | +| `/topic @branch` | Create/bind a topic (topics enabled). | +| `/ctx` | Show topic context binding (topics enabled). | +| `/ctx set @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 ` | Run with a specific engine (e.g. `takopi codex`). | +| `takopi init ` | Register the current repo as a project. | +| `takopi chat-id` | Capture the current chat id. | +| `takopi chat-id --project ` | 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 ` | 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. | + diff --git a/docs/reference/config.md b/docs/reference/config.md new file mode 100644 index 0000000..22c12f8 --- /dev/null +++ b/docs/reference/config.md @@ -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.` + +```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.` + +Plugin-specific configuration lives under `[plugins.]` 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. + diff --git a/docs/projects.md b/docs/reference/context-resolution.md similarity index 93% rename from docs/projects.md rename to docs/reference/context-resolution.md index 7ace1c3..353b845 100644 --- a/docs/projects.md +++ b/docs/reference/context-resolution.md @@ -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 diff --git a/docs/reference/env-vars.md b/docs/reference/env-vars.md new file mode 100644 index 0000000..af14864 --- /dev/null +++ b/docs/reference/env-vars.md @@ -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. | + diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..9759e15 --- /dev/null +++ b/docs/reference/index.md @@ -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) diff --git a/docs/public-api.md b/docs/reference/plugin-api.md similarity index 99% rename from docs/public-api.md rename to docs/reference/plugin-api.md index 05a9694..0dde3cf 100644 --- a/docs/public-api.md +++ b/docs/reference/plugin-api.md @@ -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 diff --git a/docs/runner/claude/claude-runner.md b/docs/reference/runners/claude/runner.md similarity index 100% rename from docs/runner/claude/claude-runner.md rename to docs/reference/runners/claude/runner.md diff --git a/docs/runner/claude/claude-stream-json-cheatsheet.md b/docs/reference/runners/claude/stream-json-cheatsheet.md similarity index 100% rename from docs/runner/claude/claude-stream-json-cheatsheet.md rename to docs/reference/runners/claude/stream-json-cheatsheet.md diff --git a/docs/runner/claude/claude-takopi-events.md b/docs/reference/runners/claude/takopi-events.md similarity index 99% rename from docs/runner/claude/claude-takopi-events.md rename to docs/reference/runners/claude/takopi-events.md index 44bf61e..2fa80af 100644 --- a/docs/runner/claude/claude-takopi-events.md +++ b/docs/reference/runners/claude/takopi-events.md @@ -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). diff --git a/docs/runner/codex/exec-json-cheatsheet.md b/docs/reference/runners/codex/exec-json-cheatsheet.md similarity index 100% rename from docs/runner/codex/exec-json-cheatsheet.md rename to docs/reference/runners/codex/exec-json-cheatsheet.md diff --git a/docs/runner/codex/codex-takopi-events.md b/docs/reference/runners/codex/takopi-events.md similarity index 100% rename from docs/runner/codex/codex-takopi-events.md rename to docs/reference/runners/codex/takopi-events.md diff --git a/docs/reference/runners/index.md b/docs/reference/runners/index.md new file mode 100644 index 0000000..f4059f4 --- /dev/null +++ b/docs/reference/runners/index.md @@ -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) + diff --git a/docs/runner/opencode/opencode-runner.md b/docs/reference/runners/opencode/runner.md similarity index 89% rename from docs/runner/opencode/opencode-runner.md rename to docs/reference/runners/opencode/runner.md index 5096fc7..b4a89ec 100644 --- a/docs/runner/opencode/opencode-runner.md +++ b/docs/reference/runners/opencode/runner.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. diff --git a/docs/runner/opencode/opencode-stream-json-cheatsheet.md b/docs/reference/runners/opencode/stream-json-cheatsheet.md similarity index 100% rename from docs/runner/opencode/opencode-stream-json-cheatsheet.md rename to docs/reference/runners/opencode/stream-json-cheatsheet.md diff --git a/docs/runner/opencode/opencode-takopi-events.md b/docs/reference/runners/opencode/takopi-events.md similarity index 100% rename from docs/runner/opencode/opencode-takopi-events.md rename to docs/reference/runners/opencode/takopi-events.md diff --git a/docs/runner/pi/pi-runner.md b/docs/reference/runners/pi/runner.md similarity index 100% rename from docs/runner/pi/pi-runner.md rename to docs/reference/runners/pi/runner.md diff --git a/docs/runner/pi/pi-stream-json-cheatsheet.md b/docs/reference/runners/pi/stream-json-cheatsheet.md similarity index 100% rename from docs/runner/pi/pi-stream-json-cheatsheet.md rename to docs/reference/runners/pi/stream-json-cheatsheet.md diff --git a/docs/runner/pi/pi-takopi-events.md b/docs/reference/runners/pi/takopi-events.md similarity index 100% rename from docs/runner/pi/pi-takopi-events.md rename to docs/reference/runners/pi/takopi-events.md diff --git a/docs/specification.md b/docs/reference/specification.md similarity index 100% rename from docs/specification.md rename to docs/reference/specification.md diff --git a/docs/transports/telegram.md b/docs/reference/transports/telegram.md similarity index 100% rename from docs/transports/telegram.md rename to docs/reference/transports/telegram.md diff --git a/docs/tutorials/first-run.md b/docs/tutorials/first-run.md new file mode 100644 index 0000000..c0e1356 --- /dev/null +++ b/docs/tutorials/first-run.md @@ -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) + diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md new file mode 100644 index 0000000..2dfd18e --- /dev/null +++ b/docs/tutorials/index.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) diff --git a/docs/tutorials/install-and-onboard.md b/docs/tutorials/install-and-onboard.md new file mode 100644 index 0000000..d4e1feb --- /dev/null +++ b/docs/tutorials/install-and-onboard.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) + diff --git a/docs/user-guide.md b/docs/user-guide.md index 9cee041..1cd8218 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -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` is `/` 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..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 @branch` | Create a new topic bound to context | -| `/ctx` | Show the current binding | -| `/ctx set @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 -``` - -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/ -``` - -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 -``` - -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 ` | Upload a document into the repo/worktree | -| `/file get ` | Fetch a file (directories are zipped) | -| `/agent` | Show/set the default agent for the current scope | -| `/topic @branch` | Create/bind a topic | -| `/ctx` | Show current context | -| `/ctx set @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 ` | Register current directory as a project | -| `takopi chat-id` | Capture a chat ID | -| `takopi chat-id --project ` | 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) diff --git a/mkdocs.yml b/mkdocs.yml index 78c73ba..ac9b192 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 82a1c75..fc42087 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", ] diff --git a/readme.md b/readme.md index dfc8e88..12bdc06 100644 --- a/readme.md +++ b/readme.md @@ -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 diff --git a/uv.lock b/uv.lock index d1b0667..f979a12 100644 --- a/uv.lock +++ b/uv.lock @@ -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" }, ]