- ProjectScanner.ts, cliEnv.ts: fix simple-import-sort/imports order - CLAUDE.md: update tech stack to Electron 40.x, React 19.x
12 KiB
Claude Agent Teams UI
A new approach to task management with AI agent teams. Assemble agent teams with different roles that work autonomously in parallel, communicate with each other, create and manage their own tasks, review code, and collaborate across teams. You manage everything through a kanban board — like a CTO with an AI engineering team.
Key capabilities:
- Agent Teams — create teams with roles, agents work autonomously in parallel
- Cross-team communication — agents message each other within and across teams
- Kanban board — tasks change status in real-time as agents work
- Code review — diff view per task (accept/reject/comment), similar to Cursor
- Solo mode — single agent with self-managed tasks, expandable to full team
- Live process section — see running agents, open URLs in browser
- Direct messaging — send messages to any agent, comment on tasks, add quick actions on kanban cards
- Deep session analysis — bash commands, reasoning, subprocesses breakdown
- Context monitoring — token usage by category (CLAUDE.md, tool outputs, thinking, team coordination)
- Built-in code editor — edit files with Git support without leaving the app
- MCP integration — built-in mcp-server for external tools and agent plugins
- Post-compact context recovery — restores team-management instructions after context compaction
- Notification system — alerts on task completion, agent attention needed, errors
- Zero-setup onboarding — built-in Claude Code installation and authentication
100% free, open source. No API keys. No configuration. Runs entirely locally.
Tech Stack
Electron 40.x, React 19.x, TypeScript 5.x, Tailwind CSS 3.x, Zustand 4.x
Commands
Always use pnpm (not npm/yarn) for this project.
Do NOT run pnpm lint:fix unless the user explicitly asks for it — it interferes with agents running in parallel.
When running build/typecheck/test commands, pipe through tail -20 to avoid flooding the context window (e.g. pnpm typecheck 2>&1 | tail -20).
pnpm install- Install dependenciespnpm dev- Dev server with hot reloadpnpm build- Production buildpnpm typecheck- Type checkingpnpm lint:fix- Lint and auto-fixpnpm format- Format codepnpm test- Run all vitest testspnpm test:watch- Watch modepnpm test:coverage- Coverage reportpnpm test:coverage:critical- Critical path coveragepnpm test:chunks- Chunk building testspnpm test:semantic- Semantic step extraction testspnpm test:noise- Noise filtering testspnpm test:task-filtering- Task tool filtering testspnpm check- Full quality gate (types + lint + test + build)pnpm fix- Lint fix + formatpnpm quality- Full check + format check + knip
Git commits
Use normal, human-readable messages. Do not add tool-attribution trailers (for example Made-with: …) to commit messages.
Path Aliases
Use path aliases for imports:
@main/*→src/main/*@renderer/*→src/renderer/*@shared/*→src/shared/*@preload/*→src/preload/*
Data Sources
~/.claude/projects/{encoded-path}/*.jsonl - Session files ~/.claude/todos/{sessionId}.json - Todo data
Path encoding: /Users/name/project → -Users-name-project
Critical Concepts
isMeta Flag
isMeta: false= Real user message (creates new chunks)isMeta: true= Internal message (tool results, system-generated)
Chunk Structure
Independent chunk types for timeline visualization:
- UserChunk: Single user message with metrics
- AIChunk: All assistant responses with tool executions and spawned subagents
- SystemChunk: Command output/system messages
- CompactChunk: System metadata/structural messages
Each chunk has: timestamp, duration, metrics (tokens, cost, tools)
Task/Subagent Filtering
Task tool_use blocks are filtered when subagent exists Keep orphaned Task calls (no matching subagent) for visibility.
Agent Teams
Claude Code's "Orchestrate Teams" feature: multiple sessions coordinate as a team. Official docs: https://code.claude.com/docs/en/agent-teams
Message Delivery Architecture
- Lead reads ONLY stdin (stream-json). Messages to lead must go through
relayLeadInboxMessages()which converts inbox entries to stdin. - Teammates are independent CLI processes. Claude Code runtime monitors each teammate's inbox file and delivers messages between turns. No relay through lead needed.
- User → Teammate DM: UI writes to
inboxes/{member}.jsonwithfrom: "user". Teammate reads it directly. - Teammate → User response: Teammate writes to
inboxes/user.json. UI reads all inbox files includinguser.jsonviaTeamInboxReader. relayMemberInboxMessagesis DISABLED for teammate DMs (commented out inteams.tsandindex.ts). It caused bugs: lead responding instead of teammate, duplicate messages, relay loops. Code preserved but not called.relayLeadInboxMessagesis ACTIVE — lead needs it because lead reads stdin, not inbox files.- Messages in
user.jsonmay lackmessageId—TeamInboxReadergenerates deterministic IDs via sha256(from+timestamp+text). - See
docs/team-management/research-messaging.mdfor full architecture details.
Team Protocol Details
- Process.team?
{ teamName, memberName, memberColor }— enriched by SubagentResolver from Task call inputs andteammate_spawnedtool results - Teammate messages arrive as
<teammate-message teammate_id="..." color="..." summary="...">content</teammate-message>in user messages (isMeta: false). Detected byisParsedTeammateMessage()— excluded from UserChunks, rendered asTeammateMessageItemcards - Session ongoing detection treats
SendMessageshutdown_response (approve: true) and its tool_result as ending events, not ongoing activity - Display summary counts distinct teammates (by name) separately from regular subagents
- Team tools: TeamCreate, TaskCreate, TaskUpdate, TaskList, TaskGet, SendMessage, TeamDelete — have readable summaries in
toolSummaryHelpers.ts
Structured Task References
- TaskRef:
{ taskId, displayId, teamName }— shared typed reference used to persist task mentions across UI and storage - Persisted optional fields:
InboxMessage.taskRefs,TaskComment.taskRefs,TeamTask.descriptionTaskRefs,TeamTask.promptTaskRefs - Request surfaces:
SendMessageRequest.taskRefs,AddTaskCommentRequest.taskRefs,CreateTaskRequest.descriptionTaskRefs,CreateTaskRequest.promptTaskRefs,UpdateKanbanPatchrequest_changes.taskRefs - Renderer flow: task-aware inputs use
useTaskSuggestions()withtaskReferenceUtils.tsto extract refs from text; encoded zero-width metadata preserves exact task identity while keeping visible text readable - Main/IPC flow:
src/main/ipc/teams.tsandsrc/main/ipc/crossTeam.tsvalidate structured refs beforeTeamDataService, inbox stores, task stores, and readers persist/rehydrate them - Rendering/navigation:
linkifyTaskIdsInMarkdown()andparseTaskLinkHref()turn persisted refs into stabletask://links across messages, comments, task descriptions, and activity items
Visible Context Tracking
Tracks what consumes tokens in Claude's context window across 6 categories (discriminated union on category field):
| Category | Type | Source |
|---|---|---|
claude-md |
ClaudeMdContextInjection |
CLAUDE.md files (global, project, directory) |
mentioned-file |
MentionedFileInjection |
User @-mentioned files |
tool-output |
ToolOutputInjection |
Tool execution results (Read, Bash, etc.) |
thinking-text |
ThinkingTextInjection |
Extended thinking + text output tokens |
team-coordination |
TeamCoordinationInjection |
Team tools (SendMessage, TaskCreate, etc.) |
user-message |
UserMessageInjection |
User prompt text per turn |
- Types:
src/renderer/types/contextInjection.ts—ContextInjectionunion,ContextStats,TokensByCategory - Tracker:
src/renderer/utils/contextTracker.ts—computeContextStats(),processSessionContextWithPhases() - Context Phases: Compaction events reset accumulated injections, tracked via
ContextPhaseInfo - Display surfaces:
ContextBadge(per-turn popover),TokenUsageDisplay(hover breakdown),SessionContextPanel(full panel)
Error Handling
- Main: try/catch, console.error, return safe defaults
- Renderer: error state in Zustand store
- IPC: parameter validation, graceful degradation
Performance
- LRU Cache: Avoid re-parsing large JSONL files
- Streaming JSONL: Line-by-line processing
- Virtual Scrolling: For large session/message lists
- Debounced File Watching: 100ms debounce
Troubleshooting
Build Issues
rm -rf dist dist-electron node_modules
pnpm install
pnpm build
Type Errors
pnpm typecheck
Test Failures
Check for changes in message parsing or chunk building logic.
Packaged app: CLI / “Not logged in”
Each successful run of CliInstallerService.getStatus() tries to append one NDJSON line to claude-cli-auth-diag.ndjson (field diagFile: full path). Typical location: Electron app.getPath('logs') — on macOS often ~/Library/Logs/<product name>/ (exact folder is OS- and build-specific). If the file exceeds 512 KiB, it is truncated to empty before the next append (avoids unbounded growth). No line is written if the app is not under Electron, log dir cannot be resolved, or disk write fails. IPC (cliInstaller:getStatus) dedupes work for 5s (STATUS_CACHE_TTL_MS in src/main/ipc/cliInstaller.ts), so rapid UI polls do not each trigger a new file append. Default logger hides info/warn in production; logger.error still goes to the console (e.g. if assembling the diag line throws — should be rare).
TypeScript Conventions
Naming
| Category | Convention | Example |
|---|---|---|
| Services/Components | PascalCase | ProjectScanner.ts |
| Utilities | camelCase | pathDecoder.ts |
| Constants | UPPER_SNAKE_CASE | PARALLEL_WINDOW_MS |
| Type Guards | isXxx | isParsedRealUserMessage() |
| Builders | buildXxx | buildChunks() |
| Getters | getXxx | getResponses() |
Type Guards
// Message type guards (src/main/types/messages.ts)
isParsedRealUserMessage(msg) // isMeta: false, string content
isParsedInternalUserMessage(msg) // isMeta: true, array content
isAssistantMessage(msg) // type: "assistant"
// Chunk type guards
isUserChunk(chunk) // type: "user"
isAIChunk(chunk) // type: "ai"
isSystemChunk(chunk) // type: "system"
isCompactChunk(chunk) // type: "compact"
// Context injection type guards (component-scoped in ContextBadge.tsx, not exported)
isClaudeMdInjection(inj) // category: "claude-md"
isMentionedFileInjection(inj) // category: "mentioned-file"
isToolOutputInjection(inj) // category: "tool-output"
isThinkingTextInjection(inj) // category: "thinking-text"
isTeamCoordinationInjection(inj) // category: "team-coordination"
isUserMessageInjection(inj) // category: "user-message"
Barrel Exports
src/main/services/ and its domain subdirectories have barrel exports via index.ts:
// Preferred
import { ChunkBuilder, ProjectScanner } from './services';
// Also valid
import { ChunkBuilder } from './services/analysis';
Note: renderer utils/hooks/types do NOT have barrel exports — import directly from files.
Import Order
- External packages
- Path aliases (@main, @renderer, @shared)
- Relative imports
Storage And Persistence
- New persistence flows should depend on small repository/storage abstractions, not directly on
localStorage,IndexedDB, Electron APIs, or JSON files from UI components/hooks. - Keep persistence concerns split by responsibility: schema/normalization, repository interface, concrete storage implementation, and UI adapter logic should live in separate modules.
- Prefer designs where the high-level feature code can swap local browser/Electron storage for a server-backed implementation without rewriting the rendering layer.
- Reuse generic persistence/layout infrastructure when adding new draggable/resizable surfaces instead of copying feature-specific storage code.