mibbs-bot — Project Guide for Claude

This is a family manager Telegram bot running on Val Town. It uses Claude (Anthropic) for natural language understanding, Google Calendar, and SQLite for storage.

Files

FileTypePurpose
main.tsHTTPTelegram webhook handler, system prompt, agent call
email.tsEmailEmail forwarding handler, system prompt, agent call
cron.tsInterval (Sun 7pm ET)Weekly briefing — Claude synthesizes "what does this week require?"
morning-cron.tsInterval (daily 7am ET)Daily morning briefing with today's events, weather, tasks
reminder-cron.tsInterval (every 15 min)Proactive reminder pings — fires when a task's remind_at is reached
llm.tsScriptShared LLM layer: model config, createMessage() (one-shot), runAgent() (agentic tool-use loop — both Anthropic and GPT paths)
tools.tsScriptShared toolset: TOOLS array (10 tools) + executeTool() switch
db.tsScriptShared DB layer: initDb(), all task/info/message/user functions, rowsToObjects, localToUtcIso
google.tsScriptShared Google Calendar layer: auth + token refresh, fetchCalendarEvents(), all CRUD helpers
weather.tsScriptShared weather layer: Open-Meteo, fetchForecast() (raw), getWeather() (formatted)
changelog.mdFileRelease history (crayon versioning — see below)
updates.mdFileFeature backlog and implementation plans
CLAUDE.mdFileThis file

Architecture

Each deployable val (main.ts, email.ts, crons) handles its own I/O surface and passes context to the shared layer:

┌─────────────────┐   ┌──────────────────┐   ┌────────────────────┐
│   main.ts       │   │    email.ts       │   │  cron.ts /         │
│  (Telegram I/O) │   │   (Email I/O)    │   │  morning-cron.ts   │
└────────┬────────┘   └────────┬─────────┘   └─────────┬──────────┘
         │                     │                        │
         ▼                     ▼                        ▼
   runAgent()            runAgent()              createMessage()
         └──────────────────────┘                      │
                    │                                   │
              llm.ts (shared)                    llm.ts (shared)
                    │
          ┌─────────┴─────────┐
          ▼                   ▼
      tools.ts           db.ts / google.ts / weather.ts
    (TOOLS + executeTool)    (all shared helpers)

Env Vars (set in Val Town)

  • TELEGRAM_TOKEN — Telegram bot token
  • GROUP_CHAT_ID — Family group chat ID
  • ANTHROPIC_API_KEY — Anthropic API key
  • GOOGLE_TOKEN_JSON — Base64-encoded Google OAuth token (Calendar)
  • GOOGLE_CREDENTIALS_JSON — Base64-encoded Google OAuth client credentials
  • CALENDAR_ID — Google Calendar ID (or "primary")
  • HOME_LOCATION — Home city for weather (e.g. "Newton, MA")
  • MODEL — Claude model override (default: claude-sonnet-4-6)
  • RESPOND_TO_ALL — "true" to respond to all group messages (default: @mention only)
  • BOT_USERNAME — Telegram bot username (optional, avoids a getMe API call)
  • STEEL_API_KEY — Steel (steel.dev) API key for JS-rendered web page scraping. Optional: when set, fetch_url uses Steel's /v1/scrape endpoint for regular web pages instead of plain fetch. Get a free key at app.steel.dev ($30 credit included).

SQLite Tables

  • fm_tasks — household task list (id, text, created_by, due_date, completed, owner, remind_at, reminded)
  • fm_info — key/value store for household info
  • fm_messages — conversation history per chat_id
  • fm_users — Telegram user identity map (telegram_id, first_name, username) — auto-populated from inbound messages, used to resolve owner names to @mentions

Adding New Features

Adding a new tool (e.g. add_note):

  1. Add DB helpers (if needed) to db.ts
  2. Add the Anthropic.Tool definition to TOOLS in tools.ts
  3. Add the case to executeTool in tools.ts
  4. That's it — main.ts and email.ts both pick it up automatically

Adding a new cron (e.g. Friday outing suggestions):

  1. Create a new *.ts interval file
  2. Import createMessage from ./llm.ts, data helpers from ./db.ts, ./google.ts, ./weather.ts
  3. Build the prompt from live data and call createMessage({ prompt, maxTokens })

Adding a new DB table:

  1. Add the CREATE TABLE IF NOT EXISTS + any migrations to initDb() in db.ts
  2. Add the CRUD functions in db.ts
  3. Import them in tools.ts and/or whichever val needs them

Changelog — Crayon Versioning

Every release gets a crayon name. This is the versioning system:

  • Major version = new base color (Red → Blue → Green → Orange…) — a new capability era
  • Minor version = shade of that color — additions and polish within the era

Color sequence (in order)

  1. 🔴 Red family: Red, Brick Red, Scarlet, Crimson, Mahogany, Maroon
  2. 🔵 Blue family: Blue, Navy Blue, Cerulean, Cornflower, Denim, Cadet Blue
  3. 🟢 Green family: Green, Forest Green, Jungle Green, Fern, Shamrock, Pine Green
  4. 🟠 Orange family: Orange, Burnt Orange, Peach, Apricot, Mango Tango
  5. 🟡 Yellow family: Yellow, Goldenrod, Dandelion, Banana Mania, Sunglow
  6. 🟣 Purple family: Purple, Violet, Plum, Mulberry, Orchid, Lavender
  7. ⚫ Gray/Black family: Silver, Gray, Charcoal, Black, White (reserved for infrastructure-only releases)

Current version: Navy Blue (v2.1). Next minor would be Cerulean (v2.2). Next major would be Green (v3.0).

When to bump versions

Minor release — new tool, new capability added to existing era (e.g. web search, open loops, family memory):

  1. Update changelog.md — prepend a new ## 🖍️ [Color] (vX.Y) — [Short title] section
  2. Update /changelog response text in main.ts (the CHANGELOG_LATEST constant)
  3. No Telegram announcement needed for minor releases

Major release — new capability era (e.g. proactive behavior, email integration, trip planning):

  1. Update changelog.md — prepend a new ## 🖍️ [Color] (vX.0) — [Era name] section
  2. Update CHANGELOG_LATEST in main.ts
  3. Update the message in announceRelease.ts with the new version notes
  4. Run announceRelease.ts (use Val Town's run_file tool) to post the announcement to the family chat

Changelog entry format

## 🖍️ [Color] (vX.Y) — [Punchy era/feature name] *Month Year* One-sentence summary of what changed and why it matters. **Feature 1** — What it does, concretely. **Feature 2** — What it does, concretely.

Keep entries short. Focus on what the family can do with each release, not implementation details.