hermes-bot 0.2.0

Remote Claude Code control via Slack β€” each channel is a repo, each thread is a session
Documentation
# Hermes

[![CI](https://github.com/jbyoung12/hermes-bot/workflows/CI/badge.svg)](https://github.com/jbyoung12/hermes-bot/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Rust Version](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbyoung12%2Fhermes-bot%2Fmain%2FCargo.toml&query=%24.package%5B%22rust-version%22%5D&label=rust&suffix=%2B&color=blue)](https://www.rust-lang.org)

**Control Claude Code from Slack.** Each channel is a repo, each Slack thread is a session.

Access Claude from anywhere β€” start on your laptop, continue on your phone. Sessions persist across messages and restarts, local CLI sessions auto-sync to Slack, and fine-grained tool permissions control what Claude can run. Zero infrastructure needed (Socket Mode, no webhooks).

## Quick Start

```bash
# Install
cargo install hermes-bot

# Set up Slack app (see below), then:
cp .env.example .env
cp config.toml.example config.toml
hermes
```

In Slack, type in a repo channel:

```
fix the failing tests in the auth module
```

Hermes replies in a Slack thread. Reply to continue β€” full history is preserved.

## Features

- πŸ”„ **Persistent sessions** β€” Conversations survive restarts, resume automatically
- πŸ“± **Mobile access** β€” Control Claude from your phone via Slack
- πŸ”— **Local sync** β€” Start sessions with `claude` CLI, continue in Slack
- πŸ”’ **Fine-grained permissions** β€” Control which tools Claude can use per repo
- πŸ‘₯ **Team-friendly** β€” Deploy on a VPS for shared access
- ⚑ **Zero infrastructure** β€” Socket Mode (no webhooks, no public URLs)
- 🧡 **Thread-based** β€” One channel = one repo, one Slack thread = one session

## How It Works

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Slack  β”‚ ◄─── Socket Mode──►│ Hermes  β”‚ ◄─── stdin/out ───►│ Claude CLI  β”‚
β”‚ Channel β”‚                    β”‚         β”‚                    β”‚   Process   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚                              β”‚                                 β”‚
     β”‚  1. User types message       β”‚                                 β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί                                 β”‚
     β”‚                              β”‚  2. Spawn agent                 β”‚
     β”‚                              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί
     β”‚                              β”‚                                 β”‚
     β”‚                              β”‚  3. Stream events (tools, text) β”‚
     β”‚                              │◄─────────────────────────────────
     β”‚  4. Post results in Slack thread                               β”‚
     │◄──────────────────────────────                                 β”‚
     β”‚                              β”‚                                 β”‚
     β”‚  5. User replies in Slack thread                               β–Ό
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί                           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚                              β”‚  6. Resume session        β”‚ Local    β”‚
     β”‚                              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Git Repo β”‚
     β”‚  7. Continue conversation    β”‚                           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Installation

### Prerequisites

- [Rust]https://rustup.rs/ (see `rust-version` in `Cargo.toml` for MSRV)
- [Claude Code CLI]https://docs.anthropic.com/en/docs/claude-code installed and authenticated
- A Slack workspace where you can create apps

### Slack App Setup

1. Go to [api.slack.com/apps]https://api.slack.com/apps β†’ **Create New App** β†’ **From an app manifest**
2. Pick your workspace
3. Paste `slack-manifest.json` from this repo
4. Click **Create**
5. **Install to Workspace** (OAuth & Permissions)
6. Copy **Bot Token** (`xoxb-...`) from OAuth & Permissions
7. Generate **App Token** (`xapp-...`) from Basic Information β†’ App-Level Tokens β†’ add `connections:write` scope
8. **Get your Slack User ID** β€” Profile β†’ three dots β†’ **Copy member ID**

### Configuration

**1. Slack tokens:**

```bash
cp .env.example .env
```

Edit `.env`:

```env
SLACK_APP_TOKEN=xapp-1-...
SLACK_BOT_TOKEN=xoxb-...
```

**2. Configure repos:**

```bash
cp config.toml.example config.toml
```

Edit `config.toml`:

```toml
[slack]
allowed_users = ["U01234567"]  # Your Slack user ID

[defaults]
streaming_mode = "batch"  # or "live" for real-time
allowed_tools = ["Read", "Glob", "Grep", "WebSearch"]

[repos.my-project]
path = "/absolute/path/to/my-project"
allowed_tools = ["Edit", "Write", "Bash(cargo *)"]
```

**3. Run:**

```bash
cargo run  # or 'hermes' if installed
```

Hermes auto-creates channels from repo names (e.g., `repos.backend` β†’ `#backend`).

## Usage

### Commands

**In threads:**

- `!status` β€” Show session info
- `!stop` β€” Stop session
- `!model` β€” Show current model
- `!model opus` β€” Switch to Opus (also: `sonnet`, `haiku`)
- `!execute` β€” Run last plan with fresh context

**Slash commands:**

- `/claude sessions` β€” List active sessions
- `/claude help` β€” Show help

### Local Session Sync

Run `claude` locally in a configured repo, and Hermes auto-detects it:

```
Local session detected (branch: main)
> fix the tests
```

Reply in the Slack thread to continue.

To disable sync globally or per-repo:

```toml
[defaults]
sync_local_sessions = false  # disable for all repos

[repos.my-project]
sync_local_sessions = true   # re-enable for this repo only
```

### Security & Permissions

**User allowlist** β€” Only users in `allowed_users` can interact.

**Tool permissions** β€” Claude can only run pre-approved tools:

- **Global** (all repos): `Read`, `Glob`, `Grep`, `WebSearch` β€” safe, read-only
- **Per-repo**: `Edit`, `Write`, `Bash(cargo *)` β€” scoped to specific repos

Example: `frontend` repo allows `Bash(npm *)` but not `Bash(rm *)`.

**Audit trail** β€” All commands visible in Slack threads.

### Team Usage

**Personal use:** Run on your laptop, message from your phone.

**Team use:** Run on a VPS. Multiple people can use Claude, conversations are shared in Slack.

## Comparison


| Feature            | Hermes                                   | OpenClaw / Alternatives                 |
| ------------------ | ---------------------------------------- | --------------------------------------- |
| **Infrastructure** | Socket Mode β€” laptop or VPS, no webhooks | Requires public URLs, server deployment |
| **Mental model**   | Channel = repo, Slack thread = session   | Varies                                  |
| **Implementation** | Uses Claude CLI β€” gets updates free      | Often reimplements integration          |
| **Local + Remote** | Auto-syncs local sessions                | Typically Slack-only or CLI-only        |
| **Security**       | Per-repo tool permissions                | Often all-or-nothing                    |


## Troubleshooting

**"Failed to spawn claude CLI"**
β†’ Install: `npm install -g @anthropic-ai/claude-code`
β†’ Verify: `claude --version`

**"Socket Mode connection failed"**
β†’ Check `SLACK_APP_TOKEN` starts with `xapp-` and has `connections:write` scope
β†’ Check `SLACK_BOT_TOKEN` starts with `xoxb-`
β†’ Enable Socket Mode in app settings

**Bot doesn't respond**
β†’ Check `allowed_users` includes your Slack user ID
β†’ Verify bot is in the channel

**Sessions not resuming**
β†’ Ensure `sessions.json` is writable
β†’ Agent processes killed on shutdown, auto-recover on next message

## Architecture

- **Socket Mode** β€” No public URL, works on laptop or VPS
- **Agent trait** β€” Extensible backend (Claude Code implemented)
- **Session persistence** β€” `sessions.json` survives restarts
- **Concurrency guard** β€” One agent per Slack thread
- **Local session sync** β€” Filesystem watcher imports CLI sessions

```
src/
  main.rs          Socket Mode setup, shutdown handling
  config.rs        Config loading and validation
  session.rs       Session persistence (sessions.json)
  sync.rs          Local CLI session sync (notify crate)
  slack/           Slack API, message handlers, formatting
  agent/           Agent trait, Claude CLI integration, protocol parser
```

## Contributing

Contributions welcome!

```bash
git clone https://github.com/jbyoung12/hermes-bot.git
cd hermes-bot
cp .env.example .env
cp config.toml.example config.toml
cargo test
```

**Before submitting:**

```bash
cargo fmt
cargo clippy -- -D warnings
cargo test
```

**Guidelines:**

- Use `thiserror` for errors
- Add tests for new features
- Keep commits focused

Open PRs against `main`. For bugs, include steps to reproduce and your Rust version.

## License

MIT β€” see [LICENSE](LICENSE)