Metadata-Version: 2.4
Name: agent-forge-installer
Version: 0.3.6
Summary: AgentForge — multi-agent Telegram bot framework powered by Claude Code
Author: Martin Rancourt
License: MIT
Project-URL: Homepage, https://github.com/Martin-Rancourt/agentforge
Project-URL: Repository, https://github.com/Martin-Rancourt/agentforge
Project-URL: Bug Tracker, https://github.com/Martin-Rancourt/agentforge/issues
Keywords: telegram,bot,ai,claude,multi-agent,framework
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: fastapi<1,>=0.116
Requires-Dist: pydantic<3,>=2.0
Requires-Dist: uvicorn<1,>=0.35
Provides-Extra: http
Requires-Dist: requests>=2.28; extra == "http"
Provides-Extra: voice
Requires-Dist: faster-whisper>=1.0; extra == "voice"
Provides-Extra: embeddings
Requires-Dist: sentence-transformers>=2.0; extra == "embeddings"
Requires-Dist: numpy>=1.24; extra == "embeddings"
Provides-Extra: google
Requires-Dist: google-api-python-client>=2.100; extra == "google"
Requires-Dist: google-auth>=2.20; extra == "google"
Provides-Extra: full
Requires-Dist: requests>=2.28; extra == "full"
Requires-Dist: faster-whisper>=1.0; extra == "full"
Requires-Dist: sentence-transformers>=2.0; extra == "full"
Requires-Dist: numpy>=1.24; extra == "full"
Requires-Dist: google-api-python-client>=2.100; extra == "full"
Requires-Dist: google-auth>=2.20; extra == "full"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: httpx>=0.28; extra == "dev"

# 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:

```bash
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

```bash
# 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.

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

### `agentforge agent`

Manage agents declaratively.

```bash
# 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.

```bash
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.

```bash
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.

```bash
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`

```jsonc
{
  "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`

```bash
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:

```json
"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:
- `archie` → `vault/Agents/archie/tasks.md`
- `catherine` → `vault/Agents/catherine/tasks.md`
- `christophe` → `vault/Agents/christophe/tasks.md`
- `victor` → `vault/Agents/victor/tasks.md`
- `architect` → `vault/Agents/architect/tasks.md`

### File Format

```markdown
# 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:**

```bash
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_ROOT/.local/bin/claude`
- **Telegram Bot** — create one via [@BotFather](https://t.me/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

```bash
# 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

```bash
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

```bash
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

```bash
# 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](https://t.me/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**:
   ```bash
   # 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

```bash
# 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

```bash
# 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)

```bash
crontab -e
```

Add:

```cron
# 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](https://syncthing.net/) 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

```bash
# 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

```bash
# 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](https://apps.apple.com/app/m%C3%B6bius-sync/id1539203216) is the only Syncthing-compatible app for iOS. It costs ~$5 USD (one-time purchase).

1. **Install** [Möbius Sync](https://apps.apple.com/app/m%C3%B6bius-sync/id1539203216) 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

```bash
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](https://github.com/googleapis/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
