Adds comprehensive test coverage for the amc_server package:
- test_context.py: Tests _resolve_zellij_bin preference order (which
first, then fallback to bare name)
- test_control.py: Tests SessionControlMixin including two-step Enter
injection with configurable delay, freeform response handling,
plugin inject with explicit session/pane targeting, and unsafe
fallback behavior
- test_state.py: Tests StateMixin zellij session parsing with the
resolved binary path
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Polish the working indicator animations:
- Use cubic-bezier easing for smoother spinner rotation
- Add will-change hints for GPU acceleration
- Add display: inline-block to bounce-dot spans (required for transform)
- Add prefers-reduced-motion media query to disable animations for
motion-sensitive users (accessibility)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two fixes for tool call display in the dashboard:
1. **filterDisplayMessages includes tool_calls** (MessageBubble.js)
Previously filtered out messages with only tool_calls (no content/thinking).
Now correctly keeps messages that have tool_calls.
2. **Type-safe getToolSummary** (markdown.js)
The heuristic tool summary extractor was calling .slice() without
type checks. If a tool input had a non-string value (e.g., number),
it would throw TypeError. Now uses a helper function to safely
check types before calling string methods.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Visual feedback when an agent is actively processing:
1. **Spinner on status dots** (SessionCard.js, Modal.js)
- Status dot gets a spinning ring animation when session is active/starting
- Uses CSS border trick with transparent borders except top
2. **Working indicator in chat** (ChatMessages.js, Modal.js)
- Shows at bottom of conversation when agent is working
- Bouncing dots animation ("...") next to "Agent is working" text
- Only visible for active/starting statuses
3. **CSS animations** (styles.css)
- spin-ring: 0.8s rotation for the status dot border
- bounce-dot: staggered vertical bounce for the working dots
4. **Status metadata** (status.js)
- Added `spinning: true` flag for active and starting statuses
- Used by components to conditionally render spinner elements
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Codex conversation parser was only handling "message" payload types,
missing tool calls entirely. Codex uses separate response_items:
- function_call: tool invocations with name, arguments, call_id
- reasoning: thinking summaries (encrypted content, visible summary)
- message: user/assistant text (previously the only type handled)
Changes:
- Parse function_call payloads and accumulate as tool_calls array
- Attach tool_calls to the next assistant message, or flush standalone
- Parse reasoning payloads and extract summary text as thinking
- Add _parse_codex_arguments() helper to handle JSON string arguments
This fixes the dashboard not showing Codex tool calls like exec_command,
read_file, etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Replace the monolithic single-file dashboards (dashboard.html,
dashboard-preact.html) with a proper modular directory structure:
dashboard/
index.html - Entry point, loads main.js
main.js - App bootstrap, mounts <App> to #root
styles.css - Global styles (dark theme, typography)
components/
App.js - Root component, state management, polling
Header.js - Top bar with refresh/timing info
Sidebar.js - Project tree navigation
SessionCard.js - Individual session card with status/actions
SessionGroup.js - Group sessions by project path
Modal.js - Full conversation viewer overlay
ChatMessages.js - Message list with role styling
MessageBubble.js - Individual message with markdown
QuestionBlock.js - User question input with quick options
EmptyState.js - "No sessions" placeholder
OptionButton.js - Quick response button component
SimpleInput.js - Text input with send button
lib/
preact.js - Preact + htm ESM bundle (CDN shim)
markdown.js - Lightweight markdown-to-HTML renderer
utils/
api.js - fetch wrappers for /api/* endpoints
formatting.js - Time formatting, truncation helpers
status.js - Session status logic, action availability
This structure enables:
- Browser-native ES modules (no build step required)
- Component reuse and isolation
- Easier styling and theming
- IDE support for component navigation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enhance the launcher script with robust process management:
Process Lifecycle:
- Use macOS launchctl to spawn server process, avoiding parent-shell
cleanup kills that terminate AMC when Claude Code's shell exits
- Fall back to setsid+nohup on non-macOS systems
- Graceful shutdown with 5-second wait loop before force-kill
Health Checking:
- Replace simple PID file check with actual HTTP health check
- curl /api/state to verify server is truly responsive, not just alive
- Report "healthy" vs "running but not responding" states
PID Recovery:
- Recover orphan PID file from port listener (lsof -tiTCP:7400)
- Validate listener is actually amc-server (not another process on 7400)
- Auto-refresh PID file when process exists but file is stale
Logging:
- New LOG_FILE at ~/.local/share/amc/server.log
- show_recent_logs() helper for diagnostics
- Consistent log routing through launchctl submit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Split the 860+ line bin/amc-server into a modular Python package:
amc_server/
__init__.py - Package marker
context.py - Shared constants (DATA_DIR, PORT, CLAUDE_PROJECTS_DIR, etc.)
handler.py - AMCHandler class using mixin composition
logging_utils.py - Structured logging setup with signal handlers
server.py - Main entry point (ThreadingHTTPServer)
mixins/
__init__.py - Mixin package marker
control.py - Session control (dismiss, respond via Zellij)
conversation.py - Conversation history parsing (Claude JSONL format)
discovery.py - Session discovery (Codex pane inspection, Zellij cache)
http.py - HTTP response helpers (CORS, JSON, static files)
parsing.py - Session state parsing and aggregation
state.py - Session state endpoint logic
The monolithic bin/amc-server becomes a thin launcher that just imports
and calls main(). This separation enables:
- Easier testing of individual components
- Better IDE support (proper Python package structure)
- Cleaner separation of concerns (discovery vs parsing vs control)
- ThreadingHTTPServer instead of single-threaded (handles concurrent requests)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Markdown & syntax highlighting:
- Replace homegrown backtick parser with marked + DOMPurify + highlight.js
- Register 10 language grammars (JS, TS, Bash, JSON, Python, Rust, CSS,
HTML/XML, SQL, YAML) for fenced code block highlighting
- Sanitize rendered HTML via DOMPurify to prevent XSS from message content
Project-based session grouping:
- Replace four status-based groups (attention/active/starting/done) with
project-directory grouping via groupSessionsByProject()
- Groups sort by most urgent status first, then most recent activity
- Sessions within each group sort by urgency then recency
- Group headers show project name, agent count, and per-status chips
- Project directory path shown below group header
Visual refinements:
- Darken color palette across the board (bg, surface, scrollbar, glass
panels) for deeper contrast
- Add agent-header-codex and agent-header-claude CSS classes for
color-coded card and modal headers (teal vs purple tints)
- Increase agent badge visibility with higher opacity borders/backgrounds
- Cards now fixed-width 600px with taller height (850px) in flex-wrap
layout instead of responsive grid
- Input focus border color now matches the session's status color
instead of hardcoded active/attention colors
- Send button background also matches status color for visual coherence
- Lock body scroll when session modal is open
- Add agent badge to modal header for consistent identification
Replace the zellij dump-layout approach with direct process inspection
for matching Codex sessions to Zellij panes. The old method couldn't
extract pane IDs from layout output, leaving them empty. The new
approach uses a three-step pipeline:
1. pgrep -x codex to find running Codex PIDs
2. ps eww to extract ZELLIJ_PANE_ID and ZELLIJ_SESSION_NAME from each
process's inherited environment variables
3. lsof -a -p <pids> -d cwd to batch-resolve working directories
Session-to-pane matching then uses a two-tier strategy:
- Primary: lsof -t <session_file> to find which PID has the JSONL open
- Fallback: normalized CWD comparison
Also adds:
- _codex_pane_cache with 5-second TTL to avoid running pgrep/ps/lsof
on every dashboard poll cycle
- _dismissed_codex_ids set to track Codex sessions the user has
dismissed, preventing re-discovery on subsequent polls
- Clearer error message when session lacks pane info for input routing
Document AMC (Agent Mission Control) for new users and contributors:
- Overview: real-time dashboard for Claude Code session monitoring
- Features: session grouping, conversation history, response injection,
question detection (AskUserQuestion and prose), session lifecycle
- Installation: symlink setup and PATH configuration
- Hook Setup: complete settings.json configuration for all required
Claude Code hooks (SessionStart, UserPromptSubmit, Stop, SessionEnd,
PreToolUse/PostToolUse for AskUserQuestion)
- Architecture: ASCII diagram showing data flow from Claude Code hooks
through amc-hook to disk storage to amc-server to dashboard
- Components: descriptions of amc launcher, amc-server, amc-hook,
and dashboard-preact.html
- Data Storage: runtime data paths in ~/.local/share/amc/
- API Reference: all 6 endpoints with methods and descriptions
- Response Injection: request body format and two-phase freeform flow
- Session Statuses: starting, active, needs_attention, done
- Cleanup Behavior: orphan detection, stale session removal, event log gc
- Requirements: Python 3.8+, Zellij, Claude Code hooks
- Optional Zellij Plugin: for focus-free response injection
bin/amc:
- Add cross-platform browser opening: try 'open' (macOS) first,
fall back to 'xdg-open' (Linux) for desktop environments
- Wrap browser opening in conditional to avoid errors on headless systems
bin/amc-hook:
- Fix edge case in question extraction where tool_input.get("questions")
could return None instead of empty list, causing TypeError
Extend AMC to monitor Codex sessions alongside Claude Code:
Codex Integration:
- Discover active Codex sessions from ~/.codex/sessions/*.jsonl
- Parse Codex JSONL format (response_item/message payloads) for
conversation history, filtering out developer role injections
- Extract session metadata (cwd, timestamp) from session_meta records
- Match Codex sessions to Zellij panes via cwd for response injection
- Add ?agent=codex query param to /api/conversation endpoint
Session Lifecycle Improvements:
- Cache Zellij session list for 5 seconds to reduce subprocess calls
- Proactive liveness check: auto-delete orphan "starting" sessions
when their Zellij session no longer exists
- Clean up stale "starting" sessions after 1 hour (likely orphaned)
- Preserve existing event log cleanup (24h for orphan logs)
Code Quality:
- Refactor _serve_conversation into _parse_claude_conversation and
_parse_codex_conversation for cleaner separation
- Add _discover_active_codex_sessions for session file generation
- Add _get_codex_zellij_panes to match sessions to panes
- Use JSON error responses consistently via _json_error helper
Replace vanilla JS dashboard with a modern Preact-based implementation:
- Component architecture: Header, SessionCard, SessionGroup, Modal,
ChatMessages, QuestionBlock, SimpleInput, OptionButton, EmptyState
- Status-grouped display: sessions organized by needs_attention, active,
starting, and done with color-coded visual indicators
- Real-time conversation view: fetch and display chat history from
Claude Code JSONL logs with auto-scroll on new messages
- Inline response input: send messages directly from cards without
opening modal, supports both structured options and freeform text
- Modal detail view: full conversation with markdown rendering,
timestamps, and keyboard navigation (Enter to send, Escape to close)
- Smart polling: 3-second state refresh with silent conversation
updates for active sessions to minimize UI flicker
- Flexoki color scheme: custom dark theme with attention (amber),
active (green), starting (blue), and done (purple) status colors
- Berkeley Mono font stack with graceful fallbacks
- Tailwind CSS via CDN with custom color configuration
- Responsive card layout with fixed 600px height and overflow handling
The dashboard provides a unified view for monitoring multiple Claude
Code sessions, detecting when agents need human input, and responding
directly via Zellij pane injection.