# Coeditor

> Build React applications with CopilotKit and LangGraph for AI-powered collaborative editing. Creates production-ready editor applications (text, document, or node-based) with real-time AI assistance, shared state management, and agentic workflows. Use this skill when users want to build intelligent editing interfaces with AI collaboration features.

- Skill: `diegosouzapw/coeditor` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/coeditor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/coeditor/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/coeditor

---


# CopilotKit + LangGraph Collaborative Editor Builder

Build production-ready React applications that combine CopilotKit's AI copilot interface with LangGraph's agentic workflows for intelligent, collaborative editing experiences.

## When to Use This Skill

Use this skill when:
- User wants to build an AI-powered editor (text, document, or node-based)
- User mentions "CopilotKit", "LangGraph", "AI copilot", or "collaborative editing"
- User wants to add AI assistance to an existing React application
- User needs real-time state sharing between UI and AI agents
- User wants to build document collaboration features with AI
- User mentions "agentic workflows" or "multi-agent systems" in editing context

## Overview

This skill scaffolds applications that combine:
- **CopilotKit**: React components for AI copilot experiences (chat, suggestions, actions)
- **LangGraph**: Agent orchestration framework for complex workflows
- **React State Management**: Shared state between editor UI and AI agents
- **Editor Types**: Text editors, document editors, or node-based editors

## Prerequisites Check

Before starting, verify the following:

1. **Node.js**: Version 18.x or higher
   ```bash
   node --version
   ```

2. **Package Manager**: npm, yarn, or pnpm
   ```bash
   npm --version
   ```

3. **Python**: Version 3.9 or higher (for LangGraph backend)
   ```bash
   python3 --version
   ```

4. **OpenAI API Key** (or other LLM provider):
   ```bash
   # Will need to be configured in .env
   echo "User will need OpenAI API key or alternative LLM provider"
   ```

## Interactive Requirements Gathering

**IMPORTANT**: Before scaffolding, ask the user these questions to customize the setup:

### 1. Editor Type Selection

"What type of editor would you like to build?"

**A. Text Editor**
- Rich text editing with AI assistance
- Code editor with autocomplete and suggestions
- Markdown editor with AI enhancements
- Best for: Writing tools, code editors, note-taking apps

**B. Document Editor**
- Structured document editing (like Google Docs)
- Multi-section documents with AI collaboration
- Template-based document creation
- Best for: Documentation tools, report builders, collaborative writing

**C. Node-Based Editor**
- Visual graph/flow editor with AI assistance
- Workflow builders with intelligent suggestions
- Mind mapping with AI expansion
- Best for: Workflow designers, visual programming tools, diagramming apps

### 2. LangGraph Agent Configuration

"What type of AI agents do you need?"

Ask the user to describe their use case, then suggest appropriate agents:

**Common Agent Types:**
- **Writing Assistant**: Helps with content creation, editing, and refinement
- **Code Assistant**: Provides code suggestions, explanations, and debugging
- **Research Agent**: Searches and incorporates external information
- **Reviewer Agent**: Reviews content and provides feedback
- **Summarizer Agent**: Creates summaries and extracts key points
- **Translator Agent**: Translates between languages
- **Custom Agents**: User-defined specialized agents

**Multi-Agent Workflows:**
- Ask if they need multiple agents working together
- Determine if agents should run sequentially or in parallel
- Identify handoff points between agents

### 3. State Management Requirements

"What data needs to be shared between the editor and AI agents?"

**Common State Patterns:**
- **Document State**: Content, structure, metadata
- **Selection State**: Current cursor position, selected text/nodes
- **History State**: Undo/redo, version tracking
- **Collaboration State**: Multi-user presence, changes
- **Agent State**: Current agent task, progress, results

### 4. Backend Architecture

"How would you like to deploy the LangGraph backend?"

**A. Local Development Server**
- FastAPI server running locally
- Best for: Development, prototyping
- Setup: Python virtual environment

**B. Cloud Deployment**
- Deploy to Vercel, Railway, or cloud provider
- Best for: Production, sharing with team
- Setup: Containerized deployment

**C. Serverless Functions**
- Deploy agents as serverless functions
- Best for: Cost-effective scaling
- Setup: Vercel Functions, AWS Lambda

### 5. Additional Features

Ask about optional features:

**CopilotKit Features:**
- [ ] Chat interface (CopilotChat)
- [ ] Inline suggestions (CopilotTextarea)
- [ ] Custom actions (CopilotAction)
- [ ] Context providers (document context)
- [ ] Keyboard shortcuts

**Editor Features:**
- [ ] Real-time collaboration
- [ ] Version history
- [ ] Comments and annotations
- [ ] Export formats (PDF, Markdown, etc.)
- [ ] Templates
- [ ] Search and replace

**AI Features:**
- [ ] Streaming responses
- [ ] Multi-turn conversations
- [ ] Agent memory/context
- [ ] Custom prompts/instructions
- [ ] Fine-tuned models

## Project Structure

Based on user selections, create this structure:

```
<app-name>/
├── frontend/                   # React application
│   ├── src/
│   │   ├── components/
│   │   │   ├── Editor/        # Editor components
│   │   │   │   ├── TextEditor.tsx
│   │   │   │   ├── DocumentEditor.tsx
│   │   │   │   └── NodeEditor.tsx
│   │   │   ├── Copilot/       # CopilotKit components
│   │   │   │   ├── CopilotProvider.tsx
│   │   │   │   ├── ChatPanel.tsx
│   │   │   │   └── Actions.tsx
│   │   │   └── shared/        # Shared UI components
│   │   ├── hooks/             # Custom React hooks
│   │   │   ├── useEditor.ts
│   │   │   ├── useAgents.ts
│   │   │   └── useSharedState.ts
│   │   ├── state/             # State management
│   │   │   ├── editorStore.ts
│   │   │   ├── agentStore.ts
│   │   │   └── types.ts
│   │   ├── lib/               # Utilities
│   │   │   ├── copilot-config.ts
│   │   │   └── api-client.ts
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── package.json
│   ├── vite.config.ts
│   └── tsconfig.json
│
├── backend/                    # LangGraph backend
│   ├── agents/                # Agent definitions
│   │   ├── __init__.py
│   │   ├── writing_assistant.py
│   │   ├── code_assistant.py
│   │   └── custom_agents.py
│   ├── graphs/                # LangGraph workflows
│   │   ├── __init__.py
│   │   ├── editor_graph.py
│   │   └── multi_agent_graph.py
│   ├── api/                   # FastAPI endpoints
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── routes.py
│   ├── state/                 # Shared state management
│   │   ├── __init__.py
│   │   └── state_manager.py
│   ├── requirements.txt
│   └── pyproject.toml
│
├── shared/                     # Shared types/schemas
│   ├── types.ts
│   └── schemas.py
│
├── .env.example
├── .gitignore
├── docker-compose.yml          # Optional: for containerized dev
├── README.md
└── package.json               # Root workspace config
```

## Step-by-Step Implementation

### Step 1: Project Initialization

**1.1 Create React Frontend (Vite + TypeScript)**

```bash
npm create vite@latest <app-name> -- --template react-ts
cd <app-name>
mv <app-name> frontend
```

**1.2 Install CopilotKit Dependencies**

```bash
cd frontend
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/react-textarea
```

**1.3 Install Editor Dependencies**

Choose based on editor type:

**For Text/Code Editor:**
```bash
npm install @monaco-editor/react      # VS Code editor
# OR
npm install @tiptap/react @tiptap/starter-kit  # Rich text
```

**For Document Editor:**
```bash
npm install slate slate-react         # Document framework
# OR
npm install @lexical/react lexical     # Facebook's editor framework
```

**For Node-Based Editor:**
```bash
npm install reactflow                  # Flow/graph editor
# OR
npm install @xyflow/react              # Advanced node editor
```

**1.4 Install State Management**

```bash
npm install zustand                    # Lightweight state management
# OR
npm install @tanstack/react-query      # Server state management
# OR
npm install jotai                      # Atomic state management
```

**1.5 Initialize Python Backend**

```bash
cd ..
mkdir backend
cd backend
python3 -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
```

**1.6 Install LangGraph Dependencies**

```bash
pip install langgraph langchain langchain-openai fastapi uvicorn python-dotenv pydantic websockets
```

Create `requirements.txt`:
```bash
pip freeze > requirements.txt
```

### Step 2: Configure CopilotKit

**2.1 Create CopilotKit Provider (`frontend/src/components/Copilot/CopilotProvider.tsx`)**

```typescript
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";

interface CopilotProviderProps {
  children: React.ReactNode;
}

export function CopilotProvider({ children }: CopilotProviderProps) {
  return (
    <CopilotKit
      runtimeUrl="/api/copilotkit"
      // Alternatively, connect directly to LangGraph backend:
      // runtimeUrl="http://localhost:8000/copilot"
    >
      <CopilotSidebar>
        {children}
      </CopilotSidebar>
    </CopilotKit>
  );
}
```

**2.2 Configure Context Providers**

CopilotKit needs access to editor state to provide relevant suggestions:

```typescript
import { useCopilotReadable, useCopilotAction } from "@copilotkit/react-core";

export function useEditorCopilot(editorState: EditorState) {
  // Make editor state readable by AI
  useCopilotReadable({
    description: "The current document content and structure",
    value: editorState,
  });

  // Define actions AI can perform
  useCopilotAction({
    name: "insertText",
    description: "Insert text at current cursor position",
    parameters: [
      {
        name: "text",
        type: "string",
        description: "The text to insert",
        required: true,
      },
    ],
    handler: async ({ text }) => {
      // Insert text into editor
      editorState.insertText(text);
    },
  });

  useCopilotAction({
    name: "replaceSelection",
    description: "Replace currently selected text",
    parameters: [
      {
        name: "newText",
        type: "string",
        description: "The replacement text",
        required: true,
      },
    ],
    handler: async ({ newText }) => {
      editorState.replaceSelection(newText);
    },
  });
}
```

### Step 3: Set Up LangGraph Backend

**3.1 Define State Schema (`backend/state/state_manager.py`)**

```python
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage
import operator

class EditorState(TypedDict):
    """Shared state between frontend and agents."""
    # Document content
    content: str
    # Current selection
    selection: dict
    # Chat messages
    messages: Annotated[Sequence[BaseMessage], operator.add]
    # Agent context
    current_agent: str
    # Task tracking
    task_status: str
    # Additional metadata
    metadata: dict
```

**3.2 Create Base Agent (`backend/agents/base_agent.py`)**

```python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

class BaseAgent:
    def __init__(self, name: str, system_prompt: str):
        self.name = name
        self.llm = ChatOpenAI(model="gpt-4", temperature=0.7)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", system_prompt),
            ("human", "{input}"),
        ])
        self.chain = self.prompt | self.llm

    async def process(self, state: EditorState) -> EditorState:
        """Process state and return updated state."""
        raise NotImplementedError
```

**3.3 Implement Specific Agents**

Based on user requirements, create specialized agents:

**Writing Assistant (`backend/agents/writing_assistant.py`)**

```python
from .base_agent import BaseAgent
from ..state.state_manager import EditorState

class WritingAssistant(BaseAgent):
    def __init__(self):
        super().__init__(
            name="writing_assistant",
            system_prompt="""You are an expert writing assistant.
            Help users improve their writing by:
            - Suggesting better phrasing
            - Fixing grammar and style issues
            - Expanding on ideas
            - Maintaining consistent tone

            Current document context: {content}
            Current selection: {selection}
            """
        )

    async def process(self, state: EditorState) -> EditorState:
        # Get current content and selection
        content = state.get("content", "")
        selection = state.get("selection", {})

        # Process with LLM
        result = await self.chain.ainvoke({
            "input": state["messages"][-1].content,
            "content": content,
            "selection": selection,
        })

        # Update state
        state["messages"].append(result)
        state["task_status"] = "completed"

        return state
```

**Code Assistant (`backend/agents/code_assistant.py`)**

```python
from .base_agent import BaseAgent
from ..state.state_manager import EditorState

class CodeAssistant(BaseAgent):
    def __init__(self):
        super().__init__(
            name="code_assistant",
            system_prompt="""You are an expert programming assistant.
            Help users with:
            - Code completion and suggestions
            - Bug finding and fixing
            - Code explanation
            - Refactoring suggestions

            Current code: {content}
            Selected code: {selection}
            """
        )

    async def process(self, state: EditorState) -> EditorState:
        content = state.get("content", "")
        selection = state.get("selection", {})

        result = await self.chain.ainvoke({
            "input": state["messages"][-1].content,
            "content": content,
            "selection": selection,
        })

        state["messages"].append(result)
        return state
```

**3.4 Create LangGraph Workflow (`backend/graphs/editor_graph.py`)**

```python
from langgraph.graph import StateGraph, END
from ..state.state_manager import EditorState
from ..agents.writing_assistant import WritingAssistant
from ..agents.code_assistant import CodeAssistant

def create_editor_graph():
    """Create LangGraph workflow for editor agents."""

    # Initialize agents
    writing_agent = WritingAssistant()
    code_agent = CodeAssistant()

    # Create graph
    workflow = StateGraph(EditorState)

    # Add nodes
    workflow.add_node("writing_assistant", writing_agent.process)
    workflow.add_node("code_assistant", code_agent.process)

    # Add conditional routing
    def route_agent(state: EditorState) -> str:
        """Route to appropriate agent based on state."""
        current_agent = state.get("current_agent", "writing_assistant")
        return current_agent

    # Set entry point
    workflow.set_conditional_entry_point(
        route_agent,
        {
            "writing_assistant": "writing_assistant",
            "code_assistant": "code_assistant",
        }
    )

    # Add edges
    workflow.add_edge("writing_assistant", END)
    workflow.add_edge("code_assistant", END)

    return workflow.compile()
```

**3.5 Create FastAPI Server (`backend/api/main.py`)**

```python
from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from ..graphs.editor_graph import create_editor_graph
from ..state.state_manager import EditorState
import json

app = FastAPI()

# CORS configuration
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],  # Vite dev server
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Initialize graph
graph = create_editor_graph()

@app.post("/copilot")
async def copilot_endpoint(request: dict):
    """CopilotKit-compatible endpoint."""
    # Extract state from request
    state: EditorState = {
        "content": request.get("context", {}).get("content", ""),
        "selection": request.get("context", {}).get("selection", {}),
        "messages": request.get("messages", []),
        "current_agent": request.get("agent", "writing_assistant"),
        "task_status": "pending",
        "metadata": {},
    }

    # Process through graph
    result = await graph.ainvoke(state)

    # Return response
    return {
        "message": result["messages"][-1].content,
        "state": result,
    }

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    """WebSocket for real-time updates."""
    await websocket.accept()

    try:
        while True:
            # Receive state update from frontend
            data = await websocket.receive_text()
            state = json.loads(data)

            # Process through graph
            result = await graph.ainvoke(state)

            # Send back to frontend
            await websocket.send_json(result)
    except Exception as e:
        print(f"WebSocket error: {e}")
    finally:
        await websocket.close()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
```

### Step 4: Implement Editor Component

Based on editor type, create the appropriate component:

**4.1 Text Editor (Monaco/VS Code)**

```typescript
// frontend/src/components/Editor/TextEditor.tsx
import Editor from '@monaco-editor/react';
import { useEditorStore } from '../../state/editorStore';
import { useEditorCopilot } from '../../hooks/useEditorCopilot';

export function TextEditor() {
  const { content, updateContent, selection } = useEditorStore();

  // Connect to CopilotKit
  useEditorCopilot({ content, selection });

  return (
    <Editor
      height="90vh"
      defaultLanguage="typescript"
      value={content}
      onChange={(value) => updateContent(value || '')}
      onCursorSelectionChange={(selection) => {
        useEditorStore.setState({ selection });
      }}
      theme="vs-dark"
      options={{
        minimap: { enabled: true },
        fontSize: 14,
        wordWrap: 'on',
      }}
    />
  );
}
```

**4.2 Document Editor (Slate)**

```typescript
// frontend/src/components/Editor/DocumentEditor.tsx
import { createEditor } from 'slate';
import { Slate, Editable, withReact } from 'slate-react';
import { useState, useMemo } from 'react';
import { useEditorCopilot } from '../../hooks/useEditorCopilot';

export function DocumentEditor() {
  const editor = useMemo(() => withReact(createEditor()), []);
  const [value, setValue] = useState(initialValue);

  // Connect to CopilotKit
  useEditorCopilot({ content: JSON.stringify(value) });

  return (
    <Slate editor={editor} value={value} onChange={setValue}>
      <Editable
        placeholder="Start writing..."
        renderLeaf={renderLeaf}
        renderElement={renderElement}
      />
    </Slate>
  );
}
```

**4.3 Node-Based Editor (ReactFlow)**

```typescript
// frontend/src/components/Editor/NodeEditor.tsx
import ReactFlow, {
  MiniMap,
  Controls,
  Background,
  useNodesState,
  useEdgesState,
} from 'reactflow';
import 'reactflow/dist/style.css';
import { useEditorCopilot } from '../../hooks/useEditorCopilot';

export function NodeEditor() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  // Connect to CopilotKit
  useEditorCopilot({
    content: JSON.stringify({ nodes, edges }),
    selection: selectedNodes,
  });

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        fitView
      >
        <Controls />
        <MiniMap />
        <Background variant="dots" gap={12} size={1} />
      </ReactFlow>
    </div>
  );
}
```

### Step 5: State Management

**5.1 Create Editor Store (Zustand)**

```typescript
// frontend/src/state/editorStore.ts
import { create } from 'zustand';

interface EditorState {
  content: string;
  selection: {
    start: number;
    end: number;
    text: string;
  };
  history: string[];
  currentAgent: string;

  // Actions
  updateContent: (content: string) => void;
  updateSelection: (selection: EditorState['selection']) => void;
  setAgent: (agent: string) => void;
  undo: () => void;
  redo: () => void;
}

export const useEditorStore = create<EditorState>((set, get) => ({
  content: '',
  selection: { start: 0, end: 0, text: '' },
  history: [],
  currentAgent: 'writing_assistant',

  updateContent: (content) =>
    set((state) => ({
      content,
      history: [...state.history, state.content],
    })),

  updateSelection: (selection) =>
    set({ selection }),

  setAgent: (agent) =>
    set({ currentAgent: agent }),

  undo: () => {
    const { history } = get();
    if (history.length > 0) {
      set({
        content: history[history.length - 1],
        history: history.slice(0, -1),
      });
    }
  },

  redo: () => {
    // Implement redo logic
  },
}));
```

**5.2 Create Agent Store**

```typescript
// frontend/src/state/agentStore.ts
import { create } from 'zustand';

interface Agent {
  id: string;
  name: string;
  description: string;
  status: 'idle' | 'working' | 'completed' | 'error';
}

interface AgentState {
  agents: Agent[];
  activeAgent: string | null;
  taskQueue: Task[];

  // Actions
  setActiveAgent: (agentId: string) => void;
  updateAgentStatus: (agentId: string, status: Agent['status']) => void;
  addTask: (task: Task) => void;
}

export const useAgentStore = create<AgentState>((set) => ({
  agents: [
    {
      id: 'writing_assistant',
      name: 'Writing Assistant',
      description: 'Helps with writing and editing',
      status: 'idle',
    },
    {
      id: 'code_assistant',
      name: 'Code Assistant',
      description: 'Helps with coding and debugging',
      status: 'idle',
    },
  ],
  activeAgent: null,
  taskQueue: [],

  setActiveAgent: (agentId) =>
    set({ activeAgent: agentId }),

  updateAgentStatus: (agentId, status) =>
    set((state) => ({
      agents: state.agents.map((agent) =>
        agent.id === agentId ? { ...agent, status } : agent
      ),
    })),

  addTask: (task) =>
    set((state) => ({
      taskQueue: [...state.taskQueue, task],
    })),
}));
```

### Step 6: Connect Frontend to Backend

**6.1 Create API Client (`frontend/src/lib/api-client.ts`)**

```typescript
interface CopilotRequest {
  context: {
    content: string;
    selection: any;
  };
  messages: any[];
  agent: string;
}

export class AgentAPIClient {
  private baseUrl: string;

  constructor(baseUrl: string = 'http://localhost:8000') {
    this.baseUrl = baseUrl;
  }

  async sendMessage(request: CopilotRequest) {
    const response = await fetch(`${this.baseUrl}/copilot`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(request),
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.statusText}`);
    }

    return response.json();
  }

  // WebSocket connection for real-time updates
  connectWebSocket(onMessage: (data: any) => void) {
    const ws = new WebSocket(`ws://localhost:8000/ws`);

    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      onMessage(data);
    };

    return ws;
  }
}
```

**6.2 Create Custom Hook (`frontend/src/hooks/useAgents.ts`)**

```typescript
import { useEffect, useState } from 'react';
import { AgentAPIClient } from '../lib/api-client';
import { useEditorStore } from '../state/editorStore';
import { useAgentStore } from '../state/agentStore';

export function useAgents() {
  const [client] = useState(() => new AgentAPIClient());
  const { content, selection, currentAgent } = useEditorStore();
  const { updateAgentStatus } = useAgentStore();

  const sendToAgent = async (message: string) => {
    updateAgentStatus(currentAgent, 'working');

    try {
      const response = await client.sendMessage({
        context: { content, selection },
        messages: [{ role: 'user', content: message }],
        agent: currentAgent,
      });

      updateAgentStatus(currentAgent, 'completed');
      return response;
    } catch (error) {
      updateAgentStatus(currentAgent, 'error');
      throw error;
    }
  };

  return { sendToAgent };
}
```

### Step 7: Configuration Files

**7.1 Environment Variables**

Create `.env.example`:
```bash
# Frontend
VITE_API_URL=http://localhost:8000

# Backend
OPENAI_API_KEY=your-api-key-here
ANTHROPIC_API_KEY=optional-claude-key
LANGCHAIN_API_KEY=optional-langsmith-key
LANGCHAIN_TRACING_V2=true
LANGCHAIN_PROJECT=coeditor-project
```

**7.2 Docker Compose (Optional)**

```yaml
# docker-compose.yml
version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "5173:5173"
    environment:
      - VITE_API_URL=http://backend:8000
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "8000:8000"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - ./backend:/app
```

### Step 8: Development Scripts

**8.1 Root Package.json**

```json
{
  "name": "coeditor-app",
  "private": true,
  "workspaces": ["frontend", "backend"],
  "scripts": {
    "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:backend\"",
    "dev:frontend": "cd frontend && npm run dev",
    "dev:backend": "cd backend && python -m uvicorn api.main:app --reload",
    "build": "npm run build:frontend",
    "build:frontend": "cd frontend && npm run build",
    "type-check": "cd frontend && npm run type-check"
  },
  "devDependencies": {
    "concurrently": "^8.2.0"
  }
}
```

## Advanced Patterns

### Multi-Agent Workflows

For complex tasks requiring multiple agents:

```python
# backend/graphs/multi_agent_graph.py
from langgraph.graph import StateGraph, END

def create_multi_agent_workflow():
    workflow = StateGraph(EditorState)

    # Add multiple agents
    workflow.add_node("researcher", researcher_agent.process)
    workflow.add_node("writer", writer_agent.process)
    workflow.add_node("reviewer", reviewer_agent.process)

    # Create workflow: research → write → review
    workflow.set_entry_point("researcher")
    workflow.add_edge("researcher", "writer")
    workflow.add_edge("writer", "reviewer")

    # Conditional loop back for revisions
    workflow.add_conditional_edges(
        "reviewer",
        should_revise,
        {
            "revise": "writer",
            "approve": END,
        }
    )

    return workflow.compile()
```

### Streaming Responses

For better UX with long-running tasks:

```typescript
// Frontend streaming
async function* streamAgentResponse(message: string) {
  const response = await fetch('/api/copilot/stream', {
    method: 'POST',
    body: JSON.stringify({ message }),
  });

  const reader = response.body?.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    yield chunk;
  }
}
```

### Context-Aware Actions

Make agents aware of editor context:

```typescript
useCopilotAction({
  name: "improveSelection",
  description: "Improve the currently selected text",
  handler: async () => {
    const { selection } = useEditorStore.getState();
    const improved = await sendToAgent(
      `Improve this text: ${selection.text}`
    );
    replaceSelection(improved);
  },
});
```

## Testing

### Frontend Tests

```typescript
// frontend/src/components/Editor/__tests__/TextEditor.test.tsx
import { render, screen } from '@testing-library/react';
import { TextEditor } from '../TextEditor';

describe('TextEditor', () => {
  it('renders editor', () => {
    render(<TextEditor />);
    expect(screen.getByRole('textbox')).toBeInTheDocument();
  });

  it('updates content on change', async () => {
    // Test implementation
  });
});
```

### Backend Tests

```python
# backend/tests/test_agents.py
import pytest
from agents.writing_assistant import WritingAssistant

@pytest.mark.asyncio
async def test_writing_assistant():
    agent = WritingAssistant()
    state = {
        "content": "This is a test",
        "messages": [{"role": "user", "content": "Improve this"}],
    }
    result = await agent.process(state)
    assert "messages" in result
```

## Deployment

### Frontend Deployment (Vercel)

```bash
cd frontend
vercel deploy --prod
```

### Backend Deployment (Railway/Render)

```bash
# Dockerfile for backend
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

## Best Practices Checklist

When building the application, ensure:

### Architecture
- [ ] Clear separation between frontend and backend
- [ ] Type-safe communication (TypeScript + Pydantic)
- [ ] Shared state schema between frontend and backend
- [ ] Proper error handling and fallbacks

### CopilotKit Integration
- [ ] Context providers configured for editor state
- [ ] Custom actions defined for common tasks
- [ ] Proper streaming for long responses
- [ ] Keyboard shortcuts configured

### LangGraph Implementation
- [ ] Agents properly isolated with clear responsibilities
- [ ] State transitions well-defined
- [ ] Error recovery mechanisms
- [ ] Logging and observability (LangSmith)

### Editor Experience
- [ ] Responsive UI with loading states
- [ ] Undo/redo functionality
- [ ] Auto-save and persistence
- [ ] Keyboard shortcuts
- [ ] Accessibility (ARIA labels)

### Performance
- [ ] Debounced state updates
- [ ] Lazy loading for heavy components
- [ ] Optimized bundle size
- [ ] Caching for API responses

### Security
- [ ] API key protection (server-side only)
- [ ] Input validation
- [ ] Rate limiting
- [ ] CORS properly configured
- [ ] Sanitized user inputs

## Common Pitfalls to Avoid

- ❌ Exposing API keys in frontend code
- ❌ Not handling agent failures gracefully
- ❌ Blocking UI while waiting for agent responses
- ❌ Not validating state updates from agents
- ❌ Forgetting to cleanup WebSocket connections
- ❌ Not implementing proper error boundaries
- ❌ Ignoring TypeScript errors
- ❌ Not testing on different screen sizes
- ❌ Hardcoding backend URLs
- ❌ Not implementing proper loading states

## Example Use Cases

### 1. AI-Powered Writing App
- **Editor**: Rich text (TipTap)
- **Agents**: Writing assistant, grammar checker, style improver
- **Features**: Real-time suggestions, tone adjustment, expansion

### 2. Code Collaboration Tool
- **Editor**: Monaco (VS Code)
- **Agents**: Code assistant, reviewer, documentation generator
- **Features**: Code completion, bug detection, auto-documentation

### 3. Workflow Designer
- **Editor**: ReactFlow nodes
- **Agents**: Workflow optimizer, validator, template suggester
- **Features**: Smart node suggestions, validation, auto-layout

## Reference Documentation

For detailed implementation patterns and API references:
- `references/copilotkit-patterns.md` - CopilotKit integration patterns
- `references/langgraph-agents.md` - LangGraph agent implementations
- `references/state-management.md` - State synchronization patterns
- `references/editor-integrations.md` - Editor-specific implementations

## Post-Setup Checklist

After scaffolding, guide the user to:

1. ✅ Configure environment variables (.env)
2. ✅ Set up OpenAI or LLM provider API key
3. ✅ Test frontend development server
4. ✅ Test backend API server
5. ✅ Verify CopilotKit connection
6. ✅ Test agent responses
7. ✅ Customize agent prompts
8. ✅ Add custom actions
9. ✅ Implement additional features
10. ✅ Deploy to production

## Troubleshooting

### CopilotKit not connecting
- Verify `runtimeUrl` is correct
- Check CORS configuration on backend
- Ensure backend is running

### Agents not responding
- Check API keys are configured
- Verify LangGraph state schema matches
- Check backend logs for errors

### Editor state not syncing
- Verify `useCopilotReadable` is called
- Check WebSocket connection
- Ensure state updates are properly dispatched

## Version Compatibility

This skill targets:
- **React**: 18+
- **CopilotKit**: Latest (@copilotkit/react-core)
- **LangGraph**: Latest (langgraph)
- **Python**: 3.9+
- **Node.js**: 18+
- **TypeScript**: 5+

## Next Steps

After scaffolding:
1. Customize agent prompts for your use case
2. Add domain-specific actions
3. Implement additional editor features
4. Set up analytics and monitoring
5. Plan deployment strategy
6. Create user documentation

