Skip to content

Instantly share code, notes, and snippets.

@Evan-Kim2028
Created May 23, 2026 16:05
Show Gist options
  • Select an option

  • Save Evan-Kim2028/7c58d24c6b1bf02557d6c7242302abce to your computer and use it in GitHub Desktop.

Select an option

Save Evan-Kim2028/7c58d24c6b1bf02557d6c7242302abce to your computer and use it in GitHub Desktop.
Composer 2.5 + pi inside Takopi (Telegram) — setup guide

Composer 2.5 + pi inside Takopi (Telegram)

How to run Cursor Composer 2.5 as the Takopi engine via pi + pi-cursor-sdk, without the Takopi control plane.

Tested on Linux with Takopi installed at /opt/takopi-runtime, pi via nvm, and Telegram transport.


Architecture (what talks to what)

Telegram message
  → Takopi (Python, /opt/takopi-runtime)
  → patched pi runner (takopi-runners-pi.py)
  → subprocess: pi --print --mode json --provider cursor --model cursor/composer-2.5
  → pi-cursor-sdk (npm) → Cursor SDK → Composer 2.5
  → JSONL events on stdout → Takopi progress UI → Telegram edits

Key idea: Takopi does not call the Cursor SDK directly. It shells out to pi, which uses the pi-cursor-sdk extension as a provider. Takopi’s job is to translate pi’s JSONL stream into Telegram progress messages.


Prerequisites

Piece Purpose
Takopi runtime e.g. /opt/takopi-runtime/bin/python + takopi package
Node + pi @mariozechner/pi-coding-agent CLI on PATH (nvm is fine)
pi-cursor-sdk npm extension that adds cursor/composer-2.5 to pi
CURSOR_API_KEY From Cursor dashboard → Integrations
Telegram bot token Existing Takopi bot config or @BotFather

Step 1 — Install pi + pi-cursor-sdk

# Example: nvm node 22
npm install -g @mariozechner/pi-coding-agent
npm install -g pi-cursor-sdk   # or: pi install pi-cursor-sdk

pi list | grep pi-cursor-sdk     # should show the extension

Auth (pick one):

# Option A: env var (what we use for systemd)
export CURSOR_API_KEY="crsr_..."

# Option B: pi auth store (~/.pi/agent/auth.json)
mkdir -p ~/.pi/agent
cat > ~/.pi/agent/auth.json <<'EOF'
{
  "cursor": { "type": "api_key", "key": "crsr_..." }
}
EOF
chmod 600 ~/.pi/agent/auth.json

Smoke test pi alone:

export PI_STDIN_CLOSE=1
export PI_CURSOR_SETTING_SOURCES=none
export PI_CURSOR_NATIVE_TOOL_DISPLAY=1

pi --no-session --provider cursor --model cursor/composer-2.5 --print \
  -p "Reply with exactly: ok" </dev/null

Step 2 — Takopi bot config (engine = pi)

Minimal ~/.config/takopi-standalone/linux/takopi.toml:

default_engine = "pi"
transport = "telegram"

[transports.telegram]
bot_token = "YOUR_BOT_TOKEN"
chat_id = YOUR_CHAT_ID
voice_transcription = true

[pi]
model = "cursor/composer-2.5"
provider = "cursor"
extra_args = []

If migrating from control-plane bots, a small sync script can patch legacy configs:

PI_BLOCK = {
    "model": "cursor/composer-2.5",
    "provider": "cursor",
    "extra_args": [],
}
# set default_engine = "pi", keep existing telegram token

Secrets file ~/.config/takopi-standalone/secrets.env (mode 600):

CURSOR_API_KEY=crsr_...

We reuse the key from ~/.hermes/.env when Hermes is already set up.


Step 3 — Wrapper script (preload patched pi runner)

Stock Takopi takopi.runners.pi does not handle Cursor’s thinking/tool JSONL well enough for Telegram. Use a wrapper that:

  1. Sources secrets.env
  2. Puts nvm pi on PATH
  3. Sets pi-cursor env vars (see Step 4)
  4. Preloads a patched takopi.runners.pi before takopi.cli.main()
#!/usr/bin/env bash
set -euo pipefail
CONFIG_PATH="$1"
ENGINE="$2"
shift 2

source ~/.config/takopi-standalone/secrets.env
export PATH="$HOME/.nvm/versions/node/v22.12.0/bin:/opt/takopi-runtime/bin:$PATH"
export PI_STDIN_CLOSE=1
export PI_CURSOR_PI_TOOL_BRIDGE=0
export PI_CURSOR_SETTING_SOURCES=none
export PI_CURSOR_NATIVE_TOOL_DISPLAY=1

exec /opt/takopi-runtime/bin/python - "$CONFIG_PATH" "$ENGINE" "$@" <<'PY'
from pathlib import Path
import importlib.util, sys

config_path = Path(sys.argv[1]).expanduser().resolve()
engine = sys.argv[2]
argv = ["takopi", engine, *sys.argv[3:]]

patch_path = Path("/path/to/takopi_standalone/patches/takopi-runners-pi.py")
if patch_path.is_file():
    spec = importlib.util.spec_from_file_location("takopi.runners.pi", patch_path)
    pi_mod = importlib.util.module_from_spec(spec)
    sys.modules["takopi.runners.pi"] = pi_mod
    spec.loader.exec_module(pi_mod)

import takopi.cli as cli, takopi.config as config, takopi.settings as settings
config.HOME_CONFIG_PATH = config_path
settings.HOME_CONFIG_PATH = config_path
cli.HOME_CONFIG_PATH = config_path
sys.argv = argv
cli.main()
PY

Run manually:

./takopi-run-standalone.sh ~/.config/takopi-standalone/linux/takopi.toml pi

Step 4 — Critical pi-cursor-sdk env vars

Set in the wrapper (systemd inherits them):

Variable Value Why
PI_STDIN_CLOSE 1 pi must not block on stdin when launched from systemd
PI_CURSOR_SETTING_SOURCES none lean headless bots; no ambient Cursor project settings
PI_CURSOR_PI_TOOL_BRIDGE 0 disable MCP pi-tool bridge for simpler Telegram runs
PI_CURSOR_NATIVE_TOOL_DISPLAY 1 Required. With 0, tool activity is batched into thinking traces after long commands finish → Telegram looks frozen for minutes

Optional:

PI_CURSOR_MCP_TOOL_TIMEOUT_SECONDS=3600   # long MCP/shell tools

Step 5 — Patched pi runner (the important part)

Patch file replaces stock takopi/runners/pi.py at runtime. It adds:

  1. Cursor trace parsing — turns thinking_delta lines like $ git status, read path, glob ** into Takopi ActionEvents
  2. toolcall_start / ToolExecutionStart handling — shows tool steps when pi-cursor emits them
  3. 12s heartbeat — while pi is quiet (Composer running a long shell/SSH), emit:
    still running… (36s quiet, after tool: read: foo.py)
    
    so Telegram keeps updating and you know it’s alive

Without the heartbeat, headless --print --mode json often emits no JSONL for minutes during a single long tool — pi is working, but Takopi has nothing to render.

Patch highlights:

_HEARTBEAT_IDLE_S = 12.0
PI_CURSOR_NATIVE_TOOL_DISPLAY=1  # in shell, not Python

# In PiRunner._iter_jsonl_events:
# - wait up to 12s for next JSONL line
# - on timeout → yield heartbeat ActionEvent with quiet seconds + last tool name

Optional: install patch into site-packages instead of preload:

sudo install -m 0644 patches/takopi-runners-pi.py \
  /opt/takopi-runtime/lib/python3.14/site-packages/takopi/runners/pi.py

Preload is nicer for iteration — no sudo.


Step 6 — systemd user service

[Unit]
Description=Takopi bot (standalone, pi + Cursor)
After=network-online.target

[Service]
Type=simple
WorkingDirectory=/path/to/your/cwd
Environment=HOME=/home/you
Environment=TAKOPI_STANDALONE_SECRETS=/home/you/.config/takopi-standalone/secrets.env
ExecStart=/path/to/takopi-run-standalone.sh /home/you/.config/takopi-standalone/linux/takopi.toml pi
Restart=always
RestartSec=5

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now takopi-standalone-linux.service
journalctl --user -u takopi-standalone-linux.service -f

Step 7 — Verify

# pi + extension present
command -v pi && pi list | grep pi-cursor-sdk

# secrets
test -f ~/.config/takopi-standalone/secrets.env

# config uses pi + composer
grep -E 'default_engine|"pi"' ~/.config/takopi-standalone/linux/takopi.toml

# direct pi smoke (no secrets printed)
source ~/.config/takopi-standalone/secrets.env
pi --no-session --model cursor/composer-2.5 --print -p "Reply with exactly: ok" </dev/null

# bot running
systemctl --user is-active takopi-standalone-linux.service

Progress path test (optional Python script): run patched PiRunner with a glob+read prompt and assert Telegram-style progress shows tool lines.


Operating notes

Sessions

Resume in Telegram with the token pi prints:

pi --session 019e5588

Or send a message while Takopi has the session token in context.

What “cancelled · pi · 2m 58s · step 15” means

That is you hitting /cancel or the cancel button — not an auto-timeout. Takopi has no runner duration limit. If progress looked dead, pi was likely still running.

Check:

journalctl --user -u takopi-standalone-linux.service --since "10 min ago" | rg "subprocess.spawn|subprocess.exit|cancel"
ps aux | rg " pi "

Long runs / “still running…”

Expected during quiet gaps. Composer may run SSH, tests, or git for minutes without new JSONL. The heartbeat line should update every ~12s:

▸ still running… (48s quiet, after ssh -o ConnectTimeout=10 …)

If (Ns quiet) keeps climbing → still working. If it stops climbing for many minutes with no new tool lines → maybe stuck; cancel is reasonable.

One bot per chat

Running linux and polymarket bots against the same Telegram chat is confusing (parallel sessions, duplicate progress). Prefer one active bot per chat.

Hermes vs Takopi

Hermes + Discord Takopi + pi + Telegram
Main model Kimi (config) Composer 2.5 via pi
Composer via coding_fleet tools direct engine
Bridge cursor_sdk in agent-fleet pi-cursor-sdk subprocess

Same CURSOR_API_KEY can feed both.


Troubleshooting

Symptom Likely cause Fix
Progress frozen at step N for minutes PI_CURSOR_NATIVE_TOOL_DISPLAY=0 or no heartbeat patch Set display=1, use patched runner
cancelled after long wait User cancel while pi was still running Wait for heartbeat; check ps
pi works in shell, not in bot PATH missing nvm pi Fix PATH in wrapper
CURSOR_API_KEY is not set secrets not sourced Check secrets.env + systemd env
No tool lines at all patch not loaded Confirm preload path in wrapper
AttributeError on pi runner import patch / takopi version mismatch Re-sync patch with installed takopi

File layout (reference)

takopi_standalone/
├── patches/
│   └── takopi-runners-pi.py      # Cursor-aware runner + heartbeat
├── scripts/
│   ├── install-standalone.sh     # migrate configs, secrets, systemd
│   ├── takopi-run-standalone.sh  # wrapper (preload patch + env)
│   ├── sync-bot-config.py        # legacy toml → pi+cursor
│   └── verify-standalone.sh      # smoke checks
└── systemd/
    └── takopi-standalone-*.service

Minimal recipe (TL;DR)

  1. npm i -g @mariozechner/pi-coding-agent pi-cursor-sdk
  2. Put CURSOR_API_KEY in ~/.config/takopi-standalone/secrets.env
  3. Set default_engine = "pi", [pi] model = "cursor/composer-2.5"
  4. Wrapper: PATH to pi, PI_CURSOR_NATIVE_TOOL_DISPLAY=1, preload patched runner
  5. systemd user service calling wrapper
  6. Expect heartbeat during long tool gaps — pi is usually still working

Written from a working setup on Pop!_OS, May 2026. Adjust paths (/opt/takopi-runtime, nvm node version, config dirs) for your machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment