Skip to main content

AgentForge — multi-agent Telegram bot framework powered by Claude Code

Project description

AgentForge

A reusable framework for building multi-agent Telegram bots powered by Claude AI.

Zero dependencies by default — runs on Python 3.10+ stdlib. Optional extras for voice transcription and faster HTTP.

Native App Scaffold

This repo now includes an Apple-platform scaffold under apps/ for future native clients:

  • AgentForgeKit shared Swift module for models, networking stubs, and utilities
  • AgentForgeiOS SwiftUI app target for iOS 17+
  • AgentForgemacOS SwiftUI app target for macOS 14+

Build commands:

make native-build      # Builds the shared Swift package
make native-test       # Runs the shared Swift package tests
make ios-build         # Builds the iOS app target with xcodebuild
make macos-build       # Builds the macOS app target with xcodebuild
make native-verify     # Runs all of the above

Open the native project in Xcode with open apps/AgentForgeApps.xcodeproj.

Important: this native app currently uses both the Swift package manifest at apps/Package.swift and the Xcode project at apps/AgentForgeApps.xcodeproj. When adding, renaming, or removing Swift source files for the native app, update apps/AgentForgeApps.xcodeproj/project.pbxproj as well or Xcode may show "Cannot find ... in scope" errors even if swift build succeeds.

Quick Start

# Clone and install
git clone https://github.com/Martin-Rancourt/AgentForge.git
cd AgentForge
./install.sh

# Configure
agentforge init              # Launch interactive setup wizard

# Create agents
agentforge agent create assistant --model sonnet --thread-id 11
agentforge agent create researcher --model opus --thread-id 12

# Run
agentforge bot

How It Works

AgentForge turns a Telegram Supergroup with Forum Topics into a multi-agent workspace. Each topic routes to a different Claude-powered agent with its own identity, model, and memory.

Telegram Topic (thread_id: 12)
  → telegram_adapter.py (long-polling)
  → message_router.py (resolve agent config)
  → claude_runner.py (invoke Claude CLI with agent's soul.md)
  → Response sent back to Telegram

Message Flow

  1. Bot polls Telegram for new messages
  2. message_thread_id determines which agent handles the message
  3. Conversation history is loaded from SQLite (configurable context window)
  4. Claude Code CLI is invoked with the agent's soul.md as system prompt
  5. Response is stored in SQLite and sent back to Telegram

Architecture

agentforge/
├── src/agentforge/
│   ├── telegram_adapter.py   # Main bot — polling, routing, sending
│   ├── claude_runner.py      # Claude Code CLI subprocess wrapper
│   ├── message_router.py     # Thread → agent config resolution
│   ├── config.py             # Typed config loader (dataclasses)
│   ├── memory_manager.py     # SQLite + FTS5 conversation database
│   ├── vault.py              # Structured memory store (per-agent)
│   ├── voice.py              # Voice note transcription (optional)
│   ├── http_client.py        # Dual-stack: requests or urllib
│   └── cli/                  # CLI commands (bot, agent, memory, init, vault)
├── scripts/                   # Bash utilities
│   ├── telegram_send.sh      # Send messages to topics (with dedup)
│   ├── heartbeat.sh          # Proactive task runner (cron)
│   ├── healthcheck.sh        # Daily system health check
│   ├── log_action.sh         # Action logging (Google Sheets + local)
│   └── memory_consolidation.sh  # Legacy helper; built-ins now run via heartbeat.sh
├── tests/                     # Pytest suite (49 tests)
├── install.sh                # One-liner bootstrap
└── pyproject.toml            # Package metadata

CLI Reference

agentforge bot

Start the Telegram bot.

agentforge bot [--config path/to/config.json]

agentforge agent

Manage agents declaratively.

# Create — scaffolds soul.md, memory.md, daily/ directory
agentforge agent create <name> --model <model> --thread-id <id>

# List all configured agents
agentforge agent list

# Remove an agent (--keep-files to preserve files on disk)
agentforge agent remove <name> [--keep-files]

Models: sonnet (fast, balanced), opus (strongest reasoning), haiku (fastest, cheapest)

agentforge memory

Query the conversation database.

agentforge memory search "deployment error"    # Full-text search (FTS5)
agentforge memory recent 50                     # Last 50 messages
agentforge memory stats                         # Database statistics

agentforge vault

Structured memory store — facts, preferences, and knowledge per agent.

agentforge vault write --agent victor --category knowledge --title "API design" --content "..."
agentforge vault search "deployment"
agentforge vault list --agent archie

agentforge init

Bootstrap a new instance from scratch.

agentforge init [--dir /path/to/instance]
agentforge init --non-interactive [--dir /path/to/instance]

Default behavior launches an interactive wizard that validates the Telegram token, detects a chat ID after /start, checks Claude Code CLI readiness, captures bot identity, and writes .env, agents/<name>/config.json, canonical vault prompt files under vault/Agents/<name>/, and shared handbook seeds under vault/Agents/_team/.

Use --non-interactive to preserve the original scaffold-only flow.

The interactive wizard also checks gh auth status and prints a non-blocking warning if GitHub auth is missing. Use either gh auth login or set GH_TOKEN in .env before running GitHub-driven automation.

Configuration

bot/config.json

{
  "telegram": {
    "token_env_var": "AGENTFORGE_TELEGRAM_TOKEN",  // env var name holding the token
    "bot_username": "agentforge_bot",               // optional expected bot username
    "allowed_chat_ids": [123456789],                // whitelist
    "group_chat_id": -100123456789,                 // supergroup ID
    "topics": {                                      // named topic shortcuts
      "general": 11,
      "dev": 12
    }
  },
  "claude": {
    "model": "sonnet",          // default model
    "max_budget_usd": 1.0       // per-invocation spend cap
  },
  "threads": {
    "11": { "name": "assistant", "model": "sonnet" },
    "12": { "name": "researcher", "model": "opus" }
  }
}

Recent conversation context is selected dynamically at runtime: AgentForge uses messages from roughly the last 4 hours, capped at 20 messages, with a fallback floor of 3 messages if the window is sparse.

.env

AGENTFORGE_TELEGRAM_TOKEN=your_bot_token_here
AGENTFORGE_ROOT=/path/to/instance
# Optional: preferred GitHub auth token for gh/API usage
GH_TOKEN=ghp_xxx
# Compatibility only; prefer GH_TOKEN above
GITHUB_TOKEN=ghp_xxx
# Optional: preserve gh credential lookup when HOME is overridden
GH_CONFIG_DIR=/Users/you/.config/gh

When AgentForge invokes Claude CLI, it sets HOME to AGENTFORGE_ROOT. Setting GH_CONFIG_DIR (or letting AgentForge infer it from your original home directory) keeps gh credential resolution stable in that environment.

Multi-Agent System

Each agent gets its own prompt identity via soul.md, compact durable memory via memory.md, and working notes in daily/:

agents/
├── victor/
│   ├── config.json    # Runtime config, logs, cron, operational files
│   └── ...
├── archie/
└── catherine/
vault/
└── Agents/
    ├── victor/
    │   ├── soul.md    # Canonical personality, role, instructions
    │   ├── memory.md  # Canonical compact durable memory
    │   └── daily/     # Canonical consolidation input only
    └── _team/
        ├── CLAUDE.md  # Shared team-wide operating rules
        ├── memory.md  # Shared compact injected memory
        ├── index.md   # Shared handbook index
        └── *.md       # Team handbook pages, not auto-injected

At runtime, AgentForge builds prompt context from:

  1. CLAUDE.md
  2. vault/Agents/_team/CLAUDE.md if present, else agents/shared/CLAUDE.md, else CLAUDE.local.md
  3. vault/Agents/_team/memory.md if present, else agents/shared/memory.md
  4. vault/Agents/<name>/soul.md if present, else agents/<name>/soul.md
  5. vault/Agents/<name>/memory.md if present, else agents/<name>/memory.md

Daily notes are not auto-injected. Agents can:

  • Have different Claude models (opus for complex reasoning, haiku for quick tasks)
  • Maintain separate conversation histories and per-agent memory
  • Store per-agent structured memory in the vault

For operational questions about current runtime behavior, prefer the generated read-only note vault/Agents/_team/system-state.md. After manual config.json edits or after upgrading to a release that changes shared verification guidance, run agentforge agent sync-system-state.

  • Delegate directly via each agent's bot for 1:1 handoffs; use project topics only for shared multi-agent work

Project Topics

Beyond agent topics, you can configure project topics where multiple agents collaborate:

"project_topics": {
  "42": {
    "label": "🏗 Dev",
    "agents": ["victor", "archie"],
    "default_agent": "victor"
  }
}

Use @agentname mentions to address a specific agent in a shared topic.

Scripts

Script Purpose Trigger
telegram_send.sh Send messages to topics Manual / cross-agent
heartbeat.sh Run proactive tasks from heartbeat.md Cron
healthcheck.sh System health report Cron (daily)
log_action.sh Log external actions to Sheets + file After side-effects
memory_consolidation.sh Legacy helper script Manual / legacy

Dedup

telegram_send.sh has built-in deduplication: identical messages to the same topic within 60 seconds are silently dropped.

Memory System

AgentForge provides three layers of memory:

Layer Storage Purpose
Conversations SQLite + FTS5 Full message history, searchable
Vault SQLite + FTS5 Structured facts/preferences per agent
Files Markdown soul.md (identity), memory.md (long-term), daily/ (learnings)

Built-in daily and weekly memory jobs are scheduled through the single heartbeat.sh runner. Root cron/ tasks apply to every agent dynamically, and agents/<name>/cron/ can override per-agent behavior when needed.

Todo System

Each agent has a dedicated human todo file in the Obsidian vault. During every proactive_check, AgentForge parses that file and injects only the actionable slice instead of the full backlog.

File Location

vault/Agents/<agent>/tasks.md

Agent-to-file mapping:

  • archievault/Agents/archie/tasks.md
  • catherinevault/Agents/catherine/tasks.md
  • christophevault/Agents/christophe/tasks.md
  • victorvault/Agents/victor/tasks.md
  • architectvault/Agents/architect/tasks.md

File Format

# Todos — <Agent>

## Inbox

- [ ] Review PR #136
  due:: 2026-04-13
  recur:: weekly
  priority:: high
  tags:: #project/agentforge #goal/review

## Next

- [ ] Check release notes 📅 2026-04-14 🔁 every week #project/agentforge

## Waiting

## Done

Supported scheduling syntax:

  • multiline fields: due::, recur::, priority::, tags::, last_done::
  • Obsidian Tasks plugin inline markers: 📅 YYYY-MM-DD and 🔁 every week|day|month|monday|...
  • tags: #project/name, #projet/name, #goal/name, #objectif/name, #autre/name

Recommended usage:

  • use ## Next for proactively actionable work without a hard due date
  • use ## Waiting for blocked work
  • keep recurring routines in tasks.md instead of creating custom agent cron jobs when possible

The installer also seeds a handbook page at vault/Agents/_team/todo-system.md with the canonical rules for humans and agents.

Slash Commands

The following slash commands are available for managing todos (defined in commands/):

Command Description
/todo-add Add a todo to an agent
/todo-all List all todos for an agent
/todo-next Show the next single todo
/todo-nexts Show the next few todos
/todo-projet Show todos for a specific project tag
/todo-projets List all projects that have todos
/todo-goal Show todos for a specific goal tag
/todo-goals List all goals that have todos
/todo-autres Show todos without a project or goal tag
/todo-migrer Migrate unchecked todos from a daily note to the Inbox

Heartbeat Integration

The proactive_check built-in cron in heartbeat.sh calls the markdown todo parser and injects only:

  • overdue tasks
  • tasks due now
  • recurring tasks that are due now
  • tasks under ## Next

Delegated machine-readable task payloads stay separate under agents/<agent>/tasks/.

Dependencies

Core: Python 3.10+ stdlib only (no pip install required for basic operation)

Optional extras:

pip install agentforge[full]    # requests + faster-whisper
pip install agentforge[dev]     # pytest + pytest-cov
Extra Package Purpose
full requests>=2.28 Faster HTTP client (falls back to urllib)
full faster-whisper>=1.0 Voice note transcription
dev pytest>=7 Test runner
dev pytest-cov Coverage reports

External Requirements

  • Claude Code CLI — must be installed at $AGENTFORGE_RUNTIME_ROOT/.local/bin/claude
  • Telegram Bot — create one via @BotFather, enable Forum Topics in your supergroup

Full Installation Guide (Fresh Linux Server)

This is the step-by-step procedure to install AgentForge on a fresh Linux machine (Ubuntu/Debian).

Prerequisites

# System packages
sudo apt update && sudo apt install -y python3.12 python3.12-venv git curl

# Node.js (required for Claude Code CLI)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

Step 1 — Create a dedicated user

sudo adduser --disabled-password --gecos "AgentForge Bot" agentforge
sudo loginctl enable-linger agentforge   # allows systemd user services to run without login

Step 2 — Install Claude Code CLI

sudo -u agentforge -i   # switch to agentforge user

# Install Claude Code
npm install -g @anthropic-ai/claude-code
# Verify
claude --version

Note: Claude Code requires an Anthropic API key or Claude Max subscription. Run claude once to authenticate.

Step 3 — Clone and install AgentForge

# Still as agentforge user
cd ~
git clone https://github.com/Martin-Rancourt/AgentForge.git
cd AgentForge
./install.sh ~

The installer will:

  1. Check Python 3.10+ ✓
  2. Check Claude CLI ✓
  3. Create a virtual environment
  4. Install the agentforge package
  5. Run the interactive agentforge init wizard
  6. Generate a systemd user service
  7. Check for Syncthing

Step 4 — Set up Telegram

Before running the bot, you need a Telegram Bot + Supergroup:

  1. Create a bot — Talk to @BotFather on Telegram:

    • /newbot → choose a name and username
    • Copy the bot token
  2. Create a Supergroup with Forum Topics enabled:

    • Telegram → New Group → add your bot → make it a Supergroup
    • Group Settings → Topics → Enable
    • Create one topic per agent (e.g. "Assistant", "Researcher", "Dev")
  3. Get the chat ID and thread IDs:

    • Send a message in each topic
    • Use https://api.telegram.org/bot<TOKEN>/getUpdates to see recent messages
    • Note the chat.id (negative number) and each message_thread_id
  4. Update config:

    # Edit .env with your token
    nano ~/.env
    # → AGENTFORGE_TELEGRAM_TOKEN=your_token_here
    
    # Edit config with your chat ID and thread IDs
    nano ~/bot/config.json
    

Step 5 — Create your agents

# Create agents matching your Telegram topics
agentforge agent create assistant --model sonnet --thread-id 11
agentforge agent create researcher --model opus --thread-id 12

# Edit each agent's personality
nano ~/vault/Agents/assistant/soul.md
nano ~/vault/Agents/researcher/soul.md

Step 6 — Start the bot

# Enable and start the systemd service
systemctl --user daemon-reload
systemctl --user enable --now agentforge

# Verify it's running
systemctl --user status agentforge

# Watch logs
journalctl --user -u agentforge -f

Step 7 — Set up cron jobs (optional but recommended)

crontab -e

Add:

# Heartbeat — single scheduler for all built-in and per-agent cron jobs
*/30 * * * * AGENTFORGE_ROOT=$HOME $HOME/.venv/bin/agentforge heartbeat >> $HOME/logs/heartbeat.log 2>&1

# Daily health check at 08:00
0 8 * * * $HOME/scripts/healthcheck.sh >> $HOME/logs/healthcheck.log 2>&1

Syncthing — Cross-Device Knowledge Sync

AgentForge uses Syncthing to sync your knowledge vault (Obsidian vault, agent memory, etc.) across devices. This is optional but recommended if you want to access your bot's brain from your phone or laptop.

Architecture

┌──────────────┐     Syncthing      ┌──────────────┐
│ Linux Server │ ◄──────────────► │    macOS     │
│  (always-on) │                    │  (laptop)    │
└──────┬───────┘                    └──────────────┘
       │
       │  Syncthing
       ▼
┌──────────────┐
│   iPhone     │
│ (Möbius Sync)│
└──────────────┘

The Linux server is the hub — always on, always syncing. Other devices sync when they're online.

Linux Server Setup

# Install Syncthing
sudo apt install -y syncthing

# Enable as a user service (runs without login thanks to lingering)
systemctl --user enable --now syncthing

# Access the web UI
# Syncthing listens on http://localhost:8384
# If remote: ssh -L 8384:localhost:8384 agentforge@your-server

Configure the shared folder:

  1. Open Syncthing web UI (http://localhost:8384)
  2. Add Folder → set the path to your knowledge vault (e.g. ~/obsidian-brain/)
  3. Set a Folder ID (e.g. agentforge-vault) — you'll need this on other devices
  4. Under Advanced → set Folder Type to "Send & Receive"

macOS Setup

# Install via Homebrew
brew install syncthing

# Start as a background service
brew services start syncthing
  1. Open http://localhost:8384 in your browser
  2. Add Remote Device → paste the Linux server's Device ID (found in the server's Syncthing UI under Actions → Show ID)
  3. On the Linux server, accept the incoming device request
  4. The shared folder should auto-appear for approval on macOS — accept it
  5. Choose a local path (e.g. ~/Documents/agentforge-vault/)

For Obsidian: Point Obsidian's vault to the synced folder. Your bot's knowledge base stays in sync with your laptop.

iOS Setup (Möbius Sync)

Möbius Sync is the only Syncthing-compatible app for iOS. It costs ~$5 USD (one-time purchase).

  1. Install Möbius Sync from the App Store
  2. Open the app → go to Settings → note your Device ID
  3. On your Linux server Syncthing UI:
    • Add Remote Device → paste the iOS Device ID
    • Share the agentforge-vault folder with this device
  4. Back in Möbius Sync:
    • Accept the incoming device connection
    • Accept the shared folder
    • Choose a local location (e.g. under "On My iPhone" or iCloud)
  5. For Obsidian Mobile: Point Obsidian to the Möbius Sync folder to browse your vault on the go

Tip: Möbius Sync works best with "Background Sync" enabled (Settings → Background Sync → On). iOS may still throttle it, so open the app occasionally to force a full sync.

Syncthing Tips

  • Device IDs are the only thing you exchange — no accounts, no cloud, fully P2P encrypted
  • Ignore patterns: Add a .stignore file in your vault root to exclude files:
    .obsidian/workspace.json
    .DS_Store
    *.tmp
    
  • Conflict resolution: Syncthing creates .sync-conflict-* files if two devices edit the same file simultaneously. Resolve manually.
  • Bandwidth: Initial sync can be slow for large vaults. Subsequent syncs are incremental and near-instant.

Testing

pip install -e ".[dev]"
pytest                      # Run all 49 tests
pytest --cov=agentforge     # With coverage

Tests cover: config validation, message routing, memory manager, vault operations.

Releases

This project uses release-please for automated releases:

  1. Use conventional commits (feat:, fix:, docs:, chore:, etc.)
  2. Release-please accumulates changes and opens a Release PR with version bump + changelog
  3. Merge the Release PR → GitHub Release + git tag created automatically

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_forge_installer-0.3.13.tar.gz (168.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_forge_installer-0.3.13-py3-none-any.whl (154.2 kB view details)

Uploaded Python 3

File details

Details for the file agent_forge_installer-0.3.13.tar.gz.

File metadata

  • Download URL: agent_forge_installer-0.3.13.tar.gz
  • Upload date:
  • Size: 168.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_forge_installer-0.3.13.tar.gz
Algorithm Hash digest
SHA256 54daa0073f768a592aa82a7545152a765334de4241f11c6f27d7e5cbd16a4fab
MD5 95d9b9ba4f18cedb8822f02017b02c9b
BLAKE2b-256 8bc7cd3da7e83fafd12809dfff4bc5ab1e5c71b3960ddffb92e347f04e7844ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_forge_installer-0.3.13.tar.gz:

Publisher: release-please.yml on Martin-Rancourt/AgentForge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_forge_installer-0.3.13-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_forge_installer-0.3.13-py3-none-any.whl
Algorithm Hash digest
SHA256 647f44cd5aa5b17cb73cfebbcfa84eb5e9e6d0546a4bea28ec6e71a8e5d9ce53
MD5 bfbc9083e02c96b9303ffc848bb25cf5
BLAKE2b-256 5436291a9636494427b23e2336fe2b4b00484a5f5ffb67b959a938a1578ce4a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_forge_installer-0.3.13-py3-none-any.whl:

Publisher: release-please.yml on Martin-Rancourt/AgentForge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page