A terminal pet companion that monitors Claude Code sessions
Project description
TPET - Terminal Pet Companion
A terminal pet that monitors your Claude Code sessions and generates personality-driven commentary. Runs in a tmux pane with ASCII art animation and Rich rendering.
Table of Contents
- About
- Screenshots
- Features
- Prerequisites
- Installing using uv (recommended)
- Installing using pipx
- Installing for dev mode
- Usage
- CLI Reference
- Configuration
- Environment Variables
- How It Works
- Development
- Contributing
- License
Screenshots
ASCII mode — live commentary
ASCII mode — pet details card
Sixel mode — live commentary
Sixel mode — pet details card
About
I really liked the idea of the Claude Code buddy so I created my own that supports infinite variations and customization. It even supports watching plain files and commenting on them!
tpet is a CLI application that creates a unique AI-generated pet creature. The pet displays in your terminal using Rich Live rendering, monitors your Claude Code session JSONL files (or plain text files) via watchdog, and produces in-character commentary about your coding sessions.
Built with Typer, Rich, and the Claude Agent SDK.
Features
Core Capabilities
- AI-Generated Pets: Each pet is unique -- name, personality, ASCII art, backstory, and stats all generated by the LLM
- Personality-Driven Stats: HUMOR, PATIENCE, CHAOS, WISDOM, SNARK -- generated to match the creature's character and clamped by rarity tier
- Rarity System: Common, Uncommon, Rare, and Legendary tiers determine stat ranges and display colors
- Session Monitoring: Watches Claude Code JSONL sessions via watchdog or follows plain text files with
--follow - In-Character Commentary: Session events trigger personality-driven reactions influenced by the pet's stats
- Idle Chatter: When nothing is happening, your pet comments on its own
Art Modes
- ASCII (default): Character art generated by the LLM, displayed with Rich
- Sixel Art: AI-generated sprites rendered as ANSI half-block characters (requires API key)
- Custom Base Images: Supply your own idle frame image with
--base-image
Technical Excellence
- No API Key Required: Commentary and pet generation use your Claude Code subscription via the Agent SDK
- Per-Pipeline Providers: Independently configure the provider/model for profile generation, commentary, and image art
- Multiple Providers: Claude (Agent SDK), Ollama, OpenAI, OpenRouter, and Google Gemini for any pipeline
- Non-Blocking Commentary: Background
ThreadPoolExecutorkeeps the 4fps display smooth during LLM calls - YAML Persistence: Human-readable config and profiles via Pydantic serialization
- XDG Compliance: Config and data paths follow XDG base directory conventions
Prerequisites
- Install and authenticate Claude Code (required for default commentary and pet generation)
- Install Python 3.13 or newer
- https://www.python.org/downloads/ has installers for all platforms
- On macOS with Homebrew:
brew install python@3.13
Using uv
The recommended way to install tpet. If you don't have uv installed:
curl -LsSf https://astral.sh/uv/install.sh | sh
PyPi install
uv tool install term-pet
To upgrade:
uv tool install term-pet -U --force
Installing / running using uvx
uvx --from term-pet tpet
Source install from GitHub
uv tool install git+https://github.com/paulrobello/tpet
To upgrade:
uv tool install git+https://github.com/paulrobello/tpet -U --force
pipx
Installing
If you don't have pipx installed:
pip install pipx
pipx ensurepath
PyPi install
pipx install term-pet
To upgrade an existing installation:
pipx install term-pet --force
Source install from GitHub
pipx install git+https://github.com/paulrobello/tpet
To upgrade:
pipx install git+https://github.com/paulrobello/tpet --force
Installing for dev mode
Clone the repo and run the setup make target. Note uv is required.
git clone https://github.com/paulrobello/tpet
cd tpet
make setup
Usage
tpet uses subcommands for distinct operations. The old flag-based style (--new, --details, --gen-art, etc.) still works for backward compatibility.
# Subcommand style (preferred)
tpet run # Start the live pet display (default if no subcommand)
tpet run --follow /path/to/file # Follow a plain text file instead of Claude sessions
tpet new # Generate a new pet
tpet new -y # Generate without confirmation
tpet new -C "make it a dragon" # Generate with custom criteria
tpet details # Show full pet card
tpet details --backstory # Show card with backstory
tpet art # Generate graphical art for the current pet
tpet art --art-mode sixel-art # Generate sixel art specifically
# Flag style (backward-compatible)
tpet # Start the pet (watches Claude Code sessions)
tpet --new # Generate a new pet
tpet --details # Show full pet card
tpet --gen-art # Generate graphical art
tpet --reset # Delete current pet
tpet --reset -y # Delete without confirmation
tpet --dump-config # Show current config
tpet --project /path/to/repo # Use project-specific pet
tpet --dry-run # Validate config and exit
tpet --regen-art # Regenerate ASCII art for current pet
CLI Reference
Global flags
| Flag | Short | Description |
|---|---|---|
--version |
Show version | |
--debug |
-D |
Enable debug logging |
--verbose |
-v |
Increase verbosity (stackable) |
--project |
-p |
Project directory |
--config-dir |
-c |
Config directory override |
tpet new
Generate a new pet.
| Flag | Short | Description |
|---|---|---|
--create-prompt |
-C |
Custom criteria for pet generation |
--create-prompt-file |
-F |
File containing custom criteria |
--yes |
-y |
Bypass confirmation prompts |
--seed |
-s |
Random seed for pet generation (defaults to current time) |
tpet details
Show the pet details card.
| Flag | Short | Description |
|---|---|---|
--backstory |
-b |
Include backstory in card |
tpet art
Generate graphical art for the current pet.
| Flag | Short | Description |
|---|---|---|
--art-mode |
-a |
Art display mode: ascii or sixel-art |
--art-provider |
-P |
Image art provider: openai, gemini, or openrouter |
--art-model |
Model for image art generation | |
--art-width |
-W |
Max percentage of terminal width for art (1-100) |
--art-prompt |
Custom prompt for image generation | |
--base-image |
Custom image to use as idle frame (forces OpenAI edits) |
tpet run
Start the live pet display (default mode).
| Flag | Short | Description |
|---|---|---|
--follow |
-f |
Follow a plain text file instead of Claude sessions |
--watch-dir |
-w |
Session watch directory override |
--commentary-provider |
Commentary LLM provider: claude, ollama, openai, openrouter, or gemini |
|
--commentary-model |
Model for commentary generation | |
--comment-interval |
-i |
Min seconds between comments |
--idle-chatter-interval |
-I |
Seconds between idle chatter |
--max-comments |
-M |
Max comments per session |
--sleep-threshold |
-s |
Seconds before sleep animation |
--art-mode |
-a |
Art display mode: ascii or sixel-art |
--log-level |
-l |
Log level override |
--show-session |
Show resolved session directory and file, then exit |
Legacy flag-style
The root tpet command accepts all flags from every subcommand for backward compatibility. For example, tpet --new, tpet --details, tpet --gen-art, tpet --reset, tpet --regen-art, tpet --dump-config, and tpet --dry-run all continue to work as before.
Configuration
Config file: ~/.config/tpet/config.yaml
# Per-pipeline LLM provider configuration
profile_provider_config:
provider: claude
model: claude-haiku-4-5
commentary_provider_config:
provider: claude
model: claude-haiku-4-5
image_art_provider_config:
provider: openai
model: gpt-image-1.5
# Timing
comment_interval_seconds: 30
idle_chatter_interval_seconds: 300
max_comments_per_session: 0
max_comment_length: 150
max_idle_length: 100
sleep_threshold_seconds: 120
# Animation
idle_duration_seconds: 3.0
reaction_duration_seconds: 0.5
sleep_duration_seconds: 60.0
ascii_art_frames: 6
# Display
art_mode: ascii
art_max_width_pct: 40
art_size: 120
halfblock_size: 48
bubble_placement: bottom
# Art processing
chroma_tolerance: 30
art_dir_path: art
art_prompt: ""
# Logging
log_level: WARNING
log_file: debug.log
Pipeline provider configuration
Each pipeline (profile_provider_config, commentary_provider_config, image_art_provider_config) independently configures its own LLM provider:
| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | claude |
LLM provider: claude, ollama, openai, openrouter, or gemini |
model |
string | (provider default) | Model override (empty = provider default) |
base_url |
string | (provider default) | API base URL override (empty = provider default) |
api_key_env |
string | (provider default) | Environment variable name for the API key |
Provider defaults:
| Provider | Text default model | Image default model | Default base URL |
|---|---|---|---|
claude |
claude-haiku-4-5 |
(n/a) | Agent SDK (no URL) |
ollama |
llama3.2 |
(n/a) | http://localhost:11434/v1 |
openai |
gpt-4o |
gpt-image-1.5 |
https://api.openai.com/v1 |
openrouter |
anthropic/claude-haiku-4-5 |
openai/dall-e-3 |
https://openrouter.ai/api/v1 |
gemini |
gemini-2.5-flash |
gemini-2.5-flash |
Google Genai client |
General configuration reference
| Field | Type | Default | Description |
|---|---|---|---|
comment_interval_seconds |
float | 30.0 |
Minimum seconds between event-triggered comments |
idle_chatter_interval_seconds |
float | 300.0 |
Seconds of inactivity before idle chatter |
max_comments_per_session |
int | 0 |
Maximum comments per run (0 = unlimited) |
max_comment_length |
int | 150 |
Character limit for event comments |
max_idle_length |
int | 100 |
Character limit for idle chatter |
log_level |
string | WARNING |
Log verbosity: DEBUG, INFO, WARNING, ERROR |
log_file |
string | debug.log |
Log filename (written inside the config directory) |
sleep_threshold_seconds |
int | 120 |
Inactivity seconds before sleep animation |
idle_duration_seconds |
float | 3.0 |
Seconds per idle animation frame cycle |
reaction_duration_seconds |
float | 0.5 |
Duration of the reaction animation |
sleep_duration_seconds |
float | 60.0 |
Duration of each sleep animation cycle |
ascii_art_frames |
int | 6 |
Number of animation frames per pet |
art_mode |
string | ascii |
Display mode: ascii or sixel-art |
art_max_width_pct |
int | 40 |
Percentage of terminal width allocated to the art panel |
art_size |
int | 120 |
Target pixel height for art sprites (multiple of 6) |
halfblock_size |
int | 48 |
Target pixel height for sixel-art half-blocks (must be even) |
chroma_tolerance |
int | 30 |
Chroma key removal tolerance for background removal |
art_dir_path |
string | art |
Subdirectory name under config dir for art files |
art_prompt |
string | "" |
Custom prompt override for image generation (empty = auto-generate) |
bubble_placement |
string | bottom |
Speech bubble position: top, right, or bottom |
Pet profiles are stored at:
- Global:
~/.config/tpet/profile.yaml - Per-project:
<project>/.tpet/profile.yaml
Environment Variables
API keys for graphical art generation and third-party providers are read from environment variables. The recommended approach is to place them in ~/.config/tpet/.env, which tpet loads automatically on every startup.
| Variable | Required for | Description |
|---|---|---|
OPENAI_API_KEY |
provider: openai |
OpenAI API key for text or image generation |
GEMINI_API_KEY |
provider: gemini |
Google Gemini API key for text or image generation |
OPENROUTER_API_KEY |
provider: openrouter |
OpenRouter API key for any pipeline |
Example ~/.config/tpet/.env:
OPENAI_API_KEY=sk-proj-...
GEMINI_API_KEY=AIza...
OPENROUTER_API_KEY=sk-or-...
No API key is required for the default claude provider or for ASCII art mode -- the Claude provider uses your Claude Code subscription via the Agent SDK. Ollama runs locally and requires no key.
How It Works
- On first run, tpet generates a unique pet using the LLM (name, personality, ASCII art, backstory)
- Stats (HUMOR, PATIENCE, CHAOS, WISDOM, SNARK) are personality-driven -- generated by the LLM to match the creature's character, then clamped to the rarity range
- The pet's rarity (Common/Uncommon/Rare/Legendary) determines stat ranges and display colors
- By default, tpet monitors your Claude Code session's JSONL files via watchdog (use
--followto watch a plain text file instead) - Session events trigger in-character commentary influenced by the pet's personality and stats
- The pet animates through a 6-frame cycle: idle, idle-shift, blink, blink-shift, react, and sleep. Blink frames are generated programmatically by compositing the idle pose with closed eyes from the sleep frame
- On exit, tpet prints a session summary showing total comments, API calls, token counts, and estimated cost
Development
make checkall # Run all checks (fmt, lint, typecheck, test)
make test # Run tests only
make lint # Lint with ruff
make fmt # Format with ruff
make typecheck # Type check with pyright
Dev mode
From repo root:
make dev
Contributing
Please ensure that all pull requests are formatted with ruff, pass ruff lint and pyright.
You can run the make target checkall to ensure the pipeline will pass with your changes.
The easiest way to setup your environment:
With uv installed:
uv tool install pre-commit
With pipx installed:
pipx install pre-commit
From repo root:
pre-commit install
pre-commit run --all-files
After running the above, all future commits will auto run pre-commit. Pre-commit will fix what it can and show what remains to be fixed.
License
MIT -- see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file term_pet-0.1.0.tar.gz.
File metadata
- Download URL: term_pet-0.1.0.tar.gz
- Upload date:
- Size: 59.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6227d31bcd4690a8b1e738834ffef4c5c1a0473dda46a2b3580692643aca27f
|
|
| MD5 |
b7ccbd797a7b7e28ad33cc4f3fc8c9e3
|
|
| BLAKE2b-256 |
6c9930411a3ba689f293a07856c4d0e814884e5e148f93c9adf49a34f2c3cbe3
|
Provenance
The following attestation bundles were made for term_pet-0.1.0.tar.gz:
Publisher:
publish.yml on paulrobello/term-pet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
term_pet-0.1.0.tar.gz -
Subject digest:
b6227d31bcd4690a8b1e738834ffef4c5c1a0473dda46a2b3580692643aca27f - Sigstore transparency entry: 1245799840
- Sigstore integration time:
-
Permalink:
paulrobello/term-pet@028ee639cd5fda0a6b5674a13df9c779e1536e57 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/paulrobello
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@028ee639cd5fda0a6b5674a13df9c779e1536e57 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file term_pet-0.1.0-py3-none-any.whl.
File metadata
- Download URL: term_pet-0.1.0-py3-none-any.whl
- Upload date:
- Size: 74.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a19c503ee2789751afdd585a0b94728bf06bd97a1e1688070cda66406b7c27d
|
|
| MD5 |
ed678ecb47b557d9b0dd733393e6af43
|
|
| BLAKE2b-256 |
b01a9266ff826c27e11523747f146533df411648eaa9111ecabc93f295d1cc0e
|
Provenance
The following attestation bundles were made for term_pet-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on paulrobello/term-pet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
term_pet-0.1.0-py3-none-any.whl -
Subject digest:
3a19c503ee2789751afdd585a0b94728bf06bd97a1e1688070cda66406b7c27d - Sigstore transparency entry: 1245799846
- Sigstore integration time:
-
Permalink:
paulrobello/term-pet@028ee639cd5fda0a6b5674a13df9c779e1536e57 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/paulrobello
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@028ee639cd5fda0a6b5674a13df9c779e1536e57 -
Trigger Event:
workflow_dispatch
-
Statement type: