Add shell-style up/down arrow navigation through past messages in the
SimpleInput component. History is derived from the conversation data
already parsed from session logs — no new state management needed.
dashboard/components/SessionCard.js:
- Pass `conversation` prop through to SimpleInput (line 170)
- Prop chain verified: App -> SessionCard -> SimpleInput, including
the Modal/enlarged path (Modal.js:69 already passes conversation)
dashboard/components/SimpleInput.js:
- Accept `conversation` prop and derive `userHistory` via useMemo,
filtering for role === 'user' messages and mapping to content
- Add historyIndexRef (-1 = not browsing) and draftRef (preserves
in-progress text when entering history mode)
- ArrowUp: intercepts only when cursor at position 0 and autocomplete
closed, walks backward through history (newest to oldest)
- ArrowDown: only when already browsing history, walks forward;
past newest entry restores saved draft and exits history mode
- Bounds clamp on ArrowUp prevents undefined array access if
userHistory shrinks between navigations (SSE update edge case)
- Reset historyIndexRef on submit (line 110) and manual input (line 141)
- Textarea height recalculated after setting history text via
setTimeout to run after Preact commits the state update
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
plans/PLAN-tool-result-display.md:
- Add comprehensive plan for displaying tool results inline in
conversation view, including truncation strategies and expand/collapse
UI patterns
plans/subagent-visibility.md:
- Mark completed phases and update remaining work items
- Reflects current state of subagent tracking implementation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create authoritative documentation suite for Claude Code JSONL session
log processing, synthesized from codebase analysis, official Anthropic
documentation, and community tooling research.
Documentation structure (docs/claude-jsonl-reference/):
01-format-specification.md (214 lines):
- Complete message envelope structure with all fields
- Content block types (text, thinking, tool_use, tool_result)
- Usage object for token reporting
- Model identifiers and version history
- Conversation DAG structure via parentUuid
02-message-types.md (346 lines):
- Every message type with concrete JSON examples
- User messages (string content vs array for tool results)
- Assistant messages with all content block variants
- Progress events (hooks, bash, MCP)
- System, summary, and file-history-snapshot types
- Codex format differences (response_item, function_call)
03-tool-lifecycle.md (341 lines):
- Complete tool invocation to result flow
- Hook input/output formats (PreToolUse, PostToolUse)
- Parallel tool call handling
- Tool-to-result pairing algorithm
- Missing result edge cases
- Codex tool format differences
04-subagent-teams.md (363 lines):
- Task tool invocation and input fields
- Subagent transcript locations and format
- Team coordination (TeamCreate, SendMessage)
- Hook events (SubagentStart, SubagentStop)
- AMC spawn tracking with pending spawn registry
- Worktree isolation for subagents
05-edge-cases.md (475 lines):
- Parsing edge cases (invalid JSON, type ambiguity)
- Type coercion gotchas (bool vs int in Python)
- Session state edge cases (orphans, dead detection)
- Tool call edge cases (missing results, parallel ordering)
- Codex-specific quirks (content injection, buffering)
- File system safety (path traversal, permissions)
- Cache invalidation strategies
06-quick-reference.md (238 lines):
- File locations cheat sheet
- jq recipes for common queries
- Python parsing snippets
- Common gotchas table
- Useful constants
- Debugging commands
Also adds CLAUDE.md at project root linking to documentation and
providing project overview for agents working on AMC.
Sources include Claude Code hooks.md, headless.md, Anthropic Messages
API reference, and community tools (claude-code-log, claude-JSONL-browser).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove components that are no longer part of the dashboard architecture:
Header.js:
- Original header component replaced by integrated spawn controls
- SpawnModal now handles project selection and agent spawning
- Sidebar provides navigation context previously in header
SessionGroup.js:
- Session grouping now handled directly in App.js
- Simplified rendering without intermediate wrapper component
- groupSessionsByProject utility provides the grouping logic
These components were bypassed during the spawn feature implementation
and maintaining them increases cognitive overhead without benefit.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Connect the skills enumeration API to session card input fields for
slash command autocomplete:
App.js:
- Add skillsConfig state for Claude and Codex skill configs
- Fetch skills for both agent types on mount using Promise.all
- Pass agent-appropriate autocompleteConfig to each SessionCard
SessionCard.js:
- Accept autocompleteConfig prop and forward to SimpleInput
- Move context usage display from header to footer status bar for
better information hierarchy (activity indicator + context together)
SimpleInput.js:
- Fix autocomplete dropdown padding (py-2 -> py-1.5)
- Fix font inheritance (add font-mono to skill name)
- Fix description tooltip whitespace handling (add font-sans,
whitespace-normal)
SpawnModal.js:
- Add SPAWN_TIMEOUT_MS (2x default) to handle pending spawn registry
wait time plus session file confirmation polling
AgentActivityIndicator.js:
- Minor styling refinement for status display
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive test coverage for skill name resolution priority:
- test_frontmatter_name_overrides_dir_name: Verifies that the 'name'
field in YAML frontmatter takes precedence over directory name,
enabling namespaced skills like "doodle:bug-hunter"
- test_name_preserved_across_fallback_files: Confirms that when SKILL.md
provides a name but README.md provides the description, the name from
SKILL.md is preserved (important for multi-file skill definitions)
- test_no_frontmatter_name_uses_dir_name: Validates fallback behavior
when no name is specified in frontmatter
This ensures the skill autocomplete system correctly handles both
simple directory-named skills and explicitly namespaced skills from
skill packs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend spawn test coverage with verification of Codex session correlation
mechanisms:
Pending spawn registry tests:
- _write_pending_spawn creates JSON file with correct spawn_id,
project_path, agent_type, and timestamp
- _cleanup_stale_pending_spawns removes files older than PENDING_SPAWN_TTL
while preserving fresh files
Session timestamp parsing tests:
- Handles ISO 8601 with Z suffix (Zulu time)
- Handles ISO 8601 with explicit +00:00 offset
- Returns None for invalid format strings
- Returns None for empty string input
These tests ensure spawn_id correlation works correctly for Codex
sessions, which don't have direct environment variable injection like
Claude sessions do.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Handle edge case where Claude's --resume creates an orphan session file
before resuming the original session, leaving two session files pointing
to the same Zellij pane.
The deduplication algorithm (_dedupe_same_pane_sessions) resolves
conflicts by preferring:
1. Sessions with context_usage (indicates actual conversation occurred)
2. Higher conversation_mtime_ns (more recent file activity)
When an orphan is identified, its session file is deleted from disk to
prevent re-discovery on subsequent state collection cycles.
Test coverage includes:
- Keeping session with context_usage over one without
- Keeping higher mtime when both have context_usage
- Keeping higher mtime when neither has context_usage
- Preserving sessions on different panes (no false positives)
- Single session per pane unchanged
- Sessions without pane info unchanged
- Handling non-numeric mtime values defensively
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Split the monolithic context.py (117 lines) into five purpose-specific
modules following single-responsibility principle:
- config.py: Server-level constants (DATA_DIR, SESSIONS_DIR, PORT,
STALE_EVENT_AGE, _state_lock)
- agents.py: Agent-specific paths and caches (CLAUDE_PROJECTS_DIR,
CODEX_SESSIONS_DIR, discovery caches)
- auth.py: Authentication token generation/validation for spawn endpoint
- spawn_config.py: Spawn feature configuration (PENDING_SPAWNS_DIR,
rate limiting, projects watcher thread)
- zellij.py: Zellij binary resolution and session management constants
This refactoring improves:
- Code navigation: Find relevant constants by domain, not alphabetically
- Testing: Each module can be tested in isolation
- Import clarity: Mixins import only what they need
- Future maintenance: Changes to one domain don't risk breaking others
All mixins updated to import from new module locations. Tests updated
to use new import paths.
Includes PROPOSED_CODE_FILE_REORGANIZATION_PLAN.md documenting the
rationale and mapping from old to new locations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Zellij outputs colored text with ANSI escape codes, which caused
session name parsing to fail. Now strips escape codes before parsing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Position the spawn modal directly under the 'New Agent' button without a
blur overlay. Uses click-outside dismissal and absolute positioning.
Reduces visual disruption for quick agent spawning.
- Add empty-state message in SpawnModal when no projects found
- Spawn button stays disabled when projects list is empty
- Server already handled OSError/missing dir gracefully (returns [])
- Add tests: missing directory, only-hidden-dirs, empty API responses
Closes: bd-3c7
- Import and call load_projects_cache() to populate cache before requests
- Import and call generate_auth_token() to create one-time auth token
- Import and call start_projects_watcher() for background cache refresh
- Inject auth token into dashboard HTML via placeholder replacement
- Add AMC_AUTH_TOKEN placeholder in index.html head
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
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
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.
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.
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>