The manual.
How the Tokibot works, how to set it up and how to change it.
Overview
The Tokibot has two halves:
- The gotchi: a Raspberry Pi Zero with an e-paper display, running its own small Buildroot image. A Python service receives events, works out a mood and draws it.
- The client: a small Python script on your computer. Your coding agents run it as a hook on every event (Claude Code also as the status line). It translates each harness into one vocabulary, sends metadata to the gotchi and never blocks the agent: sending happens in a detached background process.
The gotchi listens on port 8765. Over the USB cable it is at 192.168.77.1, on Wi-Fi at its own name, tokibot-xxxx.local. A live preview of the display is at http://tokibot-xxxx.local:8765/.
Moods
| Mood | When |
|---|---|
ready | a new session started |
working | prompt sent, tools running |
tired | working, but 80 % or more of the 5-hour budget is used |
permission | Claude asks for permission |
waiting | Claude waits for your input |
done | the reply is finished |
error | API error (overloaded, logged out, …) |
limit | rate limit reached, with the time it resets |
idle | nothing going on |
levelup | the critter just reached a new level |
summary | idle in the evening (18–23 h): the day's tally |
hungry | no work for two days or more |
With several sessions (for example worktrees), the state that needs you most wins. The footer shows how many sessions are working (▶), waiting for you (?) or done (✓), plus bars for the 5-hour and 7-day budget. The header shows the level and the number of sessions.
Growing up
The critter earns experience from your work:
- 5 XP per finished reply
- 3 XP per completed task
- 2 XP per new session
- 1 XP per tool call, for the first 40 tool calls of a day
- 5 XP per commit, 8 per push and 15 per pull request
Levels get further apart as it grows: level 2 at 120 XP, level 3 at 480, level 4 at 1,080, level 5 at 1,920, and so on. A normal working day is worth a level early on; level 10 takes a month or two. Every day with at least one finished reply extends the streak; skip a day and it starts over. In the evening it shows what you did today, and when you leave it alone for two days it gets hungry.
The progress lives in life.json on the image's small writable data partition, so it survives reboots and updates. In the simulator it is in ~/.tokibot/.
The client
It works on macOS, Linux and Windows and needs Python 3.9 or newer. On Windows it also needs Git for Windows: the hooks run through its Git Bash, and the terminal commands below go there too. The client finds Python there as py, python or python3. For Claude Code there are two ways to install it, for every other harness the install script.
As a Claude Code plugin
$ /plugin marketplace add https://tokibot.lottes.dev/marketplace.json
$ /plugin install tokibot@tokibot
The plugin brings the hooks. It has four settings, which you can change in /plugin: the gotchi's address (empty: USB cable, then Wi-Fi), its name, the token, and the relay URL. Claude Code keeps the token in its secure storage. Updates arrive through /plugin when a new version is out. If you have access to the repository, /plugin marketplace add a-lottes/claudegotchi works too.
Plugins cannot set the status line, so the budget bars need one more line in a terminal. Pass the same --token or --url as in the plugin, if you set one:
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --statusline-only
With the install script
One line installs hooks and status line straight into your settings; everything after -- goes to the installer. Do not combine it with the plugin, or every event is sent twice.
# automatic: USB cable first, then Wi-Fi
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh
# a fixed address, or a token
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --url http://192.168.1.50:8765
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --token my-secret
# other harnesses too: pick them, or auto for all found
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --harness claude,codex,gemini
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --harness auto
# remove it again
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --uninstall
The install script downloads install.py, tokibot_client.py and tokibot.sh from this website and runs the installer. It copies the client to ~/.tokibot/ and adds the hooks and the status line to ~/.claude/settings.json. It backs up the old file as settings.json.tokibot-backup. Existing hooks stay, and an existing status line is still shown.
Other harnesses
With --harness the installer adds the hooks to other coding agents as well. The client translates each of them into Claude Code's hook events, so the gotchi only knows one vocabulary and shows the harness's name next to the project. The installer remembers your choice; running it again without --harness keeps it, and a harness you leave out loses its hooks.
| Harness | --harness | Hooks go to | Tokens |
|---|---|---|---|
| Claude Code | claude | ~/.claude/settings.json (or the plugin) | from the transcript |
| OpenAI Codex CLI | codex | ~/.codex/hooks.json; approve them once with /hooks | from the session log |
| Gemini CLI | gemini | ~/.gemini/settings.json | from the session file |
| GitHub Copilot CLI | copilot | ~/.copilot/hooks/tokibot.json | estimated |
| Cursor | cursor | ~/.cursor/hooks.json | estimated |
| Devin Desktop (Windsurf) | devin | ~/.codeium/windsurf/hooks.json | estimated |
| Kiro CLI 3 | kiro | ~/.kiro/hooks/tokibot.json | estimated |
| Cline | cline | scripts in ~/Documents/Cline/Hooks/ (macOS, Linux) | estimated |
| OpenCode | opencode | plugin ~/.config/opencode/plugins/tokibot.ts | from the plugin |
| Amp | amp | plugin ~/.config/amp/plugins/tokibot.ts | estimated |
Only Claude Code reports its rate limits and context, so the 5-hour and 7-day bars need it; with other harnesses the fed bar shows instead, and the limit mood appears when a harness reports a rate-limit error. Harnesses without token counts feed the critter an estimate per reply and tool call, which counts for XP but not for the Glutton highscore. Cursor also runs the hooks in ~/.claude/settings.json; the client recognises that and reports those sessions as Cursor. Kiro, Cline, OpenCode and Amp are built from their documentation and not yet tried against the real tools.
To check the connection, play the test sequence. It walks through the moods over about a minute and prints which address answered:
$ sh ~/.tokibot/tokibot.sh test
Settings
Open http://tokibot-xxxx.local:8765/settings, or http://192.168.77.1:8765/settings over USB. The page changes Wi-Fi, time zone, display, rotation, token and relay. It writes tokibot.toml on the boot partition, keeps the comments in it, and applies the change right away: the app restarts, or the gotchi reboots for Wi-Fi, USB, time zone and Bluetooth, and the page reloads by itself when it is back. Once a token is set, the page asks for it; Where do I find the token? on the page says where it comes from.
tokenSave the token as soon as you set it in tokibot.toml or pair a client. Your computer keeps the paired one in ~/.tokibot/tokibot.json (grep token ~/.tokibot/tokibot.json prints it); the gotchi keeps it under [server] in tokibot.toml.
You can also edit the file directly: put the card into your computer and open tokibot.toml on the TOKIBOT drive.
[device]
display = "waveshare_2in13_v4" # see Displays below
rotate = 0 # 180 if the picture is upside down
timezone = "Europe/Berlin"
[server]
port = 8765
token = "" # only accept clients that send this token
[usb]
enabled = true # network adapter on the USB data port
address = "192.168.77.1"
[wifi]
ssid = "" # empty: USB cable only
psk = ""
country = "DE"
[relay]
url = "" # ntfy topic for agents in the cloud
auth = "" # access token for a protected ntfy server
Without Wi-Fi the gotchi takes the time from your computer. With Wi-Fi it uses NTP.
Displays
The gotchi drives the Waveshare e-Paper HATs that Pwnagotchi builds use. Pick yours under Display on the settings page, or set display in tokibot.toml. Pwnagotchi's names from ui.display.type work as well. The revision is printed on the back of the board.
| Display | display = | Pwnagotchi name |
|---|---|---|
| 2.13″ V4, 250 × 122 (default) | waveshare_2in13_v4 | waveshare_4 |
| 2.13″ V3 | waveshare_2in13_v3 | waveshare_3 |
| 2.13″ V2 | waveshare_2in13_v2 | waveshare_2 |
| 2.13″ V1 | waveshare_2in13_v1 | waveshare_1 |
| 2.13″ B V4, black/white/red, full refresh only | waveshare_2in13b_v4 | waveshare2in13b_v4 |
| 2.7″ V2, 264 × 176 | waveshare_2in7_v2 | waveshare27inchv2 |
| 2.7″ V1, full refresh only | waveshare_2in7_v1 | waveshare27inch |
Panels without partial refresh redraw the whole screen each time, which flashes and takes several seconds, so the gotchi updates them at most once a minute and leaves out the animations. On the 2.7″ the picture sits in the middle with a white margin. Smaller displays (Inky pHAT, PaPiRus, 1.54″, OLED or LCD HATs) are not supported. With an unknown name the gotchi keeps running without a picture, so you can still fix it on the settings page.
Security
Every gotchi has its own network name, tokibot-xxxx; the four characters come from the Pi's serial number. It shows the name on screen until a client talks to it. So two gotchis on one Wi-Fi never answer to the same name, and the client sends the name along with every event: a gotchi with another name refuses it.
The installer pairs your computer with the gotchi on the USB cable. For Wi-Fi only, pass the name: --gotchi tokibot-xxxx. Pairing remembers the name, and if the gotchi has no token yet, the gotchi makes one, saves it under [server] token and hands it to your computer. From then on it only takes events from clients with that token, and the settings page and app updates need it too. Pair again any time:
$ sh ~/.tokibot/tokibot.sh pair
$ sh ~/.tokibot/tokibot.sh pair tokibot-xxxx
Without pairing, the client learns the name the first time it reaches a gotchi over USB, but the gotchi has no token: anyone on the same network can send it events.
The image has no SSH. The root filesystem is read-only.
Claude in the cloud
Hooks run wherever the agent runs. A session in the cloud, for example in a Claude Project, runs in a container that cannot see your local network. For that case the gotchi can listen to a relay: an ntfy topic. Cloud hooks publish to the topic, and the gotchi keeps one outgoing connection to it, so it works behind any Wi-Fi, even a phone hotspot.
This is how sessions in a Claude Project, or in Claude Code on the web, reach the gotchi. It takes four steps, once.
-
Give the gotchi Wi-Fi. It needs internet to listen to the relay; a phone hotspot is fine. See step 5 of the assembly guide or the settings page.
-
Create a relay on your computer. This makes a random topic on ntfy.sh and a token, and sets up your local client to use them:
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --new-relay # if your hooks come from the plugin, only add the status line: $ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --new-relay --statusline-onlyIt prints a
[server]token and a[relay]url. Enter both on the gotchi's settings page (fields Token and Relay URL) or intokibot.toml. With the plugin, also enter the token in/plugin, because the gotchi now expects it from your computer too. -
Print the setup script for the cloud. It takes the relay and token from step 2 and carries the client inline, so the cloud needs no access to anything else:
$ curl -fsSL https://tokibot.lottes.dev/install.sh | sh -s -- --cloud-setup > cloud-setup.sh -
Add a cloud environment on claude.ai. The built-in environment has no settings and blocks ntfy.sh, so make your own. In a Claude Project, open Project settings, open the Cloud environment menu and choose Add cloud environment. In that dialog:
- Network access: choose Custom, keep the default package managers and add
ntfy.shunder Allowed domains. - Setup script: paste the whole content of
cloud-setup.sh.
Adding it also selects it for the project. Sessions started after that install the client and report to the gotchi through the relay; sessions that are already running are not affected. For Claude Code on the web outside a project, the same dialog is described in the cloud environments docs.
- Network access: choose Custom, keep the default package managers and add
To check it, start a new thread or session and give it something to do. The critter gets busy, and the footer counts the cloud session like a local one.
The topic name is the secret: anyone who knows it can read the events (metadata only) and could publish to it. The gotchi ignores relay messages without the right token. You can run your own ntfy server and point --relay at it; set [relay] auth if it needs an access token.
Updates
The Python app updates in place. Only kernel or system changes need a new image. Get tokibot-app.tar.gz from the download page and upload it on the settings page. Developers with the source code can also send it from the command line:
$ make update GOTCHI=http://tokibot-xxxx.local:8765 TOKEN=your-token
This packs app/ into output/tokibot-app.tar.gz and sends it to the gotchi.
The gotchi checks that the new version starts, keeps it in /data/app, and restarts. If it crashes three times in a row right after starting, it falls back to the version built into the image. "Back to the built-in version" on the settings page does the same by hand.
Simulator
For developers: try everything without hardware, from the source code. You need Python 3.9 or newer and Pillow.
$ python3 -m pip install pillow
$ make test
$ make sim # preview at http://localhost:8765/ (other port: make sim PORT=8766)
$ TOKIBOT_URL=http://localhost:8765 python3 client/tokibot_client.py test
$ make mockups # every mood in docs/mockups.png
The simulator keeps its own copy of the settings in ~/.tokibot/tokibot.toml, so the settings page does not change the template in the repository.
The image
The operating system is built with Buildroot from the os/ folder of the source code. There is one configuration per board:
$ make zero2w # Raspberry Pi Zero 2 W (64-bit)
$ make zerow # Raspberry Pi Zero W
On GitHub, the workflow image.yml builds both images for every v* tag and attaches them to the release, together with the app bundle. The website then picks them up for the download page.
- Read-only root filesystem,
/tmpand/runin RAM. Pulling the plug does not hurt the card. - A 16 MB writable
datapartition at/datafor the critter's progress and app updates. - BusyBox init with a few scripts: read settings, USB gadget, Wi-Fi, then the service.
- USB gadget as a network adapter with a DHCP server for your computer: RNDIS for Windows, CDC-ECM for macOS and Linux, no driver needed on any of them.
- avahi for
tokibot-xxxx.local, a name of its own per gotchi, NTP on Wi-Fi. - No SSH, no Debian, no Wi-Fi hacking tools.
Privacy
Only metadata is sent: event name, session ID, project folder name, tool name, notification or error type and limit percentages. Prompts, code and replies never leave your computer. With a relay, that metadata passes through the ntfy server you chose.
Troubleshooting
The most common problems are below. Many more, sorted by system, are in the FAQ.
The display stays blank
- Check that the HAT sits flush on all 40 pins.
- Check that
displaymatches your board. The revision (V1 to V4) is printed on its back; see Displays. - Give it a moment: an e-paper refresh is slow and the first boot takes a little while.
The computer does not see the gotchi
- The cable must be in the port labelled USB, not PWR.
- Try another cable. Many micro-USB cables only charge and carry no data.
- Open
http://192.168.77.1:8765/in a browser. If the preview loads, the connection works and the problem is in the client setup. - Windows: the gotchi should show up in Device Manager under Network adapters as a USB RNDIS adapter.
.localnames often do not resolve on Windows; over the cable the client uses192.168.77.1anyway. - Linux: if the adapter appears but gets no address, let NetworkManager use DHCP ("Automatic") for it.
The critter does not react to Claude Code
- Run the test sequence (see the client). It prints which addresses it tried.
- If the gotchi has a token, the client needs the same one: set it in
/plugin, or reinstall with--token. - The test says 409: the address answered with another gotchi than yours, for example a friend's. Pair again with yours.
- Start a new Claude Code session after installing; running sessions keep their old settings.
The picture is upside down
Set rotate = 180 on the settings page.
Repository
app/ the service on the device (Python, Pillow)
client/ hook and status line client for your computer, plus installer
os/ Buildroot extension (br2-external): defconfigs, board files, package
docs/ this website
All display texts are in app/tokibot/mood.py, the critter's pixels in critter.py.