CLAUDE.md
CLAUDE.mdCLAUDE.mdroot
Quality
81/100
Scores the file, not the repository.Length
5,163 words
72 headings · 39 code blocksRepository
738
— · pushed 0 days agoLast changed
3 days ago
First indexed 3 days ago.1# CLAUDE.md23This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.45## Project Overview67**Claude Code Dashboard** - A browser-based terminal dashboard for managing multiple Claude Code agents running in tmux on macOS. The application auto-discovers agents from tmux sessions and provides a unified web interface with real-time terminal streaming.89**Current Phase:** Phase 1 - Local-only, auto-discovery, no authentication10**Tech Stack:** Next.js 14 (App Router), React 18, xterm.js, WebSocket, node-pty, Tailwind CSS, lucide-react11**Platform:** macOS 12.0+, Node.js 18.17+/20.x, tmux 3.0+12**Branding:** Space Grotesk font, titled "AI Maestro"13**Port:** Application runs on port 23000 (http://localhost:23000)1415## Development Commands1617```bash18# Development19yarn install # Install all dependencies20yarn dev # Start dev server with hot reload (http://localhost:23000)2122# Production23yarn build # Build optimized production bundle24yarn start # Start production server (http://localhost:23000)25pm2 restart ai-maestro # Restart production server via PM22627# Testing28yarn test # Run unit tests (vitest)29yarn test:watch # Run tests in watch mode3031# Testing tmux sessions (for development)32tmux new-session -s test-session # Create test session33tmux list-sessions # List all sessions (what the app discovers)34tmux kill-session -t test-session # Clean up test session35```3637**Port Configuration:** The application is configured to run on port 23000. This is set in the PM2 configuration.3839**Health Check:** Do NOT use `/api/health` to check if the site is live (it doesn't exist). Use `/api/sessions` instead - it returns the list of agents and confirms the server is running.4041## Version Management4243**IMPORTANT:** When bumping the version, ALWAYS use the centralized script:4445```bash46./scripts/bump-version.sh patch # 0.17.12 -> 0.17.1347./scripts/bump-version.sh minor # 0.17.12 -> 0.18.048./scripts/bump-version.sh major # 0.17.12 -> 1.0.049./scripts/bump-version.sh 1.0.0 # Set specific version50```5152This script updates ALL version references across the codebase:53- `version.json` (source of truth)54- `package.json`55- `scripts/remote-install.sh`56- `README.md` (badge)57- `docs/index.html` (schema + display)58- `docs/ai-index.html`59- `docs/BACKLOG.md`6061**DO NOT manually edit version numbers in individual files.** Always use the script to ensure consistency.6263**CLI Script Versioning:** The `aimaestro-agent.sh` CLI tool uses an independent semver (`v1.x.x`) separate from the app version (`0.24.x`). The CLI is distributed via the plugin repo and has its own release cadence.6465## Pre-PR Checklist (MANDATORY)6667**⚠️ STOP! Before creating ANY Pull Request to main, complete this checklist:**6869```70□ 1. TESTS PASS: yarn test71□ 2. BUMP VERSION: ./scripts/bump-version.sh patch72□ 3. BUILD PASSES: yarn build73□ 4. COMMIT version bump with your changes74```7576**This is NON-NEGOTIABLE.** Every PR to main MUST include a version bump. No exceptions.7778---7980## Release & Marketing Workflow8182### Pull Request Protocol8384**IMPORTANT:** Every time you create a Pull Request to main, also draft an X (Twitter) post to announce the release.8586**PR Creation Checklist:**871. ✅ **VERSION BUMPED** (see Pre-PR Checklist above - this should already be done)882. Create PR with comprehensive description (summary, features, bug fixes, breaking changes)893. Draft X post highlighting key features and improvements904. Include release notes or link to PR in the post915. Use relevant hashtags: #AIcoding #DevTools #OpenSource926. Consider adding screenshots/GIFs for visual features937. Post during peak hours (9-11am or 1-3pm EST)9495**X Post Template:**96```97[Emoji] Shipping [Feature Name] today!9899Key improvements:100• [Feature 1]101• [Feature 2]102• [Feature 3]103104[Call to action - Star/Try/Share]105[Link to PR or GitHub]106107#AIcoding #DevTools108```109110**Examples:**111- Major release: "Shipping AI Maestro v0.3.3! 🚀"112- Feature addition: "New feature: SSH configuration for tmux 🔐"113- Bug fixes: "Squashed bugs and improved stability 🐛"114115Keep posts concise (<280 chars when possible), engaging, and focused on user benefits rather than technical implementation.116117### Marketing Content Location118119**IMPORTANT:** All marketing content files MUST be created in the `marketing/` folder:120121```122marketing/123 medium-article.md # Blog posts for Medium124 linkedin-post.md # LinkedIn content125 x-post.md # X/Twitter posts126 findings.md # Research notes (planning skill)127 task_plan.md # Task tracking (planning skill)128 progress.md # Progress logs (planning skill)129```130131- The `marketing/` folder is gitignored - content is deleted after publishing132- Never create these files in the project root133- When using the planning skill for marketing tasks, set the output directory to `marketing/`134135## Architecture: Critical Design Patterns136137### 1. Custom Server Architecture (server.mjs)138139**Why it exists:** Next.js alone doesn't support WebSocket on the same port as HTTP. The custom server combines both.140141```142HTTP Requests → Next.js handlers (API routes, pages)143WebSocket Upgrades → Custom WS server (terminal streaming)144Both on port 3000145```146147**Key constraint:** The server must handle:148- HTTP/HTTPS for Next.js (pages, API routes)149- WebSocket upgrade requests for `/term?name=<sessionName>`150- Session discovery via `tmux ls` command execution151152When modifying `server.mjs`:153- Preserve the upgrade handler that intercepts WebSocket requests154- Maintain the session pooling logic (multiple clients → one PTY)155- Never block the event loop during PTY operations156157### 2. Agent-First Architecture (CRITICAL)158159**AGENTS ARE THE CORE ENTITY.** Sessions are optional properties of agents.160161```162Agent (core entity)163├── id (UUID)164├── name (agent identity, used as session name)165├── label (optional display override)166├── workingDirectory (stored property, NOT derived from tmux)167├── sessions[] (array of AgentSession, typically 0 or 1)168│ ├── index (0 for primary session)169│ ├── status ('online' | 'offline')170│ └── workingDirectory (optional override)171└── preferences.defaultWorkingDirectory172```173174**Key principles:**1751. **Agents can exist without sessions** - An agent for querying repos/documents doesn't need a tmux session1762. **workingDirectory is STORED on the agent** - Set when agent is created or session is linked1773. **NEVER query tmux to derive agent properties** - All agent data comes from the registry1784. **Sessions are discovered and LINKED to existing agents** - Not the other way around179180**Two agent systems:**181- **`lib/agent-registry.ts`** - File-based registry (`~/.aimaestro/agents/registry.json`) with full agent metadata182- **`lib/agent.ts`** - In-memory Agent class for runtime (database, subconscious)183184When you need agent metadata (workingDirectory, etc.), use the file-based registry:185```typescript186import { getAgent, getAgentBySession } from '@/lib/agent-registry'187const agent = getAgent(agentId) || getAgentBySession(sessionName)188const workingDir = agent?.workingDirectory || agent?.sessions?.[0]?.workingDirectory189```190191**DO NOT:**192- Query tmux to get working directories193- Derive agent properties from tmux session state194- Assume an agent always has a session195- Create runtime lookups for data that should be stored196197**Subconscious runs LOCAL to the agent:**198199The subconscious process runs on the **same machine where the agent lives**. This means it has direct access to:200- Local conversation files (`~/.claude/projects/`)201- The agent's CozoDB database (`~/.aimaestro/agents/<id>/`)202- The local file system (workingDirectory, repos, etc.)203204The subconscious does NOT need remote API calls to access agent data - everything is local. This is why `index-delta` can read `.jsonl` files directly from disk.205206**Subconscious timers (v0.18.10+):**207- `maintainMemory()` - Indexes conversations for semantic search (runs periodically)208- `triggerConsolidation()` - Long-term memory consolidation (runs periodically)209- `checkMessages()` - **DISABLED by default** (push notifications replace polling)210211Message polling was removed in favor of push notifications. When messages arrive, agents receive instant tmux notifications instead of waiting for the next poll cycle. To re-enable polling (not recommended), set `messagePollingEnabled: true` in the subconscious config.212213### 3. Session Discovery Pattern214215Sessions are discovered from tmux and LINKED to agents:216217```218/api/sessions → Execute `tmux ls` → Parse output → Link to registry agents → Return JSON219```220221**Implementation details:**222- Agents are ephemeral - they exist only while tmux is running223- No persistent state between dashboard restarts224- Agent metadata comes from tmux directly (creation time, working directory)225- The dashboard does NOT create or manage agents (Phase 1 limitation)226227When implementing agent-related features:228- Always assume agents can disappear between API calls229- Never cache agent data longer than 5-10 seconds230- Handle `tmux ls` returning empty results gracefully231- Session IDs must match tmux session names exactly (alphanumeric + hyphens/underscores only)232233### 3. WebSocket-PTY Bridge234235**Critical data flow:**236```237Browser (xterm.js)238 ↕ WebSocket messages (text/binary)239Server (node-pty)240 ↕ PTY (tmux attach-session -t <name>)241tmux session242 ↕ Claude Code CLI243```244245**Important constraints:**246- PTY instances are pooled: Multiple WebSocket clients can connect to the same tmux session247- PTY is created on first client connect, destroyed when last client disconnects248- Terminal resize events must be propagated: Browser → WebSocket → PTY → tmux249- Input/output is binary-safe (supports ANSI escape codes, Unicode, etc.)250251When working with terminal components:252- xterm.js handles rendering only - it doesn't know about tmux253- WebSocket is the only communication channel (no polling)254- PTY errors (session not found, tmux crashed) must close WebSocket gracefully255- Terminal dimensions (cols/rows) must sync on window resize256257### 4. Tab-Based Multi-Terminal Architecture258259**Critical architectural pattern (v0.3.0+):** All agents are mounted simultaneously as "virtual tabs" with CSS visibility toggling.260261**Why this architecture:**262- Eliminates complex agent-switching logic (was 85+ lines of race condition handling)263- Terminals initialize once on mount, never re-initialize on agent switch264- Instant agent switching (no unmount/remount cycle)265- Preserves terminal state, scrollback, and WebSocket connections266- Agent notes stay in memory (no localStorage reload on switch)267268**Implementation:**269```tsx270// app/page.tsx - All sessions rendered, toggle visibility271{sessions.map(session => {272 const isActive = session.id === activeSessionId273 return (274 <div275 key={session.id}276 className="absolute inset-0 flex flex-col"277 style={{278 visibility: isActive ? 'visible' : 'hidden',279 pointerEvents: isActive ? 'auto' : 'none',280 zIndex: isActive ? 10 : 0281 }}282 >283 <TerminalView session={session} />284 </div>285 )286})}287```288289**Why visibility:hidden instead of display:none:**290- `display: none` removes element from layout → getBoundingClientRect() returns 0 dimensions → terminal initializes with incorrect width291- `visibility: hidden` keeps element in layout → correct dimensions → proper terminal sizing292- `pointerEvents: none` prevents hidden tabs from capturing mouse events293- Text selection works immediately without agent switching294295**Terminal initialization pattern:**296```typescript297// components/TerminalView.tsx298useEffect(() => {299 // Initialize ONCE on mount, never cleanup until unmount300 const init = async () => {301 cleanup = await initializeTerminal(containerElement)302 setIsReady(true)303 }304 init()305306 return () => {307 if (cleanup) cleanup()308 }309}, []) // Empty deps = mount once, no session.id dependency310```311312**What was removed:**313- Agent change detection (currentSessionRef, sessionChanged checks)314- Race condition handling (initializingRef, duplicate initialization prevention)315- Stale initialization cleanup verification316- Notes/logging re-sync on agent change (loaded once on mount)317318### 5. React State Management Pattern319320**Deliberately minimal:** No Redux, Zustand, or complex state libraries.321322```323App State:324- Active agent ID (localStorage persistence, drives visibility toggle)325- Agent list (fetched from /api/sessions every 10s)326- WebSocket connection state (per agent, persistent)327328Component State:329- Terminal instance (xterm.js, created once per agent)330- Connection errors (transient, cleared on retry)331- Agent notes (loaded once, persist in component state)332```333334**Key hooks:**335- `useSessions()` - Fetches session list, auto-refreshes336- `useTerminal()` - Manages xterm.js lifecycle (init once, resize, dispose)337- `useWebSocket()` - Handles WebSocket connection, reconnection, message routing338- `useActiveSession()` - Tracks selected agent with localStorage339340When adding new state:341- Keep it in the nearest component that needs it342- Use Context only if 3+ components need the same state343- Never store terminal content in React state (xterm.js manages this)344- Consider if state needs to persist across agent switches (keep in component) vs. reload (use effect with session.id dependency)345346### 6. UI Enhancement Patterns347348**Hierarchical Agent Organization:**349350Agents are organized in a 3-level hierarchy based on their names:351```352fluidmind/agents/backend-architect → Level 1: "fluidmind"353 Level 2: "agents"354 Agent: "backend-architect"355```356357**Dynamic Color System:**358- Colors assigned via hash function (same category = same color)359- 8-color palette in `SessionList.tsx` (easily customizable)360- Supports localStorage overrides per category361- No hardcoded category names - works with ANY category362363```typescript364const getCategoryColor = (category: string) => {365 // Hash-based color assignment from COLOR_PALETTE366 const hash = category.split('').reduce((acc, char) =>367 char.charCodeAt(0) + ((acc << 5) - acc), 0)368 const colorIndex = Math.abs(hash) % COLOR_PALETTE.length369 return COLOR_PALETTE[colorIndex]370}371```372373**Icon System:**374- Uses lucide-react for consistent, accessible icons375- Default icon: `Layers` (can be customized per category)376- Icons for: folders, terminals, actions (edit, delete, create)377378**Agent Notes Feature:**379- Collapsible textarea below terminal for per-agent notes380- Auto-saves to localStorage (`session-notes-${sessionId}`)381- Collapse state persisted (`session-notes-collapsed-${sessionId}`)382- Full copy/paste/edit support383384**Agent Management:**385- Rename agents with validation (API call to backend)386- Delete agents with confirmation modal387- Create new agents with optional working directory388- All actions update UI optimistically with error handling389390**UI Best Practices:**391- Avoid nested buttons (causes React hydration errors)392- Use `<div>` with `cursor-pointer` for clickable containers393- Always use `e.stopPropagation()` for nested interactive elements394- Keep hover states smooth with `transition-all duration-200`395396### 7. Team Meeting Architecture (v0.20.19+)397398**State machine pattern:** Team meetings use a `useReducer` with a `TeamMeetingState` that tracks meeting phase (`idle` → `selecting` → `ringing` → `active`), selected agents, and UI state (sidebar mode, right panel, kanban open).399400**Task system:**401- Tasks stored per-team in `~/.aimaestro/teams/tasks-{teamId}.json`402- 5 statuses: `backlog` → `pending` → `in_progress` → `review` → `completed`403- Dependency chains: tasks can block other tasks, auto-unblock on completion404- `useTasks` hook polls every 5s for multi-tab sync405406**Kanban board:**407- Full-screen overlay (`fixed inset-0 z-40`) matching agent picker overlay pattern408- Native HTML5 drag-and-drop (same pattern as AgentList.tsx)409- `KanbanCard`: `draggable={!task.isBlocked}`, stores taskId in `dataTransfer`410- `KanbanColumn`: `onDragOver`/`onDrop` handlers update task status411- Escape key closes modals in priority order: detail view → quick-add → board412- Blocked tasks show lock icon, not draggable413414### 8. TypeScript Type System Organization415416**Strict separation by domain:**417418```419types/session.ts - Session metadata, status enums420types/terminal.ts - xterm.js configuration, dimensions421types/websocket.ts - Message protocol, connection states422```423424**WebSocket message protocol:**425```typescript426{ type: 'input', data: string } // User typed in terminal427{ type: 'output', data: string } // Terminal output from tmux428{ type: 'resize', cols: number, rows: number } // Terminal resized429{ type: 'ping' / 'pong' } // Heartbeat430{ type: 'error', error: string } // Protocol error431```432433All WebSocket messages are JSON. Raw terminal output (ANSI codes) is wrapped in `{ type: 'output', data: ... }`.434435## File Structure Conventions436437**DO NOT create these directories** (they don't exist yet in Phase 1):438- `tests/` - No test suite in Phase 1439- `server/` - Server logic lives in root `server.mjs`440- `public/` - No static assets currently needed441- `styles/` - Styles in `app/globals.css` + Tailwind only442443**Current structure:**444```445app/446 page.tsx - Main dashboard with footer (SessionList + TerminalView)447 layout.tsx - Root layout, Space Grotesk font, app title "AI Maestro"448 globals.css - Tailwind imports + terminal scrollbar styles449 api/sessions/route.ts - GET endpoint for tmux session discovery450451components/452 SessionList.tsx - Hierarchical sidebar with icons, colors, session management453 TerminalView.tsx - Terminal display with collapsible notes area454 [Other components] - Keep them small, single responsibility455 team-meeting/456 MeetingHeader.tsx - Meeting header with status, controls, kanban toggle457 MeetingSidebar.tsx - Agent list sidebar during meetings458 MeetingTerminalArea.tsx - Terminal grid for active meeting agents459 MeetingRightPanel.tsx - Right panel wrapper (tasks + chat tabs)460 MeetingChatPanel.tsx - Meeting chat using AMP messages461 TaskPanel.tsx - Task list panel with filtering and quick-add462 TaskCard.tsx - Task card with status, assignee, dependencies463 TaskCreateForm.tsx - Full task creation form with all fields464 TaskDetailView.tsx - Detailed task view with edit capabilities465 TaskKanbanBoard.tsx - Full-screen kanban overlay with 5 columns + drag-and-drop466 KanbanColumn.tsx - Single kanban column with drop zone467 KanbanCard.tsx - Compact draggable task card for kanban468 DependencyPicker.tsx - Dependency selection for task relationships469470hooks/471 useWebSocket.ts - WebSocket connection (reconnection, heartbeat)472 useTerminal.ts - xterm.js lifecycle (init, fit, dispose)473 useSessions.ts - Session list fetching + auto-refresh474 useTasks.ts - Task CRUD with tasksByStatus, optimistic updates, 5s polling475 useMeetingMessages.ts - Meeting chat messages via AMP with 7s polling476477lib/478 api.ts - Fetch wrappers for /api/sessions479 websocket.ts - WebSocket message creators480 terminal.ts - Terminal utility functions481 utils.ts - Shared utilities (date formatting, etc.)482483types/484 session.ts - Session metadata, status enums, hierarchical structure485 terminal.ts - xterm.js configuration, dimensions486 websocket.ts - Message protocol, connection states487488docs/489 images/ - Screenshots for README documentation490 REQUIREMENTS.md - Installation prerequisites491 OPERATIONS-GUIDE.md - Session management, troubleshooting492493plugin/ - Plugin submodule (git submodule from 23blocks-OS/ai-maestro-plugins)494 .claude-plugin/ - Marketplace manifest495 plugins/ai-maestro/ - The AI Maestro plugin496 scripts/ - All CLI scripts (AMP, graph, docs, memory, agent management)497 skills/ - All 6 Claude Code skills498 hooks/ - Session tracking hooks499 .claude-plugin/ - Plugin manifest500501scripts/502 generate-social-logos.js - Generate social media logos from SVG503 init-all-agents.mjs - Initialize memory for all agents504 register-agent-from-session.mjs - Register agent(s) from tmux session(s)505 setup-tmux.sh - Setup tmux configuration506507install-plugin.sh - Plugin installer (skills, scripts, CLI tools)508509server.mjs - Custom Next.js server (HTTP + WebSocket)510CLAUDE.md - This file - guidance for Claude Code511```512513## Agent Messaging Protocol (AMP)514515**Overview:** AI Maestro uses the Agent Messaging Protocol (AMP) for inter-agent communication. AMP is like email for AI agents - it works locally by default and can optionally federate with external providers.516517**Key Features:**518- **Local-first**: Works immediately without external dependencies519- **Cryptographic signing**: Ed25519 signatures for message authenticity520- **Federation**: Connect to external providers (CrabMail, etc.) for global messaging521- **Provider-agnostic**: Same CLI works with any AMP provider522523### Installation524525The AMP plugin is bundled in the plugin submodule at `plugin/plugins/ai-maestro/`.526527```bash528# Install AMP scripts and skills529./install-plugin.sh530531# Non-interactive installation532./install-plugin.sh -y533534# Migrate existing messages only535./install-plugin.sh --migrate536```537538**What gets installed:**539- AMP scripts (`amp-*.sh`) → `~/.local/bin/`540- AMP skill → `~/.claude/skills/agent-messaging/`541- Message storage → `~/.agent-messaging/`542543### Quick Start544545```bash546# 1. Initialize your agent identity (first time only)547amp-init.sh --auto548549# 2. Send a message550amp-send.sh alice "Hello" "How are you?"551552# 3. Check your inbox553amp-inbox.sh554555# 4. Read a message556amp-read.sh <message-id>557```558559### Architecture560561**Two Components:**5625631. **AMP Plugin (Client)** - Installed on each agent machine564 - Location: `plugin/plugins/ai-maestro/` (submodule)565 - Storage: `~/.agent-messaging/`566 - Commands: `amp-init`, `amp-send`, `amp-inbox`, `amp-read`, etc.567 - Handles: Key generation, message signing, local storage5685692. **AI Maestro (Provider)** - Server that routes messages570 - Endpoints: `/api/v1/register`, `/api/v1/route`, `/api/v1/messages/pending`571 - Handles: Message routing, relay queue, push notifications572 - Optional: Agents can use external providers (CrabMail) instead573574**Message Storage (Client-side):**575```576~/.agent-messaging/577├── config.json # Agent configuration578├── keys/579│ ├── private.pem # Ed25519 private key (never shared)580│ └── public.pem # Ed25519 public key581├── messages/582│ ├── inbox/ # Received messages583│ └── sent/ # Sent messages584└── registrations/ # External provider registrations585```586587### AMP CLI Commands588589| Command | Description |590|---------|-------------|591| `amp-init.sh --auto` | Initialize agent identity |592| `amp-status.sh` | Show agent status and registrations |593| `amp-inbox.sh` | Check inbox for messages |594| `amp-read.sh <id>` | Read a specific message |595| `amp-send.sh <to> <subject> <message>` | Send a message |596| `amp-reply.sh <id> <message>` | Reply to a message |597| `amp-delete.sh <id>` | Delete a message |598| `amp-register.sh --provider <url>` | Register with external provider |599| `amp-fetch.sh` | Fetch messages from external providers |600601### Address Formats602603**Local addresses** (work immediately):604- `alice` → `alice@default.local`605- `bob@myteam.local` → Local delivery606607**External addresses** (require registration):608- `alice@acme.crabmail.ai` → Via CrabMail provider609- `backend@company.otherprovider.com` → Via other provider610611### Provider API (v0.20.0+)612613AI Maestro can act as an AMP provider. Agents register with AI Maestro and it handles routing.614615**Endpoints:**616- `GET /api/v1/health` - Provider health status (no auth)617- `GET /api/v1/info` - Provider capabilities (no auth)618- `POST /api/v1/register` - Register agent, get API key619- `POST /api/v1/route` - Route a signed message620- `GET /api/v1/messages/pending` - Poll for offline messages621- `DELETE /api/v1/messages/pending?id=X` - Acknowledge message622623**Registration flow:**624```bash625# Agent registers with local AI Maestro626amp-register.sh --provider localhost:23000 --tenant myorg627# Returns API key, stores in ~/.agent-messaging/registrations/628```629630### Push Notifications631632When a message is routed to a local agent, AI Maestro sends a push notification via tmux:633634```635[MESSAGE] From: alice - Subject line - check your inbox636```637638**Configuration (environment variables):**639- `NOTIFICATIONS_ENABLED=false` - Disable push notifications640- `NOTIFICATION_FORMAT` - Customize notification format641642### Message Storage643644All messages are stored in AMP per-agent directories:645```646~/.agent-messaging/agents/<agentName>/messages/inbox/647~/.agent-messaging/agents/<agentName>/messages/sent/648```649650Per-agent directories are auto-created when agents first use AMP commands.651The old `~/.aimaestro/messages/` system is no longer used.652653### Claude Code Skill654655The AMP skill (`plugin/plugins/ai-maestro/skills/agent-messaging/SKILL.md`) provides natural language:656657```658"Check my messages" → amp-inbox.sh659"Send a message to backend-api about deployment" → amp-send.sh backend-api "Deployment" "..."660"Reply to the last message" → amp-reply.sh <id> "..."661```662663### Development Notes664665- **Submodule**: Plugin repo is at `plugin/` - update with `git submodule update --remote`666- **Protocol spec**: https://agentmessaging.org667- **Security**: Messages are signed with Ed25519; AI Maestro verifies signatures668- **Relay queue**: Offline agents get messages via polling (`/api/v1/messages/pending`)669670## Critical Implementation Details671672### Terminal Rendering Performance673674xterm.js uses **Canvas or WebGL** for rendering. The WebGL addon significantly improves performance for high-output scenarios (e.g., large file dumps).675676```typescript677// In useTerminal hook678try {679 const webglAddon = new WebglAddon()680 terminal.loadAddon(webglAddon)681} catch (e) {682 // Fallback to canvas if WebGL unavailable683}684```685686**Never** read terminal content via React state. Always use xterm.js APIs (`terminal.write()`, `terminal.onData()`).687688### Critical Terminal Configuration for PTY/tmux689690**IMPORTANT:** The following terminal settings are critical for proper Claude Code CLI behavior:6916921. **`convertEol: false`** - PTY and tmux handle line endings correctly. Setting this to `true` causes character duplication and incorrect line breaks because xterm.js will convert `\n` to `\r\n`, but the PTY has already handled this.6936942. **Alternate Screen Buffer Support** - Claude Code (like vim, less, etc.) uses tmux's alternate screen buffer. This means:695 - When Claude is active, it uses a separate screen that doesn't mix with your shell history696 - Scrollback must be captured from tmux's buffer, not just xterm.js's buffer697 - The `windowOptions: { setWinLines: true }` setting enables proper alternate buffer support6986993. **Scrollback Capture Strategy** - On initial connection, capture both normal and alternate screen content:700```bash701 # Try to capture full history (50000 lines)702 tmux capture-pane -t <session> -p -S -50000 -e -1703 # Fallback to visible content only704 tmux capture-pane -t <session> -p705```706707**Common Issues and Fixes:**708709- **Every character creates a new line**: `convertEol` was set to `true` - must be `false` for PTY connections710- **Can't scroll back during Claude session**: Claude Code uses alternate screen buffer - use Shift+PageUp/Down to scroll xterm.js buffer, or tmux copy mode (Ctrl-b [) to access tmux's scrollback711- **Lost history after switching agents**: History capture timeout was too short or tmux session not fully initialized - increased timeout to 150ms712713### WebSocket Reconnection Strategy714715```typescript716const reconnect = {717 maxAttempts: 5,718 backoff: [100, 500, 1000, 2000, 5000], // Exponential backoff719 strategy: 'exponential'720}721```722723After 5 failed reconnection attempts, show error to user. Do NOT retry indefinitely (would waste resources if tmux session truly ended).724725### Session Naming Constraints726727tmux session names are limited to: `^[a-zA-Z0-9_-]+$`728729**Enforce this** in any UI that creates sessions (Phase 2+). Invalid characters will cause `tmux attach` to fail silently.730731### Localhost-Only Security Model732733**Phase 1 security assumptions:**734- Application binds to `localhost` (127.0.0.1) ONLY735- No authentication required (OS-level user security)736- No CORS, no origin validation737- WebSocket connections accepted from any localhost origin738739**DO NOT implement:**740- User authentication (not needed for localhost)741- Agent-level permissions (all agents accessible to local user)742- HTTPS/TLS (overkill for localhost)743744These are deferred to Phase 2+ if remote access is needed.745746## Common Gotchas747748### 1. Terminal Not Fitting Container749750```typescript751// After terminal.open(container), ALWAYS call:752fitAddon.fit()753754// And on window resize:755window.addEventListener('resize', () => fitAddon.fit())756```757758Without this, terminal dimensions won't match the container, causing ugly scrollbars.759760### 2. Hidden Terminals Must Use visibility:hidden, NOT display:none761762**CRITICAL (v0.3.0+):** When hiding inactive terminal tabs, use `visibility: hidden` instead of `display: none`.763764```tsx765// ✅ CORRECT - Keeps element in layout766style={{767 visibility: isActive ? 'visible' : 'hidden',768 pointerEvents: isActive ? 'auto' : 'none',769 zIndex: isActive ? 10 : 0770}}771772// ❌ WRONG - Removes from layout773style={{774 display: isActive ? 'flex' : 'none'775}}776```777778**Why this matters:**779- `display: none` removes element from layout → `getBoundingClientRect()` returns width/height = 0780- Terminal initializes with 0 dimensions → gets minimum columns (2) instead of full width781- Hidden elements don't receive mouse events → selection/copy doesn't work782- Using `visibility: hidden` + `pointerEvents: none` keeps correct dimensions while preventing interaction783784### 3. WebSocket Lifecycle vs React Lifecycle785786```typescript787useEffect(() => {788 const ws = new WebSocket(url)789 // ... setup handlers ...790791 return () => {792 ws.close() // CRITICAL: Clean up on unmount793 }794}, []) // Empty deps with tab architecture - WebSocket persists across visibility changes795```796797**Tab-based architecture change (v0.3.0+):** WebSocket connections are no longer recreated on agent switch. They're created once on mount and persist until component unmounts (when agent is removed from the list).798799### 4. tmux Session Name Parsing800801`tmux list-sessions` output format:802```803session-name: 1 windows (created Tue Jan 10 14:23:45 2025)804```805806Parsing must handle:807- Session names with hyphens/underscores808- Timestamps in various formats (locale-dependent)809- Multiple windows (number can be > 9)810811Use robust regex: `/^([a-zA-Z0-9_-]+):/`812813### 5. xterm.js Addon Loading Order814815```typescript816terminal.loadAddon(fitAddon) // 1. Load addons first817terminal.loadAddon(webLinksAddon)818terminal.open(container) // 2. Then open819fitAddon.fit() // 3. Then fit820```821822Wrong order causes crashes or non-functional addons.823824## Environment Variables825826All optional, with sensible defaults:827828```bash829PORT=3000 # Server port830NODE_ENV=development|production # Next.js environment831WS_RECONNECT_DELAY=3000 # WebSocket reconnect delay (ms)832WS_MAX_RECONNECT_ATTEMPTS=5 # Max reconnection attempts833TERMINAL_FONT_SIZE=14 # xterm.js font size834TERMINAL_SCROLLBACK=10000 # Terminal scrollback buffer835```836837Set via `.env.local` (gitignored). Never commit `.env.local`.838839## Server Modes840841AI Maestro supports two server modes controlled by the `MAESTRO_MODE` environment variable:842843### Full Mode (default)844```bash845yarn dev # Development with hot reload846yarn start # Production847```848- Uses Next.js for both UI pages and API routes849- All features available: dashboard, terminal WebSockets, API endpoints850- Startup: ~5s, Memory: ~300MB851852### Headless Mode853```bash854yarn headless # Development855yarn headless:prod # Production856```857- API-only mode — no Next.js, no UI pages858- All ~100 API endpoints served via standalone HTTP router (`services/headless-router.ts`)859- WebSocket connections (terminal, AMP, status, companion) work identically860- Uses `tsx` for TypeScript support (resolves `@/*` paths via tsconfig.json)861- Startup: ~1s, Memory: ~100MB862- Ideal for worker nodes that only need the API surface863864**Architecture:**865- `server.mjs` branches on `MAESTRO_MODE` at startup866- Full mode: `node server.mjs` → Next.js `app.prepare()` → `handle(req, res)`867- Headless mode: `tsx server.mjs` → `createHeadlessRouter()` → `router.handle(req, res)`868- All WebSocket servers, PTY handling, startup tasks, and graceful shutdown are shared between modes869- The `/api/internal/pty-sessions` endpoint is served directly from `server.mjs` in both modes870871## Testing the Application872873**Manual testing workflow:**8748751. Start the dashboard: `npm run dev`8762. Create test tmux sessions:877```bash878 tmux new-session -s test1 -d879 tmux send-keys -t test1 'claude' C-m880 tmux new-session -s test2 -d881 tmux send-keys -t test2 'claude' C-m882```8833. Verify auto-discovery: Sessions appear in sidebar8844. Click sessions: Terminal content loads8855. Type in terminal: Input reaches Claude8866. Kill session: `tmux kill-session -t test1`8877. Verify: Session removed after refresh888889### Unit Tests (CI — runs in GitHub Actions)890891Unit tests use vitest and run on every push/PR via `.github/workflows/ci.yml`:892893```bash894yarn test # Run all unit tests895yarn test:watch # Watch mode896```897898Tests cover: agent utilities, session service, agents-core service, AMP auth/address/canonical-json, task registry, team registry, document registry, container utils, meeting inject, content security. All service tests mock the runtime layer — no tmux/network required.899900### Integration Test Suites (Manual — requires running AI Maestro)901902These scripts test end-to-end behavior against a live AI Maestro instance with tmux:903904```bash905# AMP local routing tests (single host)906# Tests: health, registration, internal→internal, external polling, federation, acknowledgment907./scripts/test-amp-routing.sh908909# AMP cross-host mesh tests (multi-host via Tailscale)910# Tests: host health, agent registration on each host, cross-host delivery, replies, inbox counts911./scripts/test-amp-cross-host.sh # Auto-detect hosts from ~/.aimaestro/hosts.json912./scripts/test-amp-cross-host.sh --local-only # Only test local→remote913./scripts/test-amp-cross-host.sh --skip-inbox # Skip inbox verification914915# Companion call session fork tests916# Tests: __call session spawn, sidebar/agent hiding, transcript routing, disconnect cleanup, multi-client917./scripts/test-call-session.sh # Auto-picks first online agent918./scripts/test-call-session.sh <agent-id> # Specific agent919```920921**Prerequisites:** AI Maestro running on localhost:23000, jq installed, tmux installed. AMP tests also require AMP scripts (`./install-plugin.sh -y`).922923## Documentation References924925- **[README.md](./README.md)** - Project overview, quick start, architecture926- **[docs/REQUIREMENTS.md](./docs/REQUIREMENTS.md)** - Installation prerequisites927- **[docs/OPERATIONS-GUIDE.md](./docs/OPERATIONS-GUIDE.md)** - Agent management, troubleshooting928- **[docs/CEREBELLUM.md](./docs/CEREBELLUM.md)** - Cerebellum subsystem architecture, voice pipeline, TTS providers929930Refer to these when users ask about setup or usage.931932## Roadmap Context933934**Phase 1 (Current):** Auto-discovery, localhost-only, read-only agent interaction935**Phase 2 (Planned):** Agent creation from UI, grouping, search936**Phase 3 (Future):** Remote SSH sessions, authentication, collaboration937938When implementing features:939- Check if they belong in current phase940- Don't over-engineer for future phases941- Document phase boundaries clearly942943## What NOT to Do944945- **Don't query tmux to get agent properties** - workingDirectory, etc. are STORED on the agent in the registry, not derived from tmux. See "Agent-First Architecture" section.946- **Don't assume agents need sessions** - Agents are the core entity; sessions are optional. An agent can exist for querying repos/docs without a tmux session.947- **Don't use sessions.json** - Sessions are auto-discovered from tmux948- **Don't implement authentication** - Phase 1 is localhost-only949- **Don't store terminal history** - xterm.js manages scrollback in-memory950- **Don't use polling** - WebSocket only for terminal I/O951- **Don't support remote SSH** - Phase 3 feature, not Phase 1952- **Don't nest interactive elements** - Causes React hydration errors (use div with onClick instead)953- **Don't hardcode category colors** - Use the hash-based dynamic color system954- **Don't use display:none for hidden terminals** - Use visibility:hidden to maintain correct dimensions and enable selection (v0.3.0+)955- **Don't add session.id to terminal initialization useEffect** - Terminals initialize once with empty dependency array in tab architecture (v0.3.0+)956957## Key Files to Understand958959**Must read to understand the system:**9609611. `lib/agent-registry.ts` - **File-based agent registry** (stores agents in `~/.aimaestro/agents/registry.json`) - THE source of truth for agent metadata including workingDirectory9622. `lib/agent.ts` - **In-memory Agent class** for runtime operations (database, subconscious)9633. `server.mjs` - Custom server combining HTTP and WebSocket9644. `app/page.tsx` - Main UI composition with footer (SessionList + TerminalView)9655. `components/SessionList.tsx` - Hierarchical sidebar with dynamic colors, icons, agent management9666. `components/TerminalView.tsx` - Terminal display with collapsible notes feature9677. `hooks/useWebSocket.ts` - WebSocket connection management9688. `hooks/useTerminal.ts` - xterm.js lifecycle management9699. `app/api/sessions/route.ts` - tmux session discovery logic970971**Team Meeting & Kanban (v0.20.19+):**97210. `app/team-meeting/page.tsx` - Team meeting page with reducer state machine97311. `components/team-meeting/TaskKanbanBoard.tsx` - Full-screen kanban overlay with 5 columns + drag-and-drop97412. `components/team-meeting/KanbanColumn.tsx` - Single kanban column with drop zone97513. `components/team-meeting/KanbanCard.tsx` - Compact draggable task card97614. `types/task.ts` - Task types with 5 statuses: backlog, pending, in_progress, review, completed97715. `lib/task-registry.ts` - File-based CRUD for team task persistence97816. `hooks/useTasks.ts` - Task hook with tasksByStatus, optimistic updates, polling979980**Read these in order** to understand agents and data flow.981982**Key UI patterns:**983- Tab-based multi-terminal architecture (v0.3.0+) - all agents mounted, visibility toggling984- Dynamic color assignment (hash-based, no hardcoding)985- Hierarchical grouping (3-level: category/subcategory/agent)986- Agent notes (per-agent localStorage)987- Avoid nested buttons (use div with cursor-pointer)988- Use visibility:hidden for inactive tabs (not display:none)989
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Adit-Jain-srm/NightmareNetCLAUDE.md · 45 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 3 days ago | |
| bagisto/bagistoCLAUDE.md · 28k | CLAUDE.md | setupbuildteststyle+5 | 100/100 | 3 days ago | |
| microsoft/playwrightCLAUDE.md · 94k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 3 days ago | |
| dotCMS/corecore-web/CLAUDE.md · 949 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 3 days ago | |
| nimbalyst/nimbalystpackages/android/CLAUDE.md · 1.4k | CLAUDE.md | setupbuildstylearch+2 | 100/100 | 3 days ago | |
| filamentphp/filamentCLAUDE.md · 32k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 3 days ago | |
| dotCMS/coreCLAUDE.md · 949 | CLAUDE.md | setupbuildteststyle+7 | 99/100 | today | |
| lollipopkit/flutter_server_boxCLAUDE.md · 8.3k | CLAUDE.md | buildteststylearch+2 | 98/100 | 3 days ago |
