Your Personal AI Assistant & Tutor. Penny is an AI assistant you control from Telegram — she manages your tasks, teaches you to code, handles your email and calendar, and can even write code and open PRs on GitHub.
Powered by Claude. Describe what you want in plain English, and Penny figures it out — all from a Telegram chat.
She also learns new skills on the fly. Ask her to do something she doesn't know how to do, and she'll write the code, test it, fix it if it breaks, and remember it for next time.
No coding experience needed to get it running. This guide walks you through every step.
- 🤖 Write code for you — Describe a task, get a GitHub pull request
- 🧠 Learn new skills — Ask it anything actionable and it generates, tests, and self-heals code on the fly
- 💬 Chat on Telegram — Just text it like you would a friend
- 📧 Manage your email — Check, search, and send emails (auto-humanized so they don't sound like AI)
- 📅 Manage your calendar — View, create, and update events
- ✅ Todo list — Add, complete, and manage your Google Tasks
- 🍽️ Make reservations — Book restaurants via OpenTable or have AI call them for you
- 📰 Daily & weekly roundups — Get your schedule, todos, tweets, and news delivered via Telegram every morning
- 💬 Natural language — Talk naturally ("what's on my schedule?", "remind me to call Bob", "any new emails?")
- 🧠 Remembers everything — Conversations, skills, and context persist across sessions
- 🔒 Safe — Sandboxed execution, rate limiting, command denylist, access control
You only need two things to get started. Everything else is optional.
This is the bot you'll chat with on Telegram.
- Open Telegram and search for @BotFather (it has a blue checkmark)
- Send it the message:
/newbot - BotFather will ask you for a name (e.g.
My Penny) and a username (e.g.my_penny_bot) - It will reply with a token that looks like
123456789:ABCdefGHI-jklMNOpqr— copy this and save it somewhere safe
💡 This token is secret. Don't share it with anyone.
This connects Penny to Claude, the AI that does the thinking and coding.
- Go to console.anthropic.com
- Create an account (or sign in)
- Click API Keys in the sidebar
- Click Create Key
- Copy the key — it starts with
sk-ant-
💡 Anthropic gives you some free credits to start. After that, usage is pay-as-you-go (typically a few cents per task).
That's all you need. The rest is optional — add features when you're ready.
You'll need to use the Terminal (Mac/Linux) or Command Prompt (Windows) for these steps. Don't worry — just copy and paste each line.
Node.js is what runs Penny. Check if you already have it:
node --versionIf you see a version number (like v20.x.x), you're good. If not:
- Mac: Go to nodejs.org, download the LTS version, and run the installer
- Windows: Same — download from nodejs.org and run the installer
- Linux: Run
sudo apt install nodejs npm(Ubuntu/Debian) orsudo dnf install nodejs(Fedora)
git clone https://github.com/devi-labs/penny.git
cd penny
npm install💡 If
gitisn't installed, download it from git-scm.com.
cp .env.example .envNow open the .env file in any text editor (Notepad, TextEdit, VS Code — anything works).
Find these lines and fill in the values you got earlier:
TELEGRAM_BOT_TOKEN=paste-your-telegram-token-here
ANTHROPIC_API_KEY=paste-your-anthropic-key-here
If you want a joining code to keep strangers out (recommended):
TELEGRAM_JOIN_CODE=your-secret-code-here
Save the file.
node server.jsYou should see something like:
Starting Penny...
⚡️ Penny Telegram server running on port 8080 (polling mode)
Claude: enabled | GitHub: enabled | Brain: enabled | Allowed users: (any)
That's it! Open Telegram, find your bot, and send it a message.
💡 If you set a joining code, you'll need to send that code first before the bot responds.
Open your bot in Telegram and start chatting. Here are some things you can do:
repo: your-username/your-repo
task: add a health check endpoint to the express server
Just ask — if it requires computation, data processing, or logic, Penny will generate a skill, run it, and remember it:
convert 72°F to celsius
what day of the week is december 25 2026
generate a random 16 character password
base64 encode "hello world"
You don't need to memorize commands. Just say what you need:
| What you say | What happens |
|---|---|
| "what's on my schedule today?" | Shows your calendar events |
| "any meetings tomorrow?" | Calendar events for tomorrow |
| "remind me to call Bob by Friday" | Adds a todo with a due date |
| "what's on my plate?" | Shows your todo list |
| "mark 3 as done" | Completes todo #3 |
| "check my email" | Shows recent emails |
| "find emails from Sarah" | Searches your inbox |
| "book dinner for 4 at Nobu on Saturday at 7pm" | Gets an OpenTable link |
| "catch me up" | Sends the daily news roundup |
| "what can you do?" | Shows all commands |
These also work if you prefer them:
| Command | What it does |
|---|---|
help |
Show all commands |
repo: owner/repo + task: do something |
Create a pull request |
tell me about owner/repo |
Get a summary of a GitHub repo |
email send user@example.com "Subject" Body |
Send an email (with preview + confirmation) |
cal create "Lunch" 03/20 12pm 1 hour |
Create a calendar event |
cal calendars |
List all your calendars |
cal default 2 |
Set default calendar for new events |
cal delete 3 |
Delete event #3 from your last listing |
todo add Buy groceries by Friday |
Add a todo with a due date |
todo done 2 |
Complete todo #2 |
roundup |
Get your daily briefing now |
skills list |
See all learned skills |
brain status |
Check if memory is working |
brain reset |
Clear conversation memory |
self destruct |
Shut down the VM |
Penny learns new skills on the fly using a self-healing architecture inspired by Voyager and Reflexion:
- Classify — Claude decides if your message needs code or is just conversation
- Match — Checks the skill library for an existing skill that fits
- Generate — If no match, Claude writes a new JavaScript function
- Execute — Runs the code in a sandboxed VM (no file system, no network abuse, 15s timeout)
- Verify — Claude checks if the output actually answers your question (Reflexion pattern)
- Self-heal — If execution or verification fails, the error is fed back to Claude to fix the code (up to 3 attempts). Each failed attempt is remembered so mistakes aren't repeated
- Persist — Working skills are saved to the brain and reused on similar future requests
Skills are stored in the brain (local filesystem + optional GCS backup) and survive restarts. You can manage them with skills list and skills delete <name>.
When you close your terminal, Penny stops. If you want it running all the time, you have two options:
This puts Penny on a Google Cloud VM that's always on. You'll need a Google Cloud account (free tier available).
bash deploy-gce.sh .env
bash setup.sh .envdocker build -t penny:local .
docker run -d --name penny --restart=always --env-file .env -p 8080:8080 penny:localTo check logs: docker logs -f penny · To stop: docker rm -f penny
| What's happening | What to do |
|---|---|
| Bot doesn't respond at all | Make sure node server.js is running and check for errors in the terminal |
| Bot says "Please send the joining code" | Send the code you set in TELEGRAM_JOIN_CODE |
| "ANTHROPIC_API_KEY missing" | Make sure you added your Anthropic key to the .env file |
| "GITHUB_TOKEN missing" | Add your GitHub token to .env (needed for creating PRs) |
| PR creation fails | Send brain last error to see what went wrong |
| "Google Tasks not configured" | Re-authorize with the Tasks scope (see Google APIs section below) |
| Bot is slow to respond | Claude is thinking — complex tasks can take 30–60 seconds |
| Container keeps restarting | Check logs: docker logs penny --tail 50 |
- Command denylist —
rm,curl,wget,sudo,docker, and 20+ other dangerous commands are blocked - VM sandbox — Generated skills run in Node.js
vmwith norequire,fs,process, oreval - Rate limited — Configurable per-user rate limiting (default: 20 requests per 30 seconds)
- Access control — Use a joining code and/or user ID allowlist to restrict who can use it
- Emails are humanized — Outgoing emails are rewritten so they don't sound AI-generated
- Secrets stay local — API keys are read from env vars and never exposed to generated code or AI prompts
- Temp cleanup — Cloned repos are deleted after PR creation to prevent disk exhaustion
Everything below is optional. Add what you want, skip what you don't.
Lets Penny push code and create pull requests on your repos.
- Go to github.com/settings/tokens
- Click Generate new token (classic) → name it
penny→ checkreposcope → generate - Add to
.env:
GITHUB_TOKEN=ghp_your-token-here
These three features share the same credentials. Set up once, and you get all three.
| Feature | Google API to enable | OAuth scope |
|---|---|---|
| Email (check, search, send) | Gmail API | https://mail.google.com/ |
| Calendar (view, create, update) | Google Calendar API | https://www.googleapis.com/auth/calendar |
| Todos (list, add, complete) | Google Tasks API | https://www.googleapis.com/auth/tasks |
-
Go to Google Cloud Console and create a new project (or select one)
-
Enable all three APIs — search for each in the search bar and click Enable:
- Gmail API
- Google Calendar API
- Tasks API
-
Set up OAuth consent screen:
- Go to APIs & Services → OAuth consent screen
- Choose External
- Fill in the app name (e.g. "Penny")
- Add your email as a test user
-
Create OAuth credentials:
- Go to APIs & Services → Credentials → Create Credentials → OAuth client ID
- Application type: Web application
- Under Authorized redirect URIs, add:
https://developers.google.com/oauthplayground - Click Create → copy the Client ID and Client Secret
-
Get a refresh token:
- Go to OAuth Playground
- Click the ⚙️ gear icon → check "Use your own OAuth credentials" → paste your Client ID and Secret
- In the left panel, select all three scopes:
https://mail.google.com/https://www.googleapis.com/auth/calendarhttps://www.googleapis.com/auth/tasks
- Click Authorize APIs → sign in with your Google account
- Click Exchange authorization code for tokens → copy the Refresh Token
-
Add to
.env:
GMAIL_CLIENT_ID=your-client-id
GMAIL_CLIENT_SECRET=your-client-secret
GMAIL_REFRESH_TOKEN=your-refresh-token
GMAIL_USER_EMAIL=you@gmail.com
Restart Penny. You now have email, calendar, and todos working.
💡 If you already set up Gmail before and want to add calendar/todos, you just need to re-do step 5 with all three scopes selected, then update your refresh token in
.env.
Penny sends you a morning briefing every day at 9am EST — right in your Telegram chat. It automatically delivers to anyone who's messaged the bot.
Daily roundup includes:
- 📅 Today's calendar events
- ✅ Open todos
- 🐦 Latest tweets from people you follow (via X API v2)
- 📰 News on topics you care about
- 👤 News mentions of people you track
Weekly roundup — Deep-dive news topics, sent on Saturday (configurable)
Add to .env:
# Daily — news topics, Twitter, people to track
ROUNDUP_DAILY_TOPICS=AI,startups,cybersecurity
ROUNDUP_TWITTER_HANDLES=elonmusk,naval,paulg
ROUNDUP_LINKEDIN_NAMES=satya-nadella,reid-hoffman
# Weekly — deep-dive topics (sent on Saturday)
ROUNDUP_WEEKLY_TOPICS=machine learning,venture capital
ROUNDUP_WEEKLY_DAY=saturday
# When to send (24h format, EST timezone, default: 9 = 9am EST)
ROUNDUP_SEND_HOUR=9
# X/Twitter API v2 bearer token (for actual tweets — get from developer.x.com)
X_BEARER_TOKEN=your-bearer-token
Calendar and todos are included automatically if you have Google APIs configured (see above). News topics work with no API keys (uses Google News RSS). Twitter uses X API v2 if X_BEARER_TOKEN is set, otherwise falls back to RSS/Google News.
Roundups can also be sent via email if ROUNDUP_EMAIL_TO is configured.
Test it: Send roundup, catch me up, or give me my briefing in your bot chat to get an instant preview.
Two ways to book:
reserve— Generates an OpenTable booking link (no API keys needed)call— AI calls the restaurant and makes the reservation for you
For AI phone calls, add to .env:
BLAND_API_KEY=your-bland-api-key
RESERVATION_CALLER_NAME=Your Name
Get a Bland.ai key at app.bland.ai.
Optional: Add GOOGLE_PLACES_API_KEY so Penny can automatically look up restaurant phone numbers. Otherwise, include the number in your message: call +13125551234 and reserve a table for 2 at Nobu on Saturday at 7pm
server.js # Entry point
src/
├── telegram.js # Telegram message handler + command router
├── matchers.js # Natural language intent matchers (zero-latency regex)
├── config.js # Environment config
├── skills.js # Self-healing skill generator (Voyager/Reflexion)
├── reservations.js # Restaurant booking (OpenTable + Bland.ai)
├── roundup.js # Daily & weekly digests (schedule, todos, tweets, news)
├── brain/
│ └── brain.js # Persistent memory (local fs + GCS backup)
├── agent/
│ ├── sandbox.js # Sandboxed PR creation pipeline
│ └── plan.js # Claude-powered code planner
├── clients/
│ ├── telegram.js # Telegram polling client
│ ├── anthropic.js # Claude client
│ ├── openai.js # OpenAI client
│ ├── github.js # Octokit wrapper
│ ├── gcp.js # GCS client
│ ├── gmail.js # Gmail client
│ ├── calendar.js # Google Calendar client
│ └── tasks.js # Google Tasks client
├── github/
│ ├── repo.js # Repo info + README fetching
│ └── pr.js # PR summarization
└── util/
├── proc.js # Command runner + denylist
├── parse.js # URL/task parsers
└── rateLimit.js # Per-user rate limiting
Want to help improve Penny? See CONTRIBUTING.md.
MIT — see LICENSE.
See CREDITS.md.