# AMC — Agent Mission Control

A real-time dashboard for monitoring and interacting with Claude Code sessions.

## Overview

AMC provides a unified view of all running Claude Code sessions across projects. It displays session status, conversation history, and pending questions — and allows you to respond directly from the dashboard without switching terminal tabs.

## Features

- **Real-time monitoring** — Sessions are grouped by status (needs attention, active, starting, done) with 3-second polling
- **Conversation history** — View the full chat history for any session, pulled from Claude Code's JSONL logs
- **Response injection** — Send responses to agents directly via Zellij pane integration
- **Question detection** — Detects both structured `AskUserQuestion` prompts and prose questions (messages ending with "?")
- **Session lifecycle** — Automatically cleans up orphaned sessions and stale event logs

## Installation

1. Clone this repository
2. Symlink the launcher to your PATH:

```bash
ln -s /path/to/amc/bin/amc ~/.local/bin/amc
```

3. Configure Claude Code hooks (see [Hook Setup](#hook-setup))

## Usage

```bash
amc start   # Start the server and open the dashboard
amc stop    # Stop the server
amc status  # Check if the server is running
```

The dashboard opens at `http://127.0.0.1:7400`.

## Hook Setup

AMC requires Claude Code hooks to report session state. Add this to your `~/.claude/settings.json`:

```json
{
  "hooks": {
    "SessionStart": [
      { "command": "/path/to/amc/bin/amc-hook" }
    ],
    "UserPromptSubmit": [
      { "command": "/path/to/amc/bin/amc-hook" }
    ],
    "Stop": [
      { "command": "/path/to/amc/bin/amc-hook" }
    ],
    "SessionEnd": [
      { "command": "/path/to/amc/bin/amc-hook" }
    ],
    "PreToolUse": [
      { "matcher": "AskUserQuestion", "command": "/path/to/amc/bin/amc-hook" }
    ],
    "PostToolUse": [
      { "matcher": "AskUserQuestion", "command": "/path/to/amc/bin/amc-hook" }
    ]
  }
}
```

## Architecture

```
┌─────────────────────┐     ┌─────────────────────┐
│   Claude Code       │────▶│   amc-hook          │
│   (hooks)           │     │   (writes state)    │
└─────────────────────┘     └──────────┬──────────┘
                                       │
                                       ▼
                            ┌─────────────────────┐
                            │   ~/.local/share/   │
                            │   amc/sessions/     │
                            │   amc/events/       │
                            └──────────┬──────────┘
                                       │
                                       ▼
┌─────────────────────┐     ┌─────────────────────┐
│   Dashboard         │◀────│   amc-server        │
│   (Preact UI)       │     │   (Python HTTP)     │
└─────────────────────┘     └─────────────────────┘
```

### Components

| Component | Description |
|-----------|-------------|
| `bin/amc` | Launcher script — start/stop/status commands |
| `bin/amc-server` | Python HTTP server serving the API and dashboard |
| `bin/amc-hook` | Hook script called by Claude Code to write session state |
| `dashboard/` | Modular Preact dashboard (index.html, components/, lib/, utils/) |

### Data Storage

All runtime data lives in `~/.local/share/amc/`:

| Path | Contents |
|------|----------|
| `sessions/*.json` | Current session state (one file per session) |
| `events/*.jsonl` | Event log for each session (append-only) |
| `server.pid` | PID file for the running server |
| `server.log` | Server stdout/stderr |

## API Reference

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/` | Dashboard HTML |
| GET | `/api/state` | All sessions as JSON |
| GET | `/api/events/{id}` | Event timeline for a session |
| GET | `/api/conversation/{id}?project_dir=...` | Conversation history (from Claude Code logs) |
| POST | `/api/dismiss/{id}` | Dismiss (delete) a completed session |
| POST | `/api/respond/{id}` | Send a response to a session |

### Response Injection

The `/api/respond/{id}` endpoint injects text into a session's Zellij pane. Request body:

```json
{
  "text": "your response here",
  "freeform": true,
  "optionCount": 3
}
```

- `text` — The response to send
- `freeform` — If true, treats as freeform text (selects "Other" option first)
- `optionCount` — Number of options in the current question (used for freeform)

Response injection works via:
1. **Zellij plugin** (`~/.config/zellij/plugins/zellij-send-keys.wasm`) — Required for pane-targeted sends and Enter submission
2. **Optional unsafe fallback** (`AMC_ALLOW_UNSAFE_WRITE_CHARS_FALLBACK=1`) — Uses focused-pane `write-chars` only when explicitly enabled

AMC resolves the Zellij binary from PATH plus common Homebrew locations (`/opt/homebrew/bin/zellij`, `/usr/local/bin/zellij`) so response injection still works when started via `launchctl`.

## Session Statuses

| Status | Meaning |
|--------|---------|
| `starting` | Session started, no prompt submitted yet |
| `active` | Session is processing work |
| `needs_attention` | Agent is waiting for user input (question or AskUserQuestion) |
| `done` | Session stopped (can be dismissed) |

## Cleanup Behavior

- **Orphan sessions**: Sessions with missing Zellij sessions in "starting" status are auto-deleted
- **Stale "starting" sessions**: Sessions stuck in "starting" for >1 hour are removed
- **Stale event logs**: Event logs without matching sessions are deleted after 24 hours
- **SessionEnd**: Deletes the session file immediately

## Requirements

- Python 3.8+
- Zellij (for response injection)
- Claude Code with hooks support

## Testing

Run the server test suite:

```bash
python3 -m unittest discover -s tests -v
```

## Zellij Plugin

For pane-targeted response injection (including reliable Enter submission), install the `zellij-send-keys` plugin:

```bash
# Build and install the plugin
# (See zellij-send-keys repository for instructions)
```

Place the compiled WASM at `~/.config/zellij/plugins/zellij-send-keys.wasm`.

## License

MIT
