Commit Graph

40 Commits

Author SHA1 Message Date
teernisse
62d23793c4 feat(server): add spawn API HTTP routes
Add routing for spawn-related endpoints to HttpMixin:
- GET /api/projects -> _handle_projects
- GET /api/health -> _handle_health
- POST /api/spawn -> _handle_spawn
- POST /api/projects/refresh -> _handle_projects_refresh

Update CORS preflight (AC-39) to include GET in allowed methods
and Authorization in allowed headers.

Closes bd-2al
2026-02-26 17:03:00 -05:00
teernisse
7b1e47adc0 feat(dashboard): implement SpawnModal component 2026-02-26 17:00:51 -05:00
teernisse
a7a9ebbf2b feat(server): implement SpawnMixin for agent spawning
Implement amc_server/mixins/spawn.py with:
- _handle_spawn: POST /api/spawn handler with auth, validation, rate limiting
- _validate_spawn_params: path traversal/symlink escape protection
- _check_zellij_session_exists: session availability check
- _wait_for_session_file: poll for spawn correlation via spawn_id
- _spawn_agent_in_project_tab: Zellij tab creation + pane spawn
- _handle_projects: GET cached project list
- _handle_projects_refresh: POST to refresh cache
- _handle_health: Zellij availability check
- load_projects_cache: module-level project directory scanner

Closes bd-5m4
2026-02-26 17:00:49 -05:00
teernisse
48c3ddce90 test(dashboard): add autocomplete trigger/filter tests 2026-02-26 17:00:19 -05:00
teernisse
7059dea3f8 test(skills): add comprehensive SkillsMixin tests 2026-02-26 16:59:34 -05:00
teernisse
e4a0631fd7 feat(hook): add spawn_id correlation to amc-hook
Read AMC_SPAWN_ID env var and include spawn_id in session JSON when
present. This enables deterministic spawn correlation: the server
generates a UUID, passes it via env to the spawned agent, and then
polls for a session file containing that specific spawn_id.

Closes: bd-1zy
2026-02-26 16:58:20 -05:00
teernisse
1bece476a4 feat(server): add spawn constants to context.py 2026-02-26 16:58:02 -05:00
teernisse
e99ae2ed89 feat(dashboard): add spawn API constants 2026-02-26 16:57:56 -05:00
teernisse
49a57b5364 feat(dashboard): scroll selected autocomplete item into view
Closes bd-4lc. When navigating with arrow keys, the selected item now
scrolls into view using scrollIntoView({ block: 'nearest' }) to prevent
jarring scrolls when item is already visible.
2026-02-26 16:55:34 -05:00
teernisse
db3d2a2e31 feat(dashboard): add click-outside dismissal for autocomplete dropdown
Closes bd-3ny. Added mousedown listener that dismisses the dropdown when
clicking outside both the dropdown and textarea. Uses early return to avoid
registering listeners when dropdown is already closed.
2026-02-26 16:54:40 -05:00
teernisse
ba16daac2a feat(dashboard): complete autocomplete UI with dropdown and keyboard navigation
- Add triggerInfo state and filteredSkills useMemo
- Add showAutocomplete, selectedIndex state management
- Implement insertSkill callback for skill insertion
- Add full keyboard navigation (ArrowUp/Down, Enter/Tab, Escape)
- Wrap textarea in relative container for dropdown positioning
- Add autocomplete dropdown UI with empty states and mouse interaction

Closes: bd-29o, bd-1y3, bd-3vd, bd-2uj, bd-3us, bd-253

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 16:52:36 -05:00
teernisse
c7db46191c feat(dashboard): add skill autocomplete server-side enumeration and client wiring
- Add SkillsMixin with _enumerate_claude_skills and _enumerate_codex_skills
- Claude: reads ~/.claude/skills/, parses YAML frontmatter for descriptions
- Codex: reads curated cache + ~/.codex/skills/ user directory
- Add /api/skills?agent= endpoint to HttpMixin
- Add fetchSkills() API helper in dashboard
- Wire autocomplete config through Modal -> SessionCard -> SimpleInput
- Add getTriggerInfo() for detecting trigger at valid positions

Closes: bd-3q1, bd-sv1, bd-3eu, bd-g9t, bd-30p, bd-1ba, bd-2n7, bd-3s3

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 16:48:23 -05:00
teernisse
2926645b10 docs: add implementation plans for upcoming features
Planning documents for future AMC features:

PLAN-slash-autocomplete.md:
- Slash-command autocomplete for SimpleInput
- Skills API endpoint, SlashMenu dropdown, keyboard navigation
- 8 implementation steps with file locations and dependencies

plans/agent-spawning.md:
- Agent spawning acceptance criteria documentation
- Spawn command integration, status tracking, error handling
- Written as testable acceptance criteria (AC-1 through AC-10)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:25:09 -05:00
teernisse
6e566cfe82 chore: add .gitignore for bv cache directory
Ignore .bv/ directory which contains local cache and configuration
for the bv (beads viewer) graph-aware triage tool.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:25:02 -05:00
teernisse
117784f8ef chore: initialize beads project tracking
Add .beads/ directory for dependency-aware issue tracking with the
beads (br) CLI. This provides lightweight task management for the
AMC project.

Files:
- config.yaml: Project configuration (prefix, priorities, types)
- issues.jsonl: Issue database in append-only JSONL format
- metadata.json: Project metadata and statistics
- .gitignore: Ignore database files, locks, and temporaries

Current issues track slash-command autocomplete feature planning
(fetchSkills API, SlashMenu component, hook integration, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:57 -05:00
teernisse
de994bb837 feat(dashboard): add markdown preview for AskUserQuestion options
Implement side-by-side preview layout when AskUserQuestion options
include markdown content, matching the Claude Code tool spec.

Hook changes (bin/amc-hook):
- Extract optional 'markdown' field from question options
- Only include when present (maintains backward compatibility)

QuestionBlock layout:
- Detect hasMarkdownPreviews via options.some(opt => opt.markdown)
- Standard layout: vertical option list (unchanged)
- Preview layout: 40% options left, 60% preview pane right
- Fixed 400px preview height prevents layout thrashing on hover
- Track previewIndex state, update on mouseEnter/focus

Content rendering (smart detection):
- Code fences (starts with ```): renderContent() for syntax highlighting
- Everything else: raw <pre> to preserve ASCII diagrams exactly
- "No preview for this option" when hovered option lacks markdown

OptionButton enhancements:
- Accept selected, onMouseEnter, onFocus props
- Visual selected state: brighter border/bg with subtle shadow
- Compact padding (py-2 vs py-3.5) for preview layout density
- Graceful undefined handling for standard layout usage

Bug fixes during development:
- Layout thrashing: Fixed height (not max-height) on preview pane
- ASCII corruption: Detect code fences vs plain text for render path
- Horizontal overflow: Use overflow-auto (not just overflow-y-auto)
- Data pipeline: Hook was stripping markdown field (root cause)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:35 -05:00
teernisse
b9c1bd6ff1 feat(dashboard): improve real-time updates and scroll behavior
Major UX improvements to conversation display and state management.

Scroll behavior (SessionCard.js):
- Replace scroll-position tracking with wheel-event intent detection
- Accumulate scroll-up distance before disabling sticky mode (50px threshold)
- Re-enable sticky on scroll-down when near bottom (100px threshold)
- Always scroll to bottom on first load or user's own message submission
- Use requestAnimationFrame for smooth scroll positioning

Optimistic updates (App.js):
- Immediately show user messages before API confirmation
- Remove optimistic message on send failure
- Eliminates perceived latency when sending responses

Error tracking integration (App.js):
- Wire up trackError/clearErrorCount for API operations
- Track: state fetch, conversation fetch, respond, dismiss, SSE init/parse
- Clear error counts on successful operations
- Clear SSE event cache on reconnect to force refresh

Conversation management (App.js):
- Use mtime_ns (preferred) or last_event_at for change detection
- Clean up conversation cache when sessions are dismissed
- Add modalSessionRef for stable reference across renders

Message stability (ChatMessages.js):
- Prefer server-assigned message IDs for React keys
- Fallback to role+timestamp+index for legacy messages

Input UX (SimpleInput.js):
- Auto-refocus textarea after successful submission
- Use setTimeout to ensure React has re-rendered first

Sorting simplification (status.js):
- Remove status-based group/session reordering
- Return groups in API order (server handles sorting)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:21 -05:00
teernisse
3dc10aa060 feat(dashboard): add toast notification system with error tracking
Add lightweight toast notification infrastructure that surfaces
repeated errors to users while avoiding alert fatigue.

Components:
- ToastContainer: Renders toast notifications with auto-dismiss
- showToast(): Singleton function to display messages from anywhere
- trackError(): Counts errors by key, surfaces toast after threshold

Error tracking strategy:
- Errors logged immediately (console.error)
- Toast shown only after 3+ occurrences within 30-second window
- Prevents spam from transient network issues
- Reset counter on success (clearErrorCount)

Toast styling:
- Fixed position bottom-right with z-index 100
- Type-aware colors (error=red, success=green, info=yellow)
- Manual dismiss button with auto-dismiss after 5 seconds
- Backdrop blur and slide-up animation

This prepares the dashboard to gracefully handle API failures
without overwhelming users with error popups for transient issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:06 -05:00
teernisse
0d15787c7a test(server): add unit tests for conversation mtime tracking
Comprehensive test coverage for _get_conversation_mtime() and its
integration with _collect_sessions().

Test cases:
- Claude sessions: file exists, file missing, OSError on stat,
  missing project_dir, missing session_id
- Codex sessions: transcript_path exists, transcript_path missing,
  discovery fallback, discovery returns None, OSError on stat
- Edge cases: unknown agent type, missing agent key
- Integration: mtime included when file exists, omitted when missing
- Change detection: mtime changes trigger payload hash changes

Uses mock patching to isolate file system interactions and test
error handling paths without requiring actual conversation files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:23:55 -05:00
teernisse
dcbaf12f07 feat(server): add conversation mtime tracking for real-time updates
Add conversation_mtime_ns field to session state that tracks the actual
modification time of conversation files. This enables more responsive
dashboard updates by detecting changes that occur between hook events
(e.g., during streaming tool execution).

Changes:
- state.py: Add _get_conversation_mtime() to stat conversation files
  and include mtime_ns in session payloads when available
- conversation.py: Add stable message IDs (claude-{session}-{n} format)
  for React key stability and message deduplication
- control.py: Fix FIFO eviction for dismissed_codex_ids - set.pop()
  removes arbitrary element, now uses dict with insertion-order iteration
- context.py: Update dismissed_codex_ids type from set to dict

The mtime approach complements existing last_event_at tracking:
- last_event_at: Changes on hook events (session boundaries)
- conversation_mtime_ns: Changes on every file write (real-time)

Dashboard can now detect mid-session conversation updates without
waiting for the next hook event.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:23:42 -05:00
teernisse
fa1ad4b22b unify card/modal 2026-02-26 11:39:39 -05:00
teernisse
31862f3a40 perf(dashboard): optimize CSS transitions and add entrance animations
Performance and polish improvements across dashboard components:

Transition optimizations (reduces reflow/repaint overhead):
- OptionButton: transition-all → transition-[transform,border-color,
  background-color,box-shadow]
- QuestionBlock: Add transition-colors to textarea, transition-all →
  transition-[transform,filter] on send button
- SimpleInput: Same pattern as QuestionBlock
- Sidebar: transition-all → transition-colors for project buttons

Animation additions:
- App: Add animate-fade-in-up to loading and error state containers
- MessageBubble: Make fade-in-up animation conditional on non-compact
  mode to avoid animation spam in card preview

Using specific transition properties instead of transition-all tells
the browser exactly which properties to watch, avoiding unnecessary
style recalculation on unrelated property changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:28 -05:00
teernisse
4740922b8d refactor(dashboard): simplify chat rendering and add scroll behavior
Removes unnecessary complexity from ChatMessages while adding proper
scroll management to SessionCard:

ChatMessages.js:
- Remove scroll position tracking refs and effects (wasAtBottomRef,
  prevMessagesLenRef, containerRef)
- Remove spinner display logic (moved to parent components)
- Simplify to pure message filtering and rendering
- Add display limit (last 20 messages) with offset tracking for keys

SessionCard.js:
- Add chatPaneRef for scroll container
- Add useEffect to scroll to bottom when conversation updates
- Provides natural "follow" behavior for new messages

The refactor moves scroll responsibility to the component that owns
the scroll container, reducing prop drilling and effect complexity.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:08 -05:00
teernisse
8578a19330 feat(dashboard): add smooth modal entrance/exit animations
Implements animated modal open/close with accessibility support:

- Add closing state with 200ms exit animation before unmount
- Refactor to React hooks-compliant structure (guards after hooks)
- Add CSS keyframes for backdrop fade and panel scale+translate
- Include prefers-reduced-motion media query to disable animations
  for users with vestibular sensitivities
- Use handleClose callback wrapper for consistent animation behavior
  across Escape key, backdrop click, and close button

The animations provide visual continuity without being distracting,
and gracefully degrade to instant transitions when reduced motion
is preferred.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:00 -05:00
teernisse
754e85445a test(server): add unit tests for context, control, and state mixins
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>
2026-02-25 16:31:51 -05:00
teernisse
183942fbaa fix(dashboard): spinner animation polish and accessibility
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>
2026-02-25 15:36:36 -05:00
teernisse
2f80995f8d fix(dashboard): robust tool call display and filter logic
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>
2026-02-25 15:36:26 -05:00
teernisse
8224acbba7 feat(dashboard): add working indicator for active sessions
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>
2026-02-25 15:20:26 -05:00
teernisse
7cf51427b7 fix(codex): parse tool calls and reasoning in conversation API
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>
2026-02-25 15:20:18 -05:00
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
teernisse
da08d7a588 refactor(dashboard): extract modular Preact component structure
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>
2026-02-25 15:01:47 -05:00
teernisse
9cd91f6b4e feat(launcher): improve bin/amc with launchctl, health checks, and PID recovery
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>
2026-02-25 15:01:35 -05:00
teernisse
a7b2b3b902 refactor(server): extract amc_server package from monolithic script
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>
2026-02-25 15:01:26 -05:00
teernisse
e718c44555 feat(dashboard): add markdown rendering, project grouping, and visual refresh
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
2026-02-25 11:51:39 -05:00
teernisse
e994c7a0e8 feat(server): rewrite Codex pane discovery using process inspection
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
2026-02-25 11:51:28 -05:00
teernisse
c777ef05b6 docs: add comprehensive README with architecture and API reference
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
2026-02-25 11:21:48 -05:00
teernisse
0551b9fd89 fix: cross-platform browser opening and hook robustness
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
2026-02-25 11:21:35 -05:00
teernisse
a9ed8f90f4 feat(server): add Codex session support and improve session lifecycle
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
2026-02-25 11:21:30 -05:00
teernisse
4d2fcc9a3b feat(dashboard): rewrite UI with Preact for reactive session monitoring
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.
2026-02-25 11:21:15 -05:00
teernisse
b2a5712202 Initial work, pre-preact refactor 2026-02-25 09:21:59 -05:00