Back to Ruflo

ADR-027: Native TeammateTool Integration for Claude Flow

v3/implementation/adrs/ADR-027-teammate-tool-integration.md

3.6.3038.4 KB
Original Source

ADR-027: Native TeammateTool Integration for Claude Flow

Status: Implemented ✅ Date: 2026-01-25 Updated: 2026-01-25 Author: Claude Flow Architecture Team Version: 1.0.0 Requires: Claude Code >= 2.1.19


Implementation Summary

The @claude-flow/teammate-plugin package has been fully implemented with:

ComponentLinesFeatures
types.ts~50030+ interfaces, enums, constants
teammate-bridge.ts~100015 feature implementations
mcp-tools.ts~35016 MCP tools
index.ts~110Public exports
Total~1960-

Implemented Features (All 15 from Gap Analysis)

#FeatureStatusModule
1Core Bridgeteammate-bridge.ts
2Team ContextupdateTeamContext(), getTeamContext()
3Permission UpdatesupdateTeammatePermissions()
4Delegate ModedelegateToTeammate(), revokeDelegation()
5Session MemorysaveTeammateMemory(), loadTeammateMemory()
6Remote SyncpushToRemote(), pullFromRemote(), syncWithRemote()
7Transcript SharingshareTranscript()
8Error HandlingTeammateError, TeammateErrorCode (18 types)
9TeleportteleportTeam(), canTeleport()
10Plan ControlpausePlanExecution(), resumePlanExecution(), reenterPlanMode()
11Version DetectiongetVersionInfo(), MINIMUM_CLAUDE_CODE_VERSION
12Events20+ event types in TeammateBridgeEvents
13ConfigurationPluginConfig, DEFAULT_PLUGIN_CONFIG
14MCP Tools16 tools with handleMCPTool()
15TDD TestsVitest suite with mocking

Package Structure

v3/@claude-flow/teammate-plugin/
├── package.json           # npm package (requires Claude Code >= 2.1.19)
├── tsconfig.json          # TypeScript configuration
├── README.md              # Full documentation
├── src/
│   ├── index.ts           # Public API exports
│   ├── types.ts           # All TypeScript definitions
│   ├── teammate-bridge.ts # Core bridge implementation
│   └── mcp-tools.ts       # 16 MCP tools
└── tests/
    └── teammate-bridge.test.ts  # TDD test suite

Executive Summary

This ADR defines the architecture for deep integration between Claude Flow and Claude Code's native TeammateTool multi-agent orchestration system. By leveraging TeammateTool's built-in capabilities for team management, inter-agent communication, and plan approval workflows, Claude Flow can eliminate redundant coordination code and provide seamless native multi-agent experiences.


1. Context

1.1 Discovery

Analysis of Claude Code v2.1.19 binary revealed a comprehensive multi-agent orchestration system:

ComponentOccurrencesPurpose
TeammateTool34Core tool implementation
Teammate154Agent class references
spawnTeam22Team creation
broadcast42Inter-agent messaging
approvePlan/rejectPlan25Plan approval workflow
requestJoin/approveJoin25Team membership
teammate_mailbox3Message queue system

1.2 Native Capabilities

AgentInput Schema (v2.1.19):

typescript
interface AgentInput {
  description: string;
  prompt: string;
  subagent_type: string;
  model?: "sonnet" | "opus" | "haiku";
  resume?: string;
  run_in_background?: boolean;
  max_turns?: number;

  // NEW TEAMMATE FIELDS (v2.1.19+)
  allowed_tools?: string[];
  name?: string;
  team_name?: string;
  mode?: "acceptEdits" | "bypassPermissions" | "default" |
         "delegate" | "dontAsk" | "plan";
}

ExitPlanModeInput Schema:

typescript
interface ExitPlanModeInput {
  allowedPrompts?: { tool: "Bash"; prompt: string }[];
  pushToRemote?: boolean;
  remoteSessionId?: string;

  // NEW SWARM FIELDS (v2.1.19+)
  launchSwarm?: boolean;
  teammateCount?: number;
}

Environment Variables:

bash
CLAUDE_CODE_TEAM_NAME          # Team identifier
CLAUDE_CODE_TMUX_SESSION       # tmux session name
CLAUDE_CODE_TMUX_PREFIX        # tmux prefix key binding
CLAUDE_CODE_TEAMMATE_COMMAND   # Spawn command override

1.3 Problem Statement

Claude Flow currently implements its own multi-agent orchestration via:

  • MCP-based swarm coordination
  • Custom message bus implementation
  • Hierarchical/mesh topology management
  • Byzantine consensus protocols

This creates redundancy with Claude Code's native TeammateTool, which provides:

  • Native team spawn/join/discover mechanisms
  • Built-in mailbox-based messaging
  • Plan approval workflows
  • tmux/in-process spawn backends

2. Decision

Implement a Claude Flow plugin that acts as a bridge to TeammateTool, providing:

  1. Native Team Management - Use TeammateTool for spawning instead of MCP
  2. Mailbox Integration - Bridge teammate_mailbox to Claude Flow's memory system
  3. Plan Mode Orchestration - Leverage launchSwarm for coordinated execution
  4. Hybrid Topology - Combine Claude Flow's advanced topologies with native spawning

2.1 Architecture Principles

PrincipleImplementation
Native FirstUse TeammateTool when available, fallback to MCP
Zero DuplicationDelegate spawning to Claude Code entirely
Transparent BridgeClaude Flow APIs unchanged, backend swapped
Version AdaptiveDetect Claude Code version, enable features accordingly

3. Technical Specification

3.1 Plugin Structure

v3/@claude-flow/teammate-plugin/
├── src/
│   ├── index.ts                 # Plugin entry point
│   ├── teammate-bridge.ts       # Core TeammateTool bridge
│   ├── team-manager.ts          # Team lifecycle management
│   ├── mailbox-adapter.ts       # Mailbox ↔ Memory bridge
│   ├── spawn-backends/
│   │   ├── tmux-backend.ts      # tmux spawn backend
│   │   ├── in-process.ts        # In-process backend
│   │   └── backend-selector.ts  # Auto-select best backend
│   ├── plan-orchestrator.ts     # Plan mode swarm launcher
│   ├── approval-workflow.ts     # Plan approval handling
│   ├── discovery.ts             # Team discovery service
│   └── types.ts                 # TypeScript definitions
├── package.json
└── README.md

3.2 Core Interfaces

typescript
// types.ts

/**
 * TeammateTool operations mapped from Claude Code v2.1.19
 */
export type TeammateOperation =
  | 'spawnTeam'
  | 'discoverTeams'
  | 'requestJoin'
  | 'approveJoin'
  | 'rejectJoin'
  | 'write'           // Send message to teammate
  | 'broadcast'       // Broadcast to all teammates
  | 'requestShutdown'
  | 'approveShutdown'
  | 'rejectShutdown'
  | 'approvePlan'
  | 'rejectPlan'
  | 'cleanup';

/**
 * Team configuration
 */
export interface TeamConfig {
  name: string;
  topology: 'flat' | 'hierarchical' | 'mesh';
  maxTeammates: number;
  spawnBackend: 'tmux' | 'in_process' | 'auto';
  planModeRequired: boolean;
  autoApproveJoin: boolean;
  messageRetention: number;  // ms
}

/**
 * Teammate spawn configuration
 */
export interface TeammateSpawnConfig {
  name: string;
  role: string;
  prompt: string;
  model?: 'sonnet' | 'opus' | 'haiku';
  allowedTools?: string[];
  mode?: 'default' | 'plan' | 'delegate' | 'bypassPermissions';
  teamName?: string;
  runInBackground?: boolean;
}

/**
 * Team state
 */
export interface TeamState {
  name: string;
  createdAt: Date;
  teammates: TeammateInfo[];
  pendingJoinRequests: JoinRequest[];
  activePlans: PlanInfo[];
  messageCount: number;
  topology: string;
}

/**
 * Teammate info
 */
export interface TeammateInfo {
  id: string;
  name: string;
  role: string;
  status: 'active' | 'idle' | 'busy' | 'shutdown_pending';
  spawnedAt: Date;
  messagesSent: number;
  messagesReceived: number;
  currentTask?: string;
}

/**
 * Mailbox message
 */
export interface MailboxMessage {
  id: string;
  from: string;
  to: string | 'broadcast';
  timestamp: Date;
  type: 'task' | 'result' | 'status' | 'plan' | 'approval';
  payload: unknown;
  acknowledged: boolean;
}

/**
 * Plan for approval
 */
export interface TeamPlan {
  id: string;
  description: string;
  proposedBy: string;
  steps: PlanStep[];
  requiredApprovals: number;
  approvals: string[];
  rejections: string[];
  status: 'pending' | 'approved' | 'rejected' | 'executing';
}

export interface PlanStep {
  order: number;
  action: string;
  assignee?: string;
  tools: string[];
  estimatedDuration?: number;
}

3.3 TeammateBridge Implementation

typescript
// teammate-bridge.ts

import { EventEmitter } from 'events';
import { execSync, spawn } from 'child_process';
import type {
  TeamConfig,
  TeammateSpawnConfig,
  TeamState,
  TeammateInfo,
  MailboxMessage,
  TeamPlan,
  TeammateOperation,
} from './types.js';

/**
 * Bridge between Claude Flow and Claude Code's TeammateTool
 *
 * Provides unified API for multi-agent orchestration using
 * native TeammateTool capabilities when available.
 */
export class TeammateBridge extends EventEmitter {
  private claudeCodeVersion: string | null = null;
  private teammateToolAvailable: boolean = false;
  private activeTeams: Map<string, TeamState> = new Map();
  private mailboxPollers: Map<string, NodeJS.Timeout> = new Map();

  constructor(private config: Partial<TeamConfig> = {}) {
    super();
  }

  /**
   * Initialize the bridge
   * Detects Claude Code version and TeammateTool availability
   */
  async initialize(): Promise<void> {
    // Detect Claude Code version
    try {
      const version = execSync('claude --version', { encoding: 'utf-8' }).trim();
      const match = version.match(/(\d+\.\d+\.\d+)/);
      this.claudeCodeVersion = match?.[1] ?? null;

      // TeammateTool requires >= 2.1.19
      if (this.claudeCodeVersion) {
        const [major, minor, patch] = this.claudeCodeVersion.split('.').map(Number);
        this.teammateToolAvailable =
          major > 2 ||
          (major === 2 && minor > 1) ||
          (major === 2 && minor === 1 && patch >= 19);
      }
    } catch {
      this.claudeCodeVersion = null;
      this.teammateToolAvailable = false;
    }

    this.emit('initialized', {
      claudeCodeVersion: this.claudeCodeVersion,
      teammateToolAvailable: this.teammateToolAvailable,
    });

    if (!this.teammateToolAvailable) {
      console.warn(
        `[TeammateBridge] TeammateTool not available. ` +
        `Requires Claude Code >= 2.1.19, found: ${this.claudeCodeVersion ?? 'not installed'}`
      );
    }
  }

  /**
   * Check if TeammateTool is available
   */
  isAvailable(): boolean {
    return this.teammateToolAvailable;
  }

  /**
   * Get Claude Code version
   */
  getClaudeCodeVersion(): string | null {
    return this.claudeCodeVersion;
  }

  // ==================== TEAM MANAGEMENT ====================

  /**
   * Spawn a new team
   */
  async spawnTeam(config: TeamConfig): Promise<TeamState> {
    this.ensureAvailable();

    const teamState: TeamState = {
      name: config.name,
      createdAt: new Date(),
      teammates: [],
      pendingJoinRequests: [],
      activePlans: [],
      messageCount: 0,
      topology: config.topology,
    };

    // Set environment for team context
    process.env.CLAUDE_CODE_TEAM_NAME = config.name;

    if (config.planModeRequired) {
      process.env.CLAUDE_CODE_PLAN_MODE_REQUIRED = 'true';
    }

    this.activeTeams.set(config.name, teamState);

    // Start mailbox polling for this team
    this.startMailboxPoller(config.name);

    this.emit('team:spawned', { team: config.name, config });

    return teamState;
  }

  /**
   * Discover existing teams
   */
  async discoverTeams(): Promise<string[]> {
    this.ensureAvailable();

    // Teams are stored in ~/.claude/teams/
    const teamsDir = `${process.env.HOME}/.claude/teams`;

    try {
      const { readdirSync } = await import('fs');
      const teams = readdirSync(teamsDir, { withFileTypes: true })
        .filter(d => d.isDirectory())
        .map(d => d.name);

      return teams;
    } catch {
      return [];
    }
  }

  /**
   * Request to join an existing team
   */
  async requestJoin(teamName: string, agentInfo: TeammateInfo): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    team.pendingJoinRequests.push({
      agentId: agentInfo.id,
      agentName: agentInfo.name,
      requestedAt: new Date(),
      role: agentInfo.role,
    });

    this.emit('team:join_requested', { team: teamName, agent: agentInfo });

    // Auto-approve if configured
    if (this.config.autoApproveJoin) {
      await this.approveJoin(teamName, agentInfo.id);
    }
  }

  /**
   * Approve a join request
   */
  async approveJoin(teamName: string, agentId: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const requestIndex = team.pendingJoinRequests.findIndex(
      r => r.agentId === agentId
    );

    if (requestIndex === -1) {
      throw new Error(`No pending join request for agent: ${agentId}`);
    }

    const request = team.pendingJoinRequests.splice(requestIndex, 1)[0];

    team.teammates.push({
      id: agentId,
      name: request.agentName,
      role: request.role,
      status: 'active',
      spawnedAt: new Date(),
      messagesSent: 0,
      messagesReceived: 0,
    });

    this.emit('team:join_approved', { team: teamName, agent: agentId });
  }

  /**
   * Reject a join request
   */
  async rejectJoin(teamName: string, agentId: string, reason?: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    team.pendingJoinRequests = team.pendingJoinRequests.filter(
      r => r.agentId !== agentId
    );

    this.emit('team:join_rejected', { team: teamName, agent: agentId, reason });
  }

  // ==================== TEAMMATE SPAWNING ====================

  /**
   * Spawn a teammate using native AgentInput
   */
  async spawnTeammate(config: TeammateSpawnConfig): Promise<TeammateInfo> {
    this.ensureAvailable();

    // Build AgentInput for Claude Code's Task tool
    const agentInput = {
      description: `${config.role}: ${config.name}`,
      prompt: config.prompt,
      subagent_type: config.role,
      model: config.model,
      name: config.name,
      team_name: config.teamName ?? process.env.CLAUDE_CODE_TEAM_NAME,
      allowed_tools: config.allowedTools,
      mode: config.mode,
      run_in_background: config.runInBackground ?? true,
    };

    // The actual spawn happens through Claude Code's Task tool
    // This bridge prepares the configuration and tracks state
    const teammateId = `teammate-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;

    const teammateInfo: TeammateInfo = {
      id: teammateId,
      name: config.name,
      role: config.role,
      status: 'active',
      spawnedAt: new Date(),
      messagesSent: 0,
      messagesReceived: 0,
      currentTask: config.prompt.slice(0, 100),
    };

    // Add to team if team_name specified
    if (config.teamName) {
      const team = this.activeTeams.get(config.teamName);
      if (team) {
        team.teammates.push(teammateInfo);
      }
    }

    this.emit('teammate:spawned', {
      teammate: teammateInfo,
      agentInput,
    });

    return teammateInfo;
  }

  /**
   * Get spawn configuration for Claude Code Task tool
   * Returns the AgentInput object to pass to Task tool
   */
  buildAgentInput(config: TeammateSpawnConfig): Record<string, unknown> {
    return {
      description: `${config.role}: ${config.name}`,
      prompt: config.prompt,
      subagent_type: config.role,
      model: config.model,
      name: config.name,
      team_name: config.teamName ?? process.env.CLAUDE_CODE_TEAM_NAME,
      allowed_tools: config.allowedTools,
      mode: config.mode,
      run_in_background: config.runInBackground ?? true,
    };
  }

  // ==================== MESSAGING ====================

  /**
   * Send message to a specific teammate
   */
  async sendMessage(
    teamName: string,
    fromId: string,
    toId: string,
    message: Omit<MailboxMessage, 'id' | 'from' | 'to' | 'timestamp' | 'acknowledged'>
  ): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const fullMessage: MailboxMessage = {
      id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      from: fromId,
      to: toId,
      timestamp: new Date(),
      acknowledged: false,
      ...message,
    };

    // Write to mailbox file
    await this.writeToMailbox(teamName, toId, fullMessage);

    team.messageCount++;

    // Update sender stats
    const sender = team.teammates.find(t => t.id === fromId);
    if (sender) sender.messagesSent++;

    this.emit('message:sent', { team: teamName, message: fullMessage });
  }

  /**
   * Broadcast message to all teammates
   */
  async broadcast(
    teamName: string,
    fromId: string,
    message: Omit<MailboxMessage, 'id' | 'from' | 'to' | 'timestamp' | 'acknowledged'>
  ): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const fullMessage: MailboxMessage = {
      id: `broadcast-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      from: fromId,
      to: 'broadcast',
      timestamp: new Date(),
      acknowledged: false,
      ...message,
    };

    // Write to all teammate mailboxes
    for (const teammate of team.teammates) {
      if (teammate.id !== fromId) {
        await this.writeToMailbox(teamName, teammate.id, fullMessage);
        teammate.messagesReceived++;
      }
    }

    team.messageCount += team.teammates.length - 1;

    this.emit('message:broadcast', { team: teamName, message: fullMessage });
  }

  /**
   * Read messages from mailbox
   */
  async readMailbox(teamName: string, teammateId: string): Promise<MailboxMessage[]> {
    this.ensureAvailable();

    const mailboxPath = this.getMailboxPath(teamName, teammateId);

    try {
      const { readFileSync, writeFileSync } = await import('fs');
      const content = readFileSync(mailboxPath, 'utf-8');
      const messages: MailboxMessage[] = JSON.parse(content);

      // Mark as acknowledged and clear
      writeFileSync(mailboxPath, '[]', 'utf-8');

      return messages;
    } catch {
      return [];
    }
  }

  // ==================== PLAN APPROVAL ====================

  /**
   * Submit a plan for approval
   */
  async submitPlan(teamName: string, plan: Omit<TeamPlan, 'id' | 'approvals' | 'rejections' | 'status'>): Promise<TeamPlan> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const fullPlan: TeamPlan = {
      id: `plan-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      approvals: [],
      rejections: [],
      status: 'pending',
      ...plan,
    };

    team.activePlans.push(fullPlan);

    // Broadcast plan for approval
    await this.broadcast(teamName, plan.proposedBy, {
      type: 'plan',
      payload: fullPlan,
    });

    this.emit('plan:submitted', { team: teamName, plan: fullPlan });

    return fullPlan;
  }

  /**
   * Approve a plan
   */
  async approvePlan(teamName: string, planId: string, approverId: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const plan = team.activePlans.find(p => p.id === planId);
    if (!plan) {
      throw new Error(`Plan not found: ${planId}`);
    }

    if (!plan.approvals.includes(approverId)) {
      plan.approvals.push(approverId);
    }

    // Check if we have enough approvals
    if (plan.approvals.length >= plan.requiredApprovals) {
      plan.status = 'approved';
      this.emit('plan:approved', { team: teamName, plan });
    }

    this.emit('plan:approval_added', { team: teamName, planId, approverId });
  }

  /**
   * Reject a plan
   */
  async rejectPlan(teamName: string, planId: string, rejecterId: string, reason?: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const plan = team.activePlans.find(p => p.id === planId);
    if (!plan) {
      throw new Error(`Plan not found: ${planId}`);
    }

    plan.rejections.push(rejecterId);
    plan.status = 'rejected';

    this.emit('plan:rejected', { team: teamName, plan, rejecterId, reason });
  }

  /**
   * Launch swarm to execute approved plan
   * Uses Claude Code's native launchSwarm capability
   */
  async launchSwarm(teamName: string, planId: string, teammateCount?: number): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const plan = team.activePlans.find(p => p.id === planId);
    if (!plan || plan.status !== 'approved') {
      throw new Error(`Plan not approved: ${planId}`);
    }

    // Build ExitPlanModeInput for swarm launch
    const exitPlanInput = {
      launchSwarm: true,
      teammateCount: teammateCount ?? plan.steps.length,
      allowedPrompts: plan.steps.map(step => ({
        tool: 'Bash' as const,
        prompt: step.action,
      })),
    };

    plan.status = 'executing';

    this.emit('swarm:launched', {
      team: teamName,
      plan,
      exitPlanInput,
      teammateCount: exitPlanInput.teammateCount,
    });
  }

  // ==================== SHUTDOWN ====================

  /**
   * Request teammate shutdown
   */
  async requestShutdown(teamName: string, teammateId: string, reason?: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const teammate = team.teammates.find(t => t.id === teammateId);
    if (teammate) {
      teammate.status = 'shutdown_pending';
    }

    this.emit('teammate:shutdown_requested', { team: teamName, teammateId, reason });
  }

  /**
   * Approve teammate shutdown
   */
  async approveShutdown(teamName: string, teammateId: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    team.teammates = team.teammates.filter(t => t.id !== teammateId);

    this.emit('teammate:shutdown_approved', { team: teamName, teammateId });
  }

  /**
   * Reject teammate shutdown
   */
  async rejectShutdown(teamName: string, teammateId: string): Promise<void> {
    this.ensureAvailable();

    const team = this.activeTeams.get(teamName);
    if (!team) {
      throw new Error(`Team not found: ${teamName}`);
    }

    const teammate = team.teammates.find(t => t.id === teammateId);
    if (teammate) {
      teammate.status = 'active';
    }

    this.emit('teammate:shutdown_rejected', { team: teamName, teammateId });
  }

  /**
   * Cleanup team resources
   */
  async cleanup(teamName: string): Promise<void> {
    this.ensureAvailable();

    // Stop mailbox poller
    const poller = this.mailboxPollers.get(teamName);
    if (poller) {
      clearInterval(poller);
      this.mailboxPollers.delete(teamName);
    }

    // Remove team state
    this.activeTeams.delete(teamName);

    // Cleanup environment
    if (process.env.CLAUDE_CODE_TEAM_NAME === teamName) {
      delete process.env.CLAUDE_CODE_TEAM_NAME;
    }

    this.emit('team:cleanup', { team: teamName });
  }

  // ==================== UTILITY METHODS ====================

  /**
   * Get team state
   */
  getTeamState(teamName: string): TeamState | undefined {
    return this.activeTeams.get(teamName);
  }

  /**
   * Get all active teams
   */
  getAllTeams(): Map<string, TeamState> {
    return new Map(this.activeTeams);
  }

  // ==================== PRIVATE METHODS ====================

  private ensureAvailable(): void {
    if (!this.teammateToolAvailable) {
      throw new Error(
        `TeammateTool not available. Requires Claude Code >= 2.1.19, ` +
        `found: ${this.claudeCodeVersion ?? 'not installed'}`
      );
    }
  }

  private getMailboxPath(teamName: string, teammateId: string): string {
    return `${process.env.HOME}/.claude/teams/${teamName}/mailbox/${teammateId}.json`;
  }

  private async writeToMailbox(
    teamName: string,
    teammateId: string,
    message: MailboxMessage
  ): Promise<void> {
    const { mkdirSync, readFileSync, writeFileSync, existsSync } = await import('fs');

    const mailboxDir = `${process.env.HOME}/.claude/teams/${teamName}/mailbox`;
    const mailboxPath = `${mailboxDir}/${teammateId}.json`;

    // Ensure directory exists
    if (!existsSync(mailboxDir)) {
      mkdirSync(mailboxDir, { recursive: true });
    }

    // Read existing messages
    let messages: MailboxMessage[] = [];
    if (existsSync(mailboxPath)) {
      try {
        messages = JSON.parse(readFileSync(mailboxPath, 'utf-8'));
      } catch {
        messages = [];
      }
    }

    // Append new message
    messages.push(message);

    // Write back
    writeFileSync(mailboxPath, JSON.stringify(messages, null, 2), 'utf-8');
  }

  private startMailboxPoller(teamName: string): void {
    const pollInterval = 1000; // 1 second

    const poller = setInterval(async () => {
      const team = this.activeTeams.get(teamName);
      if (!team) {
        clearInterval(poller);
        return;
      }

      for (const teammate of team.teammates) {
        const messages = await this.readMailbox(teamName, teammate.id);
        if (messages.length > 0) {
          this.emit('mailbox:messages', {
            team: teamName,
            teammateId: teammate.id,
            messages,
          });
        }
      }
    }, pollInterval);

    this.mailboxPollers.set(teamName, poller);
  }
}

/**
 * Create and initialize a TeammateBridge instance
 */
export async function createTeammateBridge(
  config?: Partial<TeamConfig>
): Promise<TeammateBridge> {
  const bridge = new TeammateBridge(config);
  await bridge.initialize();
  return bridge;
}

3.4 Claude Flow Integration Layer

typescript
// claude-flow-integration.ts

import { TeammateBridge, createTeammateBridge } from './teammate-bridge.js';
import type { TeamConfig, TeammateSpawnConfig, TeamState } from './types.js';

/**
 * Integration layer between Claude Flow's swarm system
 * and Claude Code's native TeammateTool
 */
export class ClaudeFlowTeammateIntegration {
  private bridge: TeammateBridge | null = null;
  private fallbackEnabled: boolean = true;

  /**
   * Initialize integration
   * Auto-detects TeammateTool availability
   */
  async initialize(): Promise<{
    nativeAvailable: boolean;
    version: string | null;
    mode: 'native' | 'fallback';
  }> {
    this.bridge = await createTeammateBridge();

    const nativeAvailable = this.bridge.isAvailable();
    const version = this.bridge.getClaudeCodeVersion();
    const mode = nativeAvailable ? 'native' : 'fallback';

    return { nativeAvailable, version, mode };
  }

  /**
   * Map Claude Flow topology to team configuration
   */
  mapTopologyToTeamConfig(
    topology: 'hierarchical' | 'mesh' | 'adaptive',
    options: {
      maxAgents: number;
      planModeRequired?: boolean;
    }
  ): TeamConfig {
    return {
      name: `cf-team-${Date.now()}`,
      topology: topology === 'adaptive' ? 'hierarchical' : topology,
      maxTeammates: options.maxAgents,
      spawnBackend: 'auto',
      planModeRequired: options.planModeRequired ?? (topology === 'hierarchical'),
      autoApproveJoin: topology === 'mesh',
      messageRetention: 3600000, // 1 hour
    };
  }

  /**
   * Map Claude Flow agent type to teammate spawn config
   */
  mapAgentToTeammateConfig(
    agentType: string,
    task: string,
    options?: {
      model?: 'sonnet' | 'opus' | 'haiku';
      allowedTools?: string[];
    }
  ): TeammateSpawnConfig {
    // Map common Claude Flow agent types to roles
    const roleMap: Record<string, { role: string; defaultTools: string[] }> = {
      'coder': { role: 'coder', defaultTools: ['Edit', 'Write', 'Read', 'Bash'] },
      'tester': { role: 'tester', defaultTools: ['Read', 'Bash', 'Glob'] },
      'reviewer': { role: 'reviewer', defaultTools: ['Read', 'Grep', 'Glob'] },
      'researcher': { role: 'researcher', defaultTools: ['Read', 'WebSearch', 'WebFetch'] },
      'architect': { role: 'architect', defaultTools: ['Read', 'Glob', 'Grep'] },
      'security-architect': { role: 'security', defaultTools: ['Read', 'Grep', 'Bash'] },
      'hierarchical-coordinator': { role: 'coordinator', defaultTools: ['Read', 'TodoWrite'] },
    };

    const mapping = roleMap[agentType] ?? { role: agentType, defaultTools: [] };

    return {
      name: `${agentType}-${Date.now().toString(36)}`,
      role: mapping.role,
      prompt: task,
      model: options?.model,
      allowedTools: options?.allowedTools ?? mapping.defaultTools,
      mode: agentType.includes('coordinator') ? 'plan' : 'default',
      runInBackground: true,
    };
  }

  /**
   * Spawn swarm using native TeammateTool
   * Returns AgentInput configurations for Claude Code Task tool
   */
  async spawnSwarm(
    topology: 'hierarchical' | 'mesh' | 'adaptive',
    agents: Array<{ type: string; task: string; model?: 'sonnet' | 'opus' | 'haiku' }>,
    options?: { planModeRequired?: boolean }
  ): Promise<{
    teamName: string;
    agentInputs: Record<string, unknown>[];
    teamState: TeamState;
  }> {
    if (!this.bridge?.isAvailable()) {
      throw new Error('TeammateTool not available');
    }

    // Create team
    const teamConfig = this.mapTopologyToTeamConfig(topology, {
      maxAgents: agents.length,
      planModeRequired: options?.planModeRequired,
    });

    const teamState = await this.bridge.spawnTeam(teamConfig);

    // Build AgentInput for each agent
    const agentInputs = agents.map(agent => {
      const config = this.mapAgentToTeammateConfig(agent.type, agent.task, {
        model: agent.model,
      });
      config.teamName = teamConfig.name;
      return this.bridge!.buildAgentInput(config);
    });

    return {
      teamName: teamConfig.name,
      agentInputs,
      teamState,
    };
  }

  /**
   * Get bridge for direct access
   */
  getBridge(): TeammateBridge | null {
    return this.bridge;
  }
}

3.5 MCP Tool Definitions (16 Tools Implemented)

The plugin provides 16 MCP tools for complete TeammateTool integration:

ToolPurposePriority
teammate_spawn_teamCreate new teamCore
teammate_discover_teamsFind existing teamsCore
teammate_spawnSpawn teammateCore
teammate_send_messageDirect messagingCore
teammate_broadcastBroadcast to allCore
teammate_submit_planSubmit plan for approvalCore
teammate_approve_planVote to approveCore
teammate_launch_swarmExecute approved planCore
teammate_delegateDelegate authorityExtended
teammate_update_contextUpdate team contextExtended
teammate_save_memoryPersist teammate stateExtended
teammate_share_transcriptShare message historyExtended
teammate_push_remoteSync to Claude.aiExtended
teammate_teleportResume in new contextExtended
teammate_get_statusGet team statusUtility
teammate_cleanupClean up resourcesUtility
typescript
// mcp-tools.ts - Complete implementation in v3/@claude-flow/teammate-plugin/src/mcp-tools.ts

import { TEAMMATE_MCP_TOOLS, handleMCPTool } from '@claude-flow/teammate-plugin';

// List all tools
console.log(TEAMMATE_MCP_TOOLS.map(t => t.name));

// Handle tool call
const result = await handleMCPTool(bridge, 'teammate_spawn_team', {
  name: 'my-team',
  topology: 'hierarchical',
  maxTeammates: 8,
  planModeRequired: true,
});

4. Integration Patterns

4.1 Swarm Initialization Pattern

typescript
// In Claude Code conversation:

// 1. Initialize team via MCP
mcp__claude-flow__teammate_spawn_team({
  name: "feature-dev-team",
  topology: "hierarchical",
  maxTeammates: 6,
  planModeRequired: true
})

// 2. Spawn coordinator first
Task({
  description: "Spawn coordinator",
  prompt: "You are the team coordinator...",
  subagent_type: "hierarchical-coordinator",
  team_name: "feature-dev-team",
  mode: "plan",
  allowed_tools: ["Read", "TodoWrite", "teammate_*"]
})

// 3. Coordinator spawns workers via plan
// ... coordinator creates plan, gets approvals, launches swarm

4.2 Message Flow Pattern

┌─────────────────┐     broadcast      ┌─────────────────┐
│   Coordinator   │ ──────────────────►│    Coder #1     │
│                 │                    │                 │
│  Team: dev-team │     mailbox        │  Team: dev-team │
│  Role: coord    │ ◄────────────────► │  Role: coder    │
└─────────────────┘                    └─────────────────┘
        │                                      │
        │              write                   │
        │ ◄────────────────────────────────────┤
        │                                      │
        ▼                                      ▼
┌─────────────────┐                    ┌─────────────────┐
│   Memory/HNSW   │                    │   Tester #1     │
│   (sync point)  │                    │                 │
└─────────────────┘                    └─────────────────┘

4.3 Plan Approval Flow

1. Coordinator submits plan
   └─► teammate_submit_plan()

2. Plan broadcast to all teammates
   └─► broadcast(type: 'plan')

3. Teammates vote
   └─► teammate_approve_plan() / teammate_reject_plan()

4. On approval threshold met
   └─► teammate_launch_swarm()

5. Swarm executes plan steps
   └─► Claude Code spawns teammateCount agents

5. Security Considerations

5.1 Permission Model

ConcernMitigation
Tool escalationExplicit allowed_tools per teammate
Plan tamperingApproval workflow with quorum
Mailbox poisoningValidate sender in team membership
Resource exhaustionmaxTeammates limit per team

5.2 Sandbox Integration

typescript
// Teammates inherit sandbox restrictions
const teammateConfig: TeammateSpawnConfig = {
  // ...
  mode: 'default',  // Respects sandbox
  // OR
  mode: 'bypassPermissions',  // Only for trusted scenarios
};

6. Implementation Phases

Phase 1: Core Bridge ✅ COMPLETE

  • TeammateBridge implementation
  • Version detection (MINIMUM_CLAUDE_CODE_VERSION = '2.1.19')
  • Team spawn/cleanup
  • Mailbox read/write

Phase 2: Claude Flow Integration ✅ COMPLETE

  • Topology mapping (flat, hierarchical, mesh)
  • Agent type mapping (8 role presets)
  • MCP tool registration (16 tools)
  • Fallback to existing system (fallbackToMCP option)

Phase 3: Plan Orchestration ✅ COMPLETE

  • Plan submission workflow
  • Approval tracking
  • Swarm launch integration (launchSwarm)
  • Memory synchronization (saveTeammateMemory, loadTeammateMemory)

Phase 4: Testing & Documentation ✅ COMPLETE

  • TDD tests (Vitest suite)
  • README.md with version requirements
  • API documentation
  • Example workflows

Phase 5: Extended Features ✅ COMPLETE (From Gap Analysis)

  • Delegate mode (delegateToTeammate, revokeDelegation)
  • Remote sync (pushToRemote, pullFromRemote, syncWithRemote)
  • Session memory (saveTeammateMemory, loadTeammateMemory)
  • Team context (updateTeamContext, getTeamContext)
  • Teleport (teleportTeam, canTeleport)
  • Plan control (pausePlanExecution, resumePlanExecution, reenterPlanMode)
  • Transcript sharing (shareTranscript)
  • Error handling (TeammateError, 18 error codes)

7. Migration Path

From Claude Flow MCP-only to Hybrid

typescript
// Before: Pure MCP coordination
mcp__claude-flow__swarm_init({ topology: 'hierarchical' })

// After: Native when available, MCP fallback
const integration = new ClaudeFlowTeammateIntegration();
const { mode } = await integration.initialize();

if (mode === 'native') {
  // Use TeammateTool
  const { agentInputs } = await integration.spawnSwarm('hierarchical', agents);
  // Pass agentInputs to Task tool
} else {
  // Fallback to MCP
  mcp__claude-flow__swarm_init({ topology: 'hierarchical' })
}

8. Success Metrics

MetricTarget
Spawn latency< 500ms (vs MCP ~2s)
Message delivery< 100ms
Plan approval cycle< 5s for 4 agents
Memory overhead< 50MB per team
Code reduction-3000 lines (vs MCP impl)

9. Open Questions

  1. iTerm2 backend - Is this macOS-only? Need to verify spawn backend selection
  2. Statsig gates - Which feature flag controls TeammateTool? May need to enable
  3. Remote push - How does pushToRemote work for Claude.ai integration?
  4. Transcript sharing - Can teammates share execution transcripts?

10. References


Status: Implemented ✅ Package: @claude-flow/teammate-plugin (v1.0.0-alpha.1) Location: v3/@claude-flow/teammate-plugin/

Next Steps

  1. Publish to npm - Run npm publish --tag alpha from package directory
  2. Test with Claude Code 2.1.19+ - Verify native TeammateTool integration
  3. Monitor feedback - Track issues and feature requests
  4. Phase 6: Memory Bridge - Integrate with Claude Flow's HNSW memory system
  5. Phase 7: Consensus Integration - Bridge TeammateTool approval with Claude Flow consensus protocols