Files
amc/README.md
teernisse be2dd6a4fb fix(zellij): robust binary resolution and two-step Enter injection
Two reliability fixes for response injection:

1. **Zellij binary resolution** (context.py, state.py, control.py)
   
   When AMC is started via macOS launchctl, PATH is minimal and may not
   include Homebrew's bin directory. The new `_resolve_zellij_bin()`
   function tries `shutil.which("zellij")` first, then falls back to
   common installation paths:
   - /opt/homebrew/bin/zellij (Apple Silicon Homebrew)
   - /usr/local/bin/zellij (Intel Homebrew)
   - /usr/bin/zellij
   
   All subprocess calls now use ZELLIJ_BIN instead of hardcoded "zellij".

2. **Two-step Enter injection** (control.py)
   
   Previously, text and Enter were sent together, causing race conditions
   where Claude Code would receive only the Enter key (blank submit).
   Now uses `_inject_text_then_enter()`:
   - Send text (without Enter)
   - Wait for configurable delay (default 200ms)
   - Send Enter separately
   
   Delay is configurable via AMC_SUBMIT_ENTER_DELAY_MS env var (0-2000ms).

3. **Documentation updates** (README.md)
   
   - Update file table: dashboard-preact.html → dashboard/
   - Clarify plugin is required (not optional) for pane-targeted injection
   - Document AMC_ALLOW_UNSAFE_WRITE_CHARS_FALLBACK env var
   - Note about Zellij resolution for launchctl compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:20:08 -05:00

6.5 KiB

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:
ln -s /path/to/amc/bin/amc ~/.local/bin/amc
  1. Configure Claude Code hooks (see Hook Setup)

Usage

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:

{
  "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:

{
  "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:

python3 -m unittest discover -s tests -v

Zellij Plugin

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

# 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