Skip to content

Secretary Agent

An always-on Telegram bot that turns voice-of-developer messages into GitHub tickets and keeps the project board current without manual upkeep.


Overview

┌─────────────────────────────────────────────────────────────────┐
│                        You (Developer)                          │
│                   "add earnings heatmap"                        │
└──────────────────────────────┬──────────────────────────────────┘
                               │  Telegram message
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                      secretary/bot.py                           │
│                  (long-poll loop, always-on)                    │
└──────┬────────────────────┬────────────────────────────┬────────┘
       │                    │                            │
       ▼                    ▼                            ▼
┌──────────────┐  ┌──────────────────┐        ┌──────────────────┐
│ DeepSeek V3  │  │ GitHub Projects  │        │  Telegram reply  │
│ intent.py    │  │ GraphQL API      │        │  "✅ Ticket #42   │
│ classify +   │  │ github_projects  │        │  added to Backlog"│
│ generate     │  │ .py              │        └──────────────────┘
└──────────────┘  └────────┬─────────┘
                           │
                           ▼
               ┌───────────────────────┐
               │  GitHub Project Board │
               │  marketpulse (#1)     │
               │                       │
               │  Backlog → In Progress│
               │         → In Review   │
               │         → Done        │
               └───────────┬───────────┘
                           │  reads board
                           ▼
               ┌───────────────────────┐
               │     Night Agent       │
               │  (Claude Code session)│
               │  moves tickets as     │
               │  it works             │
               └───────────────────────┘
What Why it matters
Telegram is the input No app to open — just send a message from anywhere
DeepSeek V3 classifies intent Paid per-call (~$0.0004/message), fast, handles natural language
GitHub Projects is the source of truth Board is always live; night agent reads it directly
Night agent moves tickets automatically Board stays current with zero manual upkeep

How It Works

Step 1 — You send a Telegram message

You ──► "add a bull/bear score to the portfolio page"
         or
        "what's in progress?"
         or
        "bug: TSLA sentiment not updating"
  • Input: free-form text in your authorized Telegram chat
  • Output: message received by the polling loop
  • Who does it: Telegram (the messaging app), TELEGRAM_CHAT_ID gates access

Step 2 — Intent classification

Raw text
   │
   ▼
┌─────────────────────────────────────────────────┐
│  DeepSeek V3  (deepseek-chat, api.deepseek.com) │
│                                                  │
│  System prompt: 5-intent classifier              │
│  One API call, no conversation history           │
│                                                  │
│  Returns structured JSON:                        │
│  {                                               │
│    "intent":   "new_feature",                    │
│    "title":    "Bull/Bear score on portfolio",   │
│    "body":     "Add a composite score ...",      │
│    "priority": "medium"                          │
│  }                                               │
└─────────────────────────────────────────────────┘
  • Input: raw Telegram message text
  • Output: structured JSON with intent + generated ticket content
  • Who does it: secretary/intent.py

Step 3 — Duplicate check (new_feature / new_bug only)

Before calling DeepSeek to generate a ticket:

  github_projects.py → fetch all open ticket titles + statuses
         │
         ▼
  Pass to DeepSeek as context alongside the developer's message
         │
         ├── near-duplicate found?
         │     → intent = "duplicate"
         │     → reply: "ticket already exists: '<title>' (In Progress)"
         │     → stop, no ticket created
         │
         └── no duplicate → proceed to ticket generation

Step 4 — Dispatch on intent

intent = "new_feature"  ──► create GitHub issue → add to board Backlog
intent = "new_bug"      ──► create GitHub issue (bug label) → Backlog
intent = "duplicate"    ──► Telegram reply naming the existing ticket
intent = "status"       ──► query board → summarize counts + last 5 Done
intent = "move_ticket"  ──► find ticket by name → move to requested status
intent = "question"     ──► answer from git log / board state, no ticket
  • Input: classified intent + params
  • Output: action taken (ticket created, board updated, or answer drafted)
  • Who does it: secretary/bot.py dispatcher → secretary/github_projects.py

Step 5 — GitHub Projects write (create or move)

For new tickets — two-step sequence:

  gh issue create                   ← creates a real GitHub issue
         │
         ▼ issue number
  gh project item-add               ← attaches issue to the board
         │
         ▼ item_id
  updateProjectV2ItemFieldValue     ← sets Status = "Backlog"
  (GraphQL mutation)
  • Input: ticket title, body, priority from DeepSeek
  • Output: ticket visible on the GitHub Projects board at Backlog
  • Who does it: secretary/github_projects.py

Step 6 — Telegram confirmation

Secretary ──► "✅ Ticket created: Bull/Bear score on portfolio
               Status: Backlog | Priority: medium
               https://github.com/…/issues/42"
  • Input: result from dispatch
  • Output: confirmation (or error) sent back to your Telegram chat
  • Who does it: secretary/bot.pyrequests.post to Telegram Bot API

Step 7 — Night agent picks up and moves tickets

Night agent wakes (scheduled or manual)
   │
   ├── list_tickets(status="Backlog")    ← find next item
   ├── move_ticket(item_id, "In Progress")
   │
   │   [does the work — implements, tests, commits]
   │
   ├── move_ticket(item_id, "In Review")   (optional QA gate)
   └── move_ticket(item_id, "Done")
  • Input: GitHub Projects board (Backlog column)
  • Output: ticket progresses through the board automatically
  • Who does it: night agent (Claude Code session) using secretary/github_projects.py

Components

secretary/
   ├── bot.py              ─── entry point, Telegram polling loop
   ├── intent.py           ─── DeepSeek call, returns structured JSON
   └── github_projects.py  ─── shared GitHub Projects GraphQL wrapper
         │
         └── used by both bot.py AND the night agent
Component What it does Lives in
bot.py Long-polls Telegram every 10s, routes messages to intent → dispatch secretary/bot.py
intent.py One-shot DeepSeek V3 call — classify + generate ticket content secretary/intent.py
github_projects.py GraphQL wrapper for the marketpulse Projects board secretary/github_projects.py
Config constants GH_PROJECT_ID, GH_STATUS_OPTION_IDS, SECRETARY_MODEL, etc. config.py

Data Flow

Developer (Telegram)
  │
  │  "fix: VIX not showing on macro tab"
  ▼
bot.py  (10s poll loop)
  │
  │  raw text
  ▼
intent.py
  │  POST https://api.deepseek.com/v1/chat/completions
  │  model: deepseek-chat
  ▼
DeepSeek V3
  │  { intent: "new_bug", title: "VIX missing...", body: "..." }
  ▼
bot.py dispatcher
  │
  │  create_ticket("VIX missing...", label=bug)
  ▼
github_projects.py
  │  gh issue create → gh project item-add → GraphQL set Status=Backlog
  ▼
GitHub Projects Board
  │  Backlog: [VIX missing on macro tab]
  ▼
Night agent (next run)
  │  move → In Progress → [works] → Done
  ▼
GitHub Projects Board
     Done: [VIX missing on macro tab ✅]
  │
  │  (optional) secretary syncs BACKLOG.md mirror
  ▼
Developer (Telegram)
  "✅ Bug ticket created: VIX missing on macro tab"

Intent Reference

Intent Trigger phrases Action
new_feature "add", "build", "I want", "feature:" Duplicate check → GitHub issue → Backlog
new_bug "bug:", "broken", "not working", "fix:" Duplicate check → GitHub issue (bug label) → Backlog
duplicate (detected by DeepSeek against open ticket list) Telegram reply naming existing ticket + status
status "what's in progress", "board status", "what are you working on" Query board → summary reply
move_ticket "move X to done", "mark X complete" updateProjectV2ItemFieldValue
question "what does X do", "why is Y" Answer from git log / board, no ticket

Key Decisions

Decision Chosen approach Why
Model choice DeepSeek V3 via direct API ~$0.27/MTok input, $1.10/MTok output — each ticket involves intent classification + context gathering + full ticket generation; expect ~5k–20k tokens in / 1k–3k out (~$0.005–$0.02/message); OpenAI SDK compatible
Ticket creation Real GitHub issues (not draft items) Night agent can link commits; history survives project deletion
Board state GitHub Projects as source of truth Live, visual, shared; BACKLOG.md becomes a mirror only
Authorization TELEGRAM_CHAT_ID allowlist Simple, no OAuth needed; you're the only user
Polling vs webhook Long-polling (getUpdates) No public URL needed; runs behind any NAT
Shared helper github_projects.py used by both bot and night agent One place to update when GraphQL field IDs change

What Can Go Wrong

Failure Impact How we handle it
DeepSeek API down Intent classification fails Bot replies with error; user can retry
Telegram token invalid Bot receives no messages Process exits with clear error on startup
GH_TOKEN missing / wrong scope Ticket creation fails github_projects.py raises with descriptive error; bot replies
Status field IDs stale move_ticket silently no-ops Fetch fresh IDs via GraphQL and update config.py
Night agent crashes mid-ticket Ticket stuck "In Progress" Manually move via move_ticket Telegram command
Unauthorized chat sends message Security risk Bot silently ignores any chat_id ≠ TELEGRAM_CHAT_ID

Running

# Start the secretary bot (keep alive — use tmux or systemd)
python secretary/bot.py

# Or via Make
make secretary          # starts bot.py in background

# The secretary runs SEPARATELY from main.py (not part of the scheduler)

Glossary

Term Plain English definition
Secretary agent The bot you text on Telegram; it manages your to-do board for you
Intent What the bot thinks you mean — "new feature request" vs "check status" vs "report a bug"
DeepSeek V3 A cheap, fast AI model (like a lightweight ChatGPT) used to understand your message and write the ticket
GitHub Projects GitHub's built-in project board — like Trello but lives next to your code
Item ID GitHub's internal ID for a card on the board — used to move it between columns
Status field The column a ticket sits in: Backlog, In Progress, In Review, Done
Long-polling The bot repeatedly asks Telegram "any new messages?" every 10 seconds — no server or webhook needed
GraphQL The query language GitHub uses for its Projects API — the bot sends structured queries to read and update the board
GH_TOKEN A GitHub access token with project scope — gives the bot permission to create and move tickets
Night agent A separate AI coding session that runs at night, picks up Backlog tickets, and implements them
BACKLOG.md A human-readable text file that mirrors the board — kept for git history but no longer the source of truth
Dispatch The step where the bot decides which action to take after it knows the intent

Last updated: 2026-06-02 · Owner: Michael Ko