diff --git a/README.md b/README.md index 3be22c7..8aced47 100644 --- a/README.md +++ b/README.md @@ -74,18 +74,36 @@ make clean # Remove build artifacts Once installed, use these slash commands in any channel: -- `/claude ` - Send a message to Claude Code +### Session Management - `/claude-start [project-path]` - Start a new coding session - `/claude-stop` - Stop the current session -- `/claude-help` - Show help information +- `/claude-status` - Show current session status + +### Communication +- `/claude ` - Send a message to Claude Code +- `/claude-thread [action]` - Add thread context to Claude's session + - `/claude-thread` - Add thread messages as context + - `/claude-thread summarize` - Add context and ask Claude to summarize + - `/claude-thread implement` - Add context and ask Claude to implement + - `/claude-thread review` - Add context and ask Claude to review + +### File Operations +- `/claude-files` - Browse and manage project files +- `/claude-new-file ` - Create a new file + +### Help +- `/claude-help` - Show detailed help information ## Project Status -✅ **Issue #2: Project Setup** - COMPLETE -⏳ **Issue #3: Bridge Server** - In Progress -⏳ **Issue #4: Slash Commands** - Pending -⏳ **Issue #5: Interactive Components** - Pending -⏳ **Issue #6: File Operations** - Pending +✅ **Issue #2: Project Setup & Scaffolding** - COMPLETE +✅ **Issue #3: Bridge Server** - COMPLETE +✅ **Issue #4: Slash Commands & Bot Integration** - COMPLETE +✅ **Issue #5: Interactive Message Components** - COMPLETE +✅ **Issue #9: Thread Context Integration** - COMPLETE +🔄 **Issue #6: File Explorer & File Operations** - IN REVIEW (PR #11) +⏳ **Issue #7: Testing & Documentation** - IN PROGRESS +⏳ **Issue #8: Deployment & CI/CD** - PENDING See [Issues](https://github.com/appsome/claude-code-mattermost-plugin/issues) for the full roadmap. diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..c3107cc --- /dev/null +++ b/docs/API.md @@ -0,0 +1,620 @@ +# Bridge Server API Reference + +## Overview + +The Bridge Server provides a REST API for managing Claude Code sessions and a WebSocket API for real-time communication. + +**Base URL:** `http://localhost:3002` (configurable) + +**Authentication:** Bearer token (configured in plugin settings) + +``` +Authorization: Bearer YOUR_API_TOKEN +``` + +## REST API + +### Sessions + +#### Create Session + +Create a new Claude Code session. + +```http +POST /api/sessions +Content-Type: application/json + +{ + "projectPath": "/path/to/project", + "userId": "mattermost_user_id", + "channelId": "mattermost_channel_id" +} +``` + +**Response:** +```json +{ + "sessionId": "abc123", + "status": "active", + "projectPath": "/path/to/project", + "createdAt": "2024-03-09T12:00:00Z" +} +``` + +**Status Codes:** +- `201 Created` - Session created successfully +- `400 Bad Request` - Invalid project path or missing parameters +- `500 Internal Server Error` - Failed to spawn CLI + +--- + +#### Get Session + +Get information about a specific session. + +```http +GET /api/sessions/:id +``` + +**Response:** +```json +{ + "sessionId": "abc123", + "status": "active", + "projectPath": "/path/to/project", + "userId": "user123", + "channelId": "channel456", + "createdAt": "2024-03-09T12:00:00Z", + "lastActive": "2024-03-09T12:30:00Z" +} +``` + +**Status Codes:** +- `200 OK` - Session found +- `404 Not Found` - Session does not exist + +--- + +#### Stop Session + +Stop and cleanup a session. + +```http +DELETE /api/sessions/:id +``` + +**Response:** +```json +{ + "message": "Session stopped successfully", + "sessionId": "abc123" +} +``` + +**Status Codes:** +- `200 OK` - Session stopped +- `404 Not Found` - Session does not exist + +--- + +### Messages + +#### Send Prompt + +Send a user message to Claude Code. + +```http +POST /api/sessions/:id/prompt +Content-Type: application/json + +{ + "content": "Add authentication to the login page" +} +``` + +**Response:** +```json +{ + "message": "Prompt sent successfully", + "timestamp": 1709985600000 +} +``` + +**Status Codes:** +- `200 OK` - Prompt sent (response will come via WebSocket) +- `404 Not Found` - Session does not exist +- `400 Bad Request` - Empty content + +--- + +#### Inject Context + +Add context (thread history, file content) to session. + +```http +POST /api/sessions/:id/context +Content-Type: application/json + +{ + "source": "mattermost-thread", + "threadId": "thread_xyz", + "content": "Thread Context from #general...", + "action": "summarize", + "metadata": { + "channelName": "general", + "rootPostId": "xyz789", + "messageCount": 12, + "participants": ["@wojtek", "@ada"] + } +} +``` + +**Response:** +```json +{ + "message": "Context injected successfully", + "metadata": { + "contentLength": 456, + "actionRequested": true, + "source": "mattermost-thread" + } +} +``` + +**Status Codes:** +- `200 OK` - Context added +- `404 Not Found` - Session does not exist +- `400 Bad Request` - Missing required fields + +--- + +### Files + +#### List Files + +Get project file tree. + +```http +GET /api/sessions/:id/files +``` + +**Query Parameters:** +- `maxDepth` (optional) - Maximum directory depth (default: 5) +- `includeHidden` (optional) - Include hidden files (default: false) + +**Response:** +```json +{ + "files": [ + { + "name": "src", + "path": "src", + "type": "directory", + "children": [ + { + "name": "index.js", + "path": "src/index.js", + "type": "file", + "size": 1234 + } + ] + } + ] +} +``` + +**Status Codes:** +- `200 OK` - File list retrieved +- `404 Not Found` - Session does not exist + +--- + +#### Get File Content + +Read file content. + +```http +GET /api/sessions/:id/files/:path +``` + +**Example:** +```http +GET /api/sessions/abc123/files/src/index.js +``` + +**Response:** +```json +{ + "path": "src/index.js", + "content": "console.log('Hello, world!');", + "size": 28, + "encoding": "utf8" +} +``` + +**Status Codes:** +- `200 OK` - File content retrieved +- `404 Not Found` - Session or file does not exist +- `403 Forbidden` - File outside project directory + +--- + +#### Create File + +Create a new file. + +```http +POST /api/sessions/:id/files +Content-Type: application/json + +{ + "path": "src/components/NewComponent.tsx", + "content": "export default function NewComponent() {\n return
Hello
;\n}" +} +``` + +**Response:** +```json +{ + "message": "File created successfully", + "path": "src/components/NewComponent.tsx" +} +``` + +**Status Codes:** +- `201 Created` - File created +- `400 Bad Request` - Invalid path or content +- `409 Conflict` - File already exists +- `404 Not Found` - Session does not exist + +--- + +#### Update File + +Update existing file content. + +```http +PUT /api/sessions/:id/files/:path +Content-Type: application/json + +{ + "content": "Updated file content..." +} +``` + +**Response:** +```json +{ + "message": "File updated successfully", + "path": "src/index.js" +} +``` + +**Status Codes:** +- `200 OK` - File updated +- `404 Not Found` - Session or file does not exist +- `403 Forbidden` - File outside project directory + +--- + +#### Delete File + +Delete a file. + +```http +DELETE /api/sessions/:id/files/:path +``` + +**Response:** +```json +{ + "message": "File deleted successfully", + "path": "src/old-file.js" +} +``` + +**Status Codes:** +- `200 OK` - File deleted +- `404 Not Found` - Session or file does not exist +- `403 Forbidden` - File outside project directory + +--- + +### Actions + +#### Approve Change + +Approve a proposed code change. + +```http +POST /api/sessions/:id/approve +Content-Type: application/json + +{ + "changeId": "change_123" +} +``` + +**Response:** +```json +{ + "message": "Change approved", + "changeId": "change_123" +} +``` + +**Status Codes:** +- `200 OK` - Change approved +- `404 Not Found` - Session or change does not exist + +--- + +#### Reject Change + +Reject a proposed code change. + +```http +POST /api/sessions/:id/reject +Content-Type: application/json + +{ + "changeId": "change_123", + "reason": "Wrong approach, please use Redux instead" +} +``` + +**Response:** +```json +{ + "message": "Change rejected", + "changeId": "change_123" +} +``` + +**Status Codes:** +- `200 OK` - Change rejected +- `404 Not Found` - Session or change does not exist + +--- + +### Health + +#### Health Check + +Check bridge server health. + +```http +GET /health +``` + +**Response:** +```json +{ + "status": "ok", + "version": "1.0.0", + "uptime": 123456, + "sessions": 3, + "timestamp": "2024-03-09T12:00:00Z" +} +``` + +**Status Codes:** +- `200 OK` - Server is healthy +- `503 Service Unavailable` - Server is unhealthy + +--- + +## WebSocket API + +**Endpoint:** `ws://localhost:3002/ws` + +### Connection + +Connect with session ID as query parameter: + +```javascript +const ws = new WebSocket('ws://localhost:3002/ws?sessionId=abc123'); +``` + +### Server → Client Events + +#### Message Event + +Claude's response to user prompt. + +```json +{ + "type": "message", + "sessionId": "abc123", + "content": "I'll add authentication to the login page...", + "timestamp": 1709985600000, + "complete": false +} +``` + +**Fields:** +- `type` - Always `"message"` +- `sessionId` - Session identifier +- `content` - Response text (may be partial) +- `timestamp` - Unix timestamp (ms) +- `complete` - `true` when message is fully sent + +--- + +#### Status Event + +Session status change. + +```json +{ + "type": "status", + "sessionId": "abc123", + "status": "idle", + "timestamp": 1709985600000 +} +``` + +**Status Values:** +- `"active"` - Session running, processing request +- `"idle"` - Session ready for input +- `"stopped"` - Session ended + +--- + +#### File Change Event + +File modified by Claude. + +```json +{ + "type": "file_change", + "sessionId": "abc123", + "path": "src/auth/login.js", + "action": "modified", + "timestamp": 1709985600000 +} +``` + +**Action Values:** +- `"created"` - New file created +- `"modified"` - Existing file updated +- `"deleted"` - File removed + +--- + +#### Error Event + +Error occurred during processing. + +```json +{ + "type": "error", + "sessionId": "abc123", + "error": "File not found: src/missing.js", + "timestamp": 1709985600000 +} +``` + +--- + +### Client → Server Events + +#### Prompt Event + +Send user message (alternative to REST API). + +```json +{ + "type": "prompt", + "sessionId": "abc123", + "content": "Add a logout button" +} +``` + +--- + +## Error Responses + +All error responses follow this format: + +```json +{ + "error": "Error message describing what went wrong", + "code": "ERROR_CODE", + "details": { + "additionalInfo": "Optional extra context" + } +} +``` + +### Common Error Codes + +| Code | Description | +|------|-------------| +| `SESSION_NOT_FOUND` | Session ID does not exist | +| `INVALID_PROJECT_PATH` | Project path is invalid or inaccessible | +| `CLI_SPAWN_FAILED` | Failed to start Claude Code CLI | +| `FILE_NOT_FOUND` | Requested file does not exist | +| `PERMISSION_DENIED` | File operation not allowed (outside project) | +| `VALIDATION_ERROR` | Request validation failed | +| `INTERNAL_ERROR` | Unexpected server error | + +--- + +## Rate Limits + +- **Global:** 100 requests/minute per IP +- **Per Session:** 30 prompts/minute +- **File Operations:** 50 operations/minute per session + +Rate limit headers: +``` +X-RateLimit-Limit: 100 +X-RateLimit-Remaining: 95 +X-RateLimit-Reset: 1709985660 +``` + +--- + +## Examples + +### Complete Flow (REST + WebSocket) + +```javascript +// 1. Create session +const sessionResp = await fetch('http://localhost:3002/api/sessions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer YOUR_TOKEN' + }, + body: JSON.stringify({ + projectPath: '/home/user/project', + userId: 'user123', + channelId: 'channel456' + }) +}); +const { sessionId } = await sessionResp.json(); + +// 2. Connect WebSocket +const ws = new WebSocket(`ws://localhost:3002/ws?sessionId=${sessionId}`); + +ws.on('message', (data) => { + const event = JSON.parse(data); + console.log('Received:', event.type, event.content); +}); + +// 3. Send prompt +await fetch(`http://localhost:3002/api/sessions/${sessionId}/prompt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer YOUR_TOKEN' + }, + body: JSON.stringify({ + content: 'Add a login page' + }) +}); + +// 4. Receive response via WebSocket +// (WebSocket messages will arrive asynchronously) + +// 5. Stop session when done +await fetch(`http://localhost:3002/api/sessions/${sessionId}`, { + method: 'DELETE', + headers: { + 'Authorization': 'Bearer YOUR_TOKEN' + } +}); +``` + +--- + +## Changelog + +### v1.0.0 (2024-03-09) +- Initial API release +- Session management endpoints +- File operations +- Context injection +- WebSocket real-time updates diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..c60cb08 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,410 @@ +# Architecture + +## System Overview + +The Claude Code Mattermost Plugin provides a native integration between Mattermost and Claude Code CLI, replacing the web UI with chat-based interactions. + +## High-Level Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Mattermost Server │ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ Claude Code Plugin │ │ +│ │ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ Go Backend │◄─────────►│React Frontend│ │ │ +│ │ │ (Commands, │ │ (UI Components) │ │ +│ │ │ Sessions) │ │ │ │ │ +│ │ └──────┬───────┘ └────────────────┘ │ │ +│ └─────────┼──────────────────────────────────────────────┘ │ +└────────────┼──────────────────────────────────────────────┘ + │ REST + WebSocket + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Bridge Server (Node.js) │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Session │ │ WebSocket │ │ SQLite │ │ +│ │ Manager │ │ Server │ │ Database │ │ +│ └──────┬───────┘ └──────────────┘ └──────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ CLI Spawner & Manager │ │ +│ └──────────────────────────┬───────────────────────────┘ │ +└─────────────────────────────┼──────────────────────────────┘ + │ spawn/stdin/stdout + ▼ + ┌──────────────────┐ + │ Claude Code CLI │ + │ (AI Assistant) │ + └──────────────────┘ +``` + +## Components + +### 1. Mattermost Plugin (Go Backend) + +**Responsibilities:** +- Register and handle slash commands +- Manage session lifecycle per channel +- Create interactive messages and dialogs +- Communicate with bridge server +- Persist session data in KV store + +**Key Files:** +- `plugin.go` - Plugin lifecycle, initialization +- `commands.go` - Slash command routing and execution +- `session_manager.go` - Session state management +- `bridge_client.go` - HTTP client for bridge server +- `websocket_client.go` - WebSocket connection handling +- `thread_context.go` - Thread history integration +- `file_operations.go` - File browsing and management + +**Data Flow:** +1. User types slash command in Mattermost +2. Plugin receives command via `ExecuteCommand()` +3. Plugin validates and routes to handler +4. Handler interacts with bridge server +5. Response posted back to channel via bot + +### 2. Mattermost Plugin (React Frontend) + +**Responsibilities:** +- Render custom UI components +- Handle interactive button clicks +- Display dialogs and modals +- Real-time message updates + +**Key Files:** +- `index.tsx` - Plugin initialization +- `components/` - React components for UI + +**Data Flow:** +1. Plugin registers React components +2. Components render in Mattermost UI +3. User interactions trigger API calls +4. Updates reflected in real-time + +### 3. Bridge Server (Node.js) + +**Responsibilities:** +- Spawn and manage Claude Code CLI processes +- Provide REST API for session operations +- WebSocket server for real-time communication +- Session persistence in SQLite +- File system operations +- Context injection (threads, files) + +**Key Files:** +- `index.ts` - Express server setup +- `session-manager.ts` - Session lifecycle +- `cli-spawner.ts` - CLI process management +- `websocket-server.ts` - Real-time messaging +- `database.ts` - SQLite operations +- `api/sessions.ts` - Session endpoints +- `api/files.ts` - File operation endpoints +- `api/context.ts` - Context injection + +**API Endpoints:** + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/api/sessions` | Create new session | +| GET | `/api/sessions/:id` | Get session status | +| DELETE | `/api/sessions/:id` | Stop session | +| POST | `/api/sessions/:id/prompt` | Send message to Claude | +| POST | `/api/sessions/:id/context` | Inject context (thread/file) | +| GET | `/api/sessions/:id/files` | List project files | +| GET | `/api/sessions/:id/files/:path` | Get file content | +| POST | `/api/sessions/:id/files` | Create file | +| PUT | `/api/sessions/:id/files/:path` | Update file | +| DELETE | `/api/sessions/:id/files/:path` | Delete file | +| POST | `/api/sessions/:id/approve` | Approve change | +| POST | `/api/sessions/:id/reject` | Reject change | + +**WebSocket Events:** + +| Direction | Event | Payload | Description | +|-----------|-------|---------|-------------| +| Server→Client | `message` | `{content, type}` | Claude's response | +| Server→Client | `status` | `{status}` | Session status change | +| Server→Client | `file_change` | `{path, action}` | File modified | +| Server→Client | `error` | `{error}` | Error occurred | +| Client→Server | `prompt` | `{content}` | User message | + +### 4. Claude Code CLI + +**Responsibilities:** +- Process natural language instructions +- Read and modify files +- Execute shell commands +- Maintain conversation context + +**Communication:** +- Bridge server spawns CLI as child process +- Stdin for sending prompts +- Stdout for receiving responses +- Stderr for errors and logs + +## Data Models + +### Session + +```go +type Session struct { + SessionID string `json:"session_id"` + ChannelID string `json:"channel_id"` + UserID string `json:"user_id"` + ProjectPath string `json:"project_path"` + Status string `json:"status"` // "active", "idle", "stopped" + CreatedAt time.Time `json:"created_at"` + LastActive time.Time `json:"last_active"` +} +``` + +### Message + +```typescript +interface Message { + id: string; + sessionId: string; + type: 'user' | 'assistant' | 'system'; + content: string; + timestamp: number; +} +``` + +### FileNode + +```go +type FileNode struct { + Name string `json:"name"` + Path string `json:"path"` + Type string `json:"type"` // "file" | "directory" + Size *int64 `json:"size,omitempty"` + Children []FileNode `json:"children,omitempty"` +} +``` + +## Communication Patterns + +### 1. Synchronous Request/Response (REST) + +Used for commands that need immediate response: + +``` +Plugin Bridge Server + │ │ + ├──POST /api/sessions─────────► + │ │ + ◄─────{session_id}────────────┤ + │ │ +``` + +### 2. Asynchronous Streaming (WebSocket) + +Used for long-running operations and real-time updates: + +``` +Plugin Bridge Server + │ │ + ├──WebSocket Connect──────────► + │ │ + ├──{type: "prompt"}───────────► + │ │ + ◄──{type: "message"}──────────┤ + ◄──{type: "message"}──────────┤ + ◄──{type: "message"}──────────┤ + │ │ +``` + +### 3. Interactive Actions + +User approvals flow through REST API: + +``` +User Plugin Bridge Server + │ │ │ + ├─Click Approve────────► │ + │ ├─POST /api/.../approve► + │ │ │ + │ ◄─────{success}─────────┤ + │ │ │ + ◄─Confirmation Message─┤ │ + │ │ │ +``` + +## Security Considerations + +### Authentication + +- **Plugin to Bridge:** API token configured in plugin settings +- **Plugin to Mattermost:** Uses Mattermost plugin API (authenticated) +- **User to Plugin:** Mattermost handles user authentication + +### Authorization + +- Sessions are channel-scoped (one session per channel) +- Only users in the channel can interact with that session +- File operations restricted to project directory + +### Input Validation + +- File paths validated to prevent directory traversal +- Project paths must be absolute and existing +- Command arguments sanitized before execution + +### Data Privacy + +- Sessions stored in KV store (encrypted at rest by Mattermost) +- Bridge server database can be encrypted +- Thread context respects Mattermost permissions +- No user data sent to external services without consent + +## Scalability + +### Current Design + +- **Single Bridge Server:** Handles all sessions +- **In-Memory State:** Session data in memory + SQLite backup +- **One CLI per Session:** Each session spawns separate Claude Code process + +### Limitations + +- Bridge server is single point of failure +- CLI processes consume significant memory (each runs independently) +- SQLite limits concurrent writes + +### Future Improvements + +- **Horizontal Scaling:** Multiple bridge servers with load balancer +- **Session Affinity:** Sticky sessions or distributed state +- **Process Pooling:** Reuse CLI processes across sessions +- **Database:** Move to PostgreSQL for better concurrency +- **Message Queue:** Redis for WebSocket message distribution + +## Error Handling + +### Plugin Layer + +- Command validation errors → ephemeral message +- Bridge server unavailable → retry with exponential backoff +- WebSocket disconnect → automatic reconnection +- Session not found → clear message to user + +### Bridge Server Layer + +- CLI spawn failure → return error to plugin +- CLI crash → cleanup session, notify plugin +- File operation errors → return descriptive error +- Database errors → log and return 500 + +### Recovery Strategies + +- **Session Recovery:** Persist session state, restore on restart +- **CLI Crash:** Detect via process exit, optionally restart +- **Connection Loss:** Automatic WebSocket reconnection +- **Partial Updates:** Transaction-like file operations + +## Performance Considerations + +### Plugin + +- KV store operations are async +- WebSocket connection pooling +- Rate limiting on commands (prevent spam) + +### Bridge Server + +- Async I/O for file operations +- Stream large responses (don't buffer in memory) +- Connection limits (max concurrent sessions) +- SQLite WAL mode for better concurrency + +### Mattermost + +- Bot messages throttled (respect rate limits) +- Ephemeral messages don't persist (faster) +- Attachments for large content (not inline text) + +## Deployment Architecture + +### Development + +``` +[Developer Machine] + ├── Mattermost (Docker) + ├── Bridge Server (npm run dev) + └── Claude Code CLI (local install) +``` + +### Production (Single Server) + +``` +[Server] + ├── Mattermost Server + │ └── Claude Code Plugin + ├── Bridge Server (systemd/PM2) + └── Claude Code CLI +``` + +### Production (Distributed) + +``` +[Load Balancer] + │ + ├──► [Mattermost Server 1] + │ └── Plugin + │ + └──► [Mattermost Server 2] + └── Plugin + │ + ▼ + [Bridge Server Cluster] + ├── Instance 1 + ├── Instance 2 + └── Instance 3 + │ + ▼ + [Shared Database] +``` + +## Monitoring & Observability + +### Metrics (Bridge Server) + +- Session count (active/total) +- Message throughput (msg/sec) +- CLI spawn time (ms) +- Response latency (ms) +- Error rate + +### Logs + +- Plugin: Mattermost plugin logs +- Bridge: Winston/Pino structured logs +- CLI: Captured and persisted + +### Health Checks + +- Bridge: `GET /health` endpoint +- Plugin: Mattermost plugin health API +- CLI: Process existence check + +## Future Enhancements + +1. **Multi-User Sessions:** Multiple users collaborating in same session +2. **Session Sharing:** Share session across channels +3. **History Browsing:** Review past conversations +4. **Custom Actions:** User-defined interactive buttons +5. **Advanced File Editor:** Full IDE-like experience in dialogs +6. **Voice Input:** Speech-to-text for prompts +7. **Mobile Optimization:** Better mobile UI components +8. **Analytics Dashboard:** Usage statistics and insights + +## References + +- [Mattermost Plugin Architecture](https://developers.mattermost.com/integrate/plugins/) +- [WebSocket Protocol](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) +- [Claude Code GitHub](https://github.com/siteboon/claudecode) +- [Node.js Child Processes](https://nodejs.org/api/child_process.html) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..6d224ce --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,378 @@ +# Development Guide + +## Overview + +The Claude Code Mattermost Plugin integrates Claude Code AI assistant directly into Mattermost, providing a native chat-based development experience. + +## Architecture + +The project consists of three main components: + +1. **Mattermost Plugin** (Go backend + React frontend) + - Handles slash commands + - Manages interactive messages and dialogs + - Communicates with bridge server via REST and WebSocket + +2. **Bridge Server** (Node.js) + - Manages Claude Code CLI sessions + - Provides REST API for session management + - WebSocket server for real-time updates + - SQLite database for session persistence + +3. **Claude Code CLI** + - Actual AI assistant doing the work + - Spawned and controlled by bridge server + +## Development Setup + +### Prerequisites + +- **Go 1.21+** - Backend development +- **Node.js 22+** - Frontend and bridge server +- **Docker & Docker Compose** - Development environment +- **Make** - Build automation +- **Claude Code CLI** - AI assistant (optional for testing) + +### Clone and Install + +```bash +# Clone repository +git clone https://github.com/appsome/claude-code-mattermost-plugin.git +cd claude-code-mattermost-plugin + +# Install Go dependencies (automatic via go mod) +cd server && go mod download && cd .. + +# Install frontend dependencies +cd webapp && npm install && cd .. + +# Install bridge server dependencies +cd bridge-server && npm install && cd .. +``` + +### Development Environment + +Start Mattermost and dependencies with Docker Compose: + +```bash +make dev +``` + +This starts: +- Mattermost at http://localhost:8065 +- PostgreSQL database +- Default credentials: `admin@example.com` / `admin123` + +Stop the environment: + +```bash +make dev-down +``` + +### Building the Plugin + +```bash +# Build for current platform +make build + +# Build for all platforms (Linux, macOS, Windows) +make build-all + +# Create plugin bundle (.tar.gz) +make bundle + +# Create all platform bundles +make bundle-all +``` + +### Running Tests + +```bash +# Run all tests +make test + +# Run with coverage +make test-coverage + +# Backend tests only +cd server && go test -v ./... + +# Frontend tests only +cd webapp && npm test + +# Bridge server tests +cd bridge-server && npm test +``` + +### Code Style + +```bash +# Format code +make fmt + +# Lint code +make lint + +# Check style (format + lint) +make check-style +``` + +## Project Structure + +``` +claude-code-mattermost-plugin/ +├── server/ # Go backend +│ ├── plugin.go # Main plugin entry point +│ ├── commands.go # Slash command handlers +│ ├── session_manager.go # Session lifecycle management +│ ├── bridge_client.go # Bridge server REST client +│ ├── websocket_client.go # WebSocket connection +│ ├── thread_context.go # Thread context integration +│ ├── file_operations.go # File browsing and operations +│ └── configuration.go # Plugin configuration +├── webapp/ # React frontend +│ ├── src/ +│ │ ├── index.tsx # Plugin entry point +│ │ └── components/ # React components +│ └── package.json +├── bridge-server/ # Node.js bridge server +│ ├── src/ +│ │ ├── index.ts # Server entry point +│ │ ├── session-manager.ts +│ │ ├── cli-spawner.ts +│ │ ├── websocket-server.ts +│ │ └── api/ # REST API endpoints +│ └── package.json +├── plugin.json # Plugin manifest +├── Makefile # Build automation +├── docker-compose.yml # Development environment +└── docs/ # Documentation +``` + +## Plugin Development + +### Adding a New Slash Command + +1. **Register command** in `server/plugin.go`: + +```go +func (p *Plugin) OnActivate() error { + if err := p.API.RegisterCommand(&model.Command{ + Trigger: "my-command", + AutoComplete: true, + AutoCompleteDesc: "Description", + DisplayName: "My Command", + }); err != nil { + return err + } + return nil +} +``` + +2. **Handle command** in `server/commands.go`: + +```go +func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { + trigger := strings.TrimPrefix(args.Command, "/") + + switch trigger { + case "my-command": + return p.executeMyCommand(args), nil + default: + return respondEphemeral("Unknown command"), nil + } +} +``` + +### Adding Interactive Actions + +1. **Create action buttons**: + +```go +actions := []*model.PostAction{ + { + Name: "✅ Approve", + Integration: &model.PostActionIntegration{ + URL: fmt.Sprintf("%s/api/approve", p.getPluginURL()), + Context: map[string]interface{}{ + "change_id": changeID, + }, + }, + Style: "primary", + }, +} +``` + +2. **Handle action** in `ServeHTTP`: + +```go +func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/api/approve" { + p.handleApprove(w, r) + return + } +} +``` + +## Bridge Server Development + +### Adding API Endpoints + +Edit `bridge-server/src/api/index.ts`: + +```typescript +router.post('/sessions/:id/action', async (req, res) => { + const { id } = req.params; + const session = sessionManager.getSession(id); + + if (!session) { + return res.status(404).json({ error: 'Session not found' }); + } + + // Handle action + res.json({ success: true }); +}); +``` + +### WebSocket Messages + +Server → Plugin: + +```typescript +websocketServer.broadcast(sessionId, { + type: 'message', + content: 'Response from Claude', + timestamp: Date.now(), +}); +``` + +Plugin → Server: + +```go +p.wsClient.Send(websocket.Message{ + Type: "prompt", + SessionID: sessionID, + Content: userMessage, +}) +``` + +## Testing Guidelines + +### Unit Tests (Go) + +```go +func TestExecuteCommand(t *testing.T) { + plugin := setupTestPlugin(t) + + resp, err := plugin.ExecuteCommand(nil, &model.CommandArgs{ + Command: "/claude-help", + }) + + assert.Nil(t, err) + assert.Contains(t, resp.Text, "Claude Code commands") +} +``` + +### Integration Tests + +```go +func TestSessionLifecycle(t *testing.T) { + plugin := setupTestPlugin(t) + + // Start session + resp := plugin.executeClaudeStart(testArgs, "/tmp/project") + assert.NotNil(t, resp) + + // Verify active + session := plugin.GetActiveSession(testChannelID) + assert.NotNil(t, session) + + // Stop session + resp = plugin.executeClaudeStop(testArgs) + assert.Contains(t, resp.Text, "stopped") +} +``` + +## Debugging + +### Plugin Logs + +View Mattermost logs: + +```bash +docker-compose logs -f mattermost +``` + +Or from plugin code: + +```go +p.API.LogError("Error message", "key", value) +p.API.LogInfo("Info message") +p.API.LogDebug("Debug message") +``` + +### Bridge Server Logs + +```bash +cd bridge-server +npm run dev # Starts with debug logging +``` + +### Browser DevTools + +For React frontend debugging, use browser DevTools with source maps enabled. + +## Common Issues + +### Plugin Not Loading + +- Check Mattermost logs for errors +- Verify plugin.json has correct structure +- Ensure Go module dependencies are downloaded + +### Bridge Server Connection Failed + +- Verify bridge server is running +- Check plugin configuration for correct URL +- Test bridge health endpoint: `curl http://localhost:3002/health` + +### Commands Not Responding + +- Check slash command registration in logs +- Verify bot user was created +- Check channel permissions + +## Contributing + +1. Create feature branch: `git checkout -b feature/my-feature` +2. Make changes with tests +3. Run `make check-style` and `make test` +4. Commit with clear message +5. Push and create pull request +6. Assign reviewer: @suda + +## Useful Commands + +```bash +# Watch mode for frontend development +cd webapp && npm run dev + +# Bridge server with auto-reload +cd bridge-server && npm run dev + +# Run specific test +cd server && go test -v -run TestExecuteCommand + +# Build plugin and upload to Mattermost +make bundle && curl -F 'plugin=@dist/com.appsome.claudecode.tar.gz' \ + http://localhost:8065/api/v4/plugins \ + -H 'Authorization: Bearer YOUR_TOKEN' +``` + +## Resources + +- [Mattermost Plugin Documentation](https://developers.mattermost.com/integrate/plugins/) +- [Mattermost Plugin API Reference](https://developers.mattermost.com/integrate/plugins/server/reference/) +- [Claude Code Documentation](https://github.com/siteboon/claudecode) +- [Go Testing](https://go.dev/doc/tutorial/add-a-test) +- [React Testing Library](https://testing-library.com/react) diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 3c52302..8766e3c 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -2,194 +2,394 @@ ## Prerequisites -- Mattermost Server 9.0+ -- Claude Code CLI installed on the bridge server -- Node.js 22+ (for bridge server) -- Go 1.21+ (for building from source) +Before installing the Claude Code Mattermost Plugin, ensure you have: -## Method 1: Mattermost Marketplace (Recommended) +- **Mattermost Server** 9.0 or higher +- **System Admin** access to Mattermost +- **Claude Code CLI** installed on the bridge server machine +- **Node.js** 22+ (for bridge server) +- **Bridge Server** running (or ability to run it) + +## Installation Methods + +### Method 1: Via Mattermost Marketplace (Recommended) + +> **Note:** Marketplace listing pending approval. Manual installation required currently. 1. Log in to Mattermost as System Admin 2. Go to **System Console** → **Plugins** → **Marketplace** 3. Search for "Claude Code" 4. Click **Install** -5. Configure settings (see [Configuration](#configuration) section) -6. Enable the plugin +5. Enable the plugin +6. Configure settings (see Configuration section below) + +### Method 2: Manual Installation from Release -## Method 2: Manual Installation +#### Step 1: Download Plugin -### Download Latest Release +Download the latest release for your platform: ```bash # For Linux -wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/co.appsome.claudecode-linux-amd64.tar.gz +wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/com.appsome.claudecode-linux-amd64.tar.gz # For macOS -wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/co.appsome.claudecode-darwin-amd64.tar.gz +wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/com.appsome.claudecode-darwin-amd64.tar.gz # For Windows -wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/co.appsome.claudecode-windows-amd64.tar.gz +wget https://github.com/appsome/claude-code-mattermost-plugin/releases/latest/download/com.appsome.claudecode-windows-amd64.tar.gz ``` -### Upload Plugin +#### Step 2: Install Plugin in Mattermost -1. Go to **System Console** → **Plugins** → **Management** -2. Click **Upload Plugin** -3. Select the downloaded `.tar.gz` file -4. Click **Upload** -5. Enable the plugin +**Via System Console (Recommended):** -### Configuration +1. Log in to Mattermost as System Admin +2. Go to **System Console** → **Plugins** → **Management** +3. Click **Upload Plugin** +4. Select the downloaded `.tar.gz` file +5. Click **Upload** +6. Enable the plugin -1. Go to **System Console** → **Plugins** → **Claude Code** -2. Configure the following settings: +**Via CLI:** -| Setting | Description | Default | -|---------|-------------|---------| -| Bridge Server URL | URL of the Claude Code bridge server | `http://localhost:3002` | -| Claude Code CLI Path | Path to the Claude Code CLI executable | `/usr/local/bin/claude` | -| Enable File Operations | Allow users to browse and edit files via dialogs | `true` | +```bash +# On Mattermost server +sudo -u mattermost mattermost plugin add /path/to/com.appsome.claudecode.tar.gz +sudo -u mattermost mattermost plugin enable com.appsome.claudecode +``` +#### Step 3: Configure Plugin + +1. Go to **System Console** → **Plugins** → **Claude Code** +2. Configure the following settings: + - **Bridge Server URL**: `http://localhost:3002` (or your bridge server URL) + - **Claude Code CLI Path**: `/usr/local/bin/claude-code` (or your CLI path) + - **API Token**: Generate a secure token for authentication 3. Click **Save** ## Bridge Server Setup -The bridge server manages Claude Code CLI sessions and is required for the plugin to function. +The bridge server manages Claude Code CLI sessions and must be running for the plugin to work. + +### Option 1: Run with Node.js (Development/Single User) + +```bash +# Clone repository +git clone https://github.com/appsome/claude-code-mattermost-plugin.git +cd claude-code-mattermost-plugin/bridge-server + +# Install dependencies +npm install + +# Build +npm run build + +# Start server +npm start +``` + +The bridge server will start on `http://localhost:3002` by default. -### Option 1: Docker (Recommended) +### Option 2: Run with Docker (Production) ```bash -# Clone the repository -git clone https://github.com/appsome/claude-code-mattermost-plugin +# Clone repository +git clone https://github.com/appsome/claude-code-mattermost-plugin.git cd claude-code-mattermost-plugin -# Start the bridge server +# Build and start docker-compose -f docker-compose.prod.yml up -d ``` -The bridge server will be available at `http://localhost:3002`. +### Option 3: Run with systemd (Production) -### Option 2: Manual Setup +Create a systemd service file: ```bash -# Clone the repository -git clone https://github.com/appsome/claude-code-mattermost-plugin -cd claude-code-mattermost-plugin/bridge-server +sudo nano /etc/systemd/system/claude-code-bridge.service +``` -# Install dependencies -npm install +Add the following content: + +```ini +[Unit] +Description=Claude Code Bridge Server +After=network.target + +[Service] +Type=simple +User=mattermost +WorkingDirectory=/opt/claude-code-bridge +ExecStart=/usr/bin/node /opt/claude-code-bridge/dist/index.js +Restart=always +Environment=NODE_ENV=production +Environment=PORT=3002 +Environment=CLAUDE_CODE_PATH=/usr/local/bin/claude-code + +[Install] +WantedBy=multi-user.target +``` -# Build the server -npm run build +Enable and start the service: -# Start the server -npm start +```bash +sudo systemctl daemon-reload +sudo systemctl enable claude-code-bridge +sudo systemctl start claude-code-bridge +sudo systemctl status claude-code-bridge ``` -### Option 3: Using PM2 (Process Manager) +### Bridge Server Configuration + +Create a `.env` file in the bridge-server directory: ```bash -# Install PM2 globally -npm install -g pm2 +# Server Configuration +PORT=3002 +NODE_ENV=production -# Start the bridge server -cd bridge-server -npm run build -pm2 start dist/index.js --name claude-code-bridge +# Database +DATABASE_PATH=/var/lib/claude-code-bridge/sessions.db -# Enable startup script -pm2 startup -pm2 save +# Claude Code CLI +CLAUDE_CODE_PATH=/usr/local/bin/claude-code + +# Security +API_TOKEN=your-secure-token-here + +# Logging +LOG_LEVEL=info +``` + +## Claude Code CLI Installation + +The Claude Code CLI must be installed on the bridge server machine. + +### Installation + +```bash +# Install globally via npm +npm install -g claude-code + +# Verify installation +claude-code --version ``` -### Environment Variables +### Configuration -Configure the bridge server using environment variables: +Claude Code CLI requires API credentials. Set them up: -| Variable | Description | Default | -|----------|-------------|---------| -| `HOST` | Server bind address | `127.0.0.1` | -| `PORT` | Server port | `3002` | -| `DATABASE_PATH` | SQLite database path | `./data/sessions.db` | -| `CLAUDE_CODE_PATH` | Path to Claude Code CLI | `/usr/local/bin/claude` | -| `MAX_SESSIONS` | Maximum concurrent sessions | `100` | -| `SESSION_TIMEOUT_MS` | Session timeout in milliseconds | `3600000` (1 hour) | -| `LOG_LEVEL` | Logging level (debug, info, warn, error) | `info` | +```bash +# Configure Claude API key +claude-code config set apiKey YOUR_ANTHROPIC_API_KEY +``` + +See [Claude Code documentation](https://github.com/siteboon/claudecode) for more details. ## Verification -1. Open any Mattermost channel -2. Type `/claude help` -3. You should see the help message with available commands +### 1. Check Plugin Status + +In Mattermost: +1. Go to **System Console** → **Plugins** → **Management** +2. Verify "Claude Code" shows as **Enabled** +3. Check for any error messages + +### 2. Test Bridge Server + +```bash +curl http://localhost:3002/health +``` + +Expected response: +```json +{ + "status": "ok", + "version": "1.0.0", + "uptime": 123, + "sessions": 0, + "timestamp": "2024-03-09T12:00:00Z" +} +``` -### Available Commands +### 3. Test Slash Command -- `/claude ` - Send a message to Claude Code -- `/claude start` - Start a new Claude Code session -- `/claude stop` - Stop the current session -- `/claude status` - Check session status -- `/claude help` - Show help message +In any Mattermost channel: +1. Type `/claude-help` +2. You should see a help message from the bot +3. If it works, the plugin is properly configured! + +### 4. Start a Session + +``` +/claude-start /path/to/your/project +``` + +You should see a confirmation message from the bot. ## Troubleshooting -### Plugin fails to connect to bridge server +### Plugin Not Loading -1. Verify the bridge server is running: - ```bash - curl http://localhost:3002/health - ``` -2. Check the bridge server URL in plugin settings -3. Ensure there are no firewall rules blocking the connection +**Symptoms:** Plugin shows as disabled or doesn't appear in plugin list. -### Claude Code CLI not found +**Solutions:** +- Check Mattermost logs: `docker logs mattermost` or `/var/log/mattermost/mattermost.log` +- Verify plugin file was uploaded correctly +- Ensure Mattermost version is 9.0+ +- Check plugin manifest is valid: `tar -tzf plugin.tar.gz | head` -1. Verify Claude Code is installed: - ```bash - which claude - claude --version - ``` -2. Update the CLI path in plugin settings or bridge server environment +### Bridge Server Connection Failed -### Sessions not persisting +**Symptoms:** Commands fail with "Bridge server unavailable" error. -1. Check the database path is writable -2. For Docker, ensure the volume is mounted correctly +**Solutions:** +- Verify bridge server is running: `curl http://localhost:3002/health` +- Check bridge server logs +- Verify plugin configuration has correct bridge URL +- Check firewall rules (port 3002 must be accessible) +- For remote bridge server, use full URL: `http://bridge.example.com:3002` -## Updating +### Commands Not Responding -### Plugin Update +**Symptoms:** Slash commands don't trigger any response. -1. Download the latest release -2. Go to **System Console** → **Plugins** → **Management** -3. Upload the new version -4. The plugin will be automatically updated +**Solutions:** +- Check bot user was created (should happen automatically) +- Verify you have permission to use slash commands in the channel +- Check Mattermost logs for command registration errors +- Try `/claude-help` first to test basic functionality + +### Claude Code CLI Not Found -### Bridge Server Update +**Symptoms:** "Claude Code CLI not found" error from bridge server. -Using the update script: +**Solutions:** +- Verify CLI is installed: `which claude-code` +- Update plugin configuration with correct CLI path +- For systemd service, update `CLAUDE_CODE_PATH` environment variable +- Check CLI has execute permissions: `chmod +x /path/to/claude-code` + +### Session Won't Start + +**Symptoms:** `/claude-start` fails with error. + +**Solutions:** +- Verify project path exists and is accessible +- Check user running bridge server has read/write permissions on project +- Ensure Claude Code CLI is properly configured with API key +- Check bridge server logs for detailed error messages + +### WebSocket Connection Issues + +**Symptoms:** Messages don't stream in real-time. + +**Solutions:** +- Check WebSocket port (3002 by default) is not blocked +- Verify no proxy is interfering with WebSocket connections +- Check browser console for WebSocket errors (if using webapp components) +- Restart bridge server and plugin + +## Security Considerations + +### Production Deployment + +For production use: + +1. **Use HTTPS:** Configure Mattermost with TLS certificate +2. **Secure Bridge Server:** Use firewall to restrict access to bridge server port +3. **Authentication:** Use strong API token for bridge server authentication +4. **File Permissions:** Restrict project directories to necessary users only +5. **Regular Updates:** Keep plugin, bridge server, and CLI updated + +### Network Configuration + +**Internal Network (Recommended):** +``` +[Mattermost] --internal--> [Bridge Server] --local--> [Claude Code CLI] +``` +**Firewall Rules:** ```bash -./scripts/update-bridge.sh +# Allow Mattermost server to reach bridge server +sudo ufw allow from MATTERMOST_IP to any port 3002 + +# Block external access to bridge server +sudo ufw deny 3002 ``` -Or manually: +## Upgrading + +### Plugin Upgrade + +1. Download new version +2. Upload via System Console (same as installation) +3. Mattermost will handle the upgrade +4. Restart Mattermost if needed + +### Bridge Server Upgrade ```bash -cd bridge-server +cd claude-code-mattermost-plugin/bridge-server git pull origin main npm install npm run build -# Restart the server (method depends on how you're running it) +sudo systemctl restart claude-code-bridge ``` -## Security Considerations +### CLI Upgrade + +```bash +npm update -g claude-code +``` + +## Uninstallation + +### Remove Plugin + +1. **System Console** → **Plugins** → **Management** +2. Find "Claude Code" plugin +3. Click **Remove** +4. Confirm removal + +### Stop Bridge Server + +**systemd:** +```bash +sudo systemctl stop claude-code-bridge +sudo systemctl disable claude-code-bridge +sudo rm /etc/systemd/system/claude-code-bridge.service +``` + +**Docker:** +```bash +docker-compose -f docker-compose.prod.yml down +``` + +### Cleanup Data + +```bash +# Remove plugin data from Mattermost +# (Located in Mattermost data directory, varies by installation) + +# Remove bridge server data +rm -rf /var/lib/claude-code-bridge + +# Remove CLI configuration (optional) +rm -rf ~/.config/claude-code +``` + +## Getting Help + +- **GitHub Issues:** https://github.com/appsome/claude-code-mattermost-plugin/issues +- **Documentation:** https://github.com/appsome/claude-code-mattermost-plugin +- **Mattermost Community:** https://community.mattermost.com -- The bridge server should only be accessible from the Mattermost server -- Consider using HTTPS for production deployments -- Review and restrict Claude Code CLI permissions as needed -- The plugin respects Mattermost channel permissions +## Next Steps -## Support +After installation: -- [GitHub Issues](https://github.com/appsome/claude-code-mattermost-plugin/issues) -- [Documentation](https://github.com/appsome/claude-code-mattermost-plugin) +1. Read the [User Guide](USER_GUIDE.md) to learn how to use the plugin +2. Check [Architecture](ARCHITECTURE.md) to understand how it works +3. For development, see [Development Guide](DEVELOPMENT.md)