# OpenCode OpenCode is an open-source AI coding agent that operates as both a terminal user interface (TUI) and a headless server. It provides a provider-agnostic platform for AI-assisted coding, supporting models from Anthropic Claude, OpenAI, Google, and local models. The project features a client/server architecture that allows remote access via mobile apps or web interfaces while maintaining a rich TUI experience built by Neovim users and the creators of terminal.shop. The core functionality includes intelligent code editing with LSP support, multi-agent architecture (build agent for development work, plan agent for analysis), session management for conversations with AI assistants, and MCP (Model Context Protocol) integration. OpenCode ships as both an npm package (`opencode-ai`) and desktop applications for macOS, Windows, and Linux, making it accessible across all major platforms. ## CLI Installation and Basic Usage OpenCode can be installed via multiple package managers and run directly from the command line. ```bash # Install via npm/bun/pnpm/yarn npm i -g opencode-ai@latest # Install via Homebrew (macOS/Linux) brew install anomalyco/tap/opencode # Install via shell script curl -fsSL https://opencode.ai/install | bash # Custom installation directory OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash # Run OpenCode interactively (TUI mode) opencode # Run with a specific model opencode --model anthropic/claude-sonnet-4-20250514 # Run with a message directly opencode run "Explain the main function in src/index.ts" # Continue the last session opencode run --continue "What else can you improve?" # Run with a specific agent opencode run --agent plan "Analyze the codebase architecture" # Start headless server mode opencode serve --hostname 127.0.0.1 --port 4096 ``` ## SDK Client - Creating an OpenCode Instance The SDK provides a unified way to create both server and client instances programmatically. ```typescript import { createOpencode, createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk"; // Create both server and client together const { client, server } = await createOpencode({ hostname: "127.0.0.1", port: 4096, timeout: 5000, config: { logLevel: "INFO" } }); // Use the client to interact with sessions const sessions = await client.session.list(); console.log("Active sessions:", sessions.data); // When done, close the server server.close(); // Or create them separately for more control const server = await createOpencodeServer({ hostname: "127.0.0.1", port: 4096, signal: controller.signal // Optional AbortSignal for cleanup }); const client = createOpencodeClient({ baseUrl: server.url, headers: { "Authorization": "Basic " + btoa("opencode:your-password") } }); ``` ## SDK v2 Client - Session Management The v2 SDK provides a typed client for managing AI coding sessions with full type safety. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096", directory: "/path/to/project" }); // Create a new session const session = await client.session.create({ title: "Feature Implementation", permission: [ { permission: "edit", action: "allow", pattern: "*" }, { permission: "bash", action: "ask", pattern: "*" } ] }); // List all sessions sorted by most recently updated const allSessions = await client.session.list({ roots: true, // Only root sessions, not forks limit: 10, search: "feature" }); // Get session details const sessionInfo = await client.session.get({ sessionID: session.data.id }); // Fork a session at a specific message const forked = await client.session.fork({ sessionID: session.data.id, messageID: "msg_123" // Optional: fork from specific message }); // Archive a session await client.session.update({ sessionID: session.data.id, time: { archived: Date.now() } }); // Delete a session await client.session.delete({ sessionID: session.data.id }); ``` ## SDK v2 Client - Sending Messages and Prompts Send messages to AI assistants and receive streaming responses with tool execution. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // Create a session first const { data: session } = await client.session.create({ title: "Code Review" }); // Send a text prompt await client.session.prompt({ sessionID: session.id, model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" }, agent: "build", parts: [ { type: "text", text: "Review the authentication module and suggest improvements" } ] }); // Send prompt with file attachments await client.session.prompt({ sessionID: session.id, parts: [ { type: "text", text: "Analyze this code" }, { type: "file", url: "file:///path/to/file.ts", mime: "text/typescript", filename: "file.ts" } ] }); // Send async prompt (returns immediately) await client.session.promptAsync({ sessionID: session.id, parts: [{ type: "text", text: "Refactor the database layer" }] }); // Execute a command await client.session.command({ sessionID: session.id, command: "commit", arguments: "-m 'Fix authentication bug'" }); // Abort an active session await client.session.abort({ sessionID: session.id }); ``` ## SDK v2 Client - Event Subscription (Server-Sent Events) Subscribe to real-time events for session updates, tool execution, and message streaming. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // Subscribe to all events const events = await client.event.subscribe(); for await (const event of events.stream) { switch (event.type) { case "message.updated": console.log("Message updated:", event.properties.info); if (event.properties.info.role === "assistant") { console.log("Assistant response from:", event.properties.info.modelID); } break; case "message.part.updated": const part = event.properties.part; if (part.type === "text" && part.time?.end) { console.log("AI Response:", part.text); } if (part.type === "tool" && part.state.status === "completed") { console.log(`Tool ${part.tool} completed:`, part.state.output); } break; case "session.status": if (event.properties.status.type === "idle") { console.log("Session complete"); } break; case "permission.asked": console.log("Permission requested:", event.properties.permission); // Auto-approve or handle permission request await client.permission.reply({ requestID: event.properties.id, reply: "once" // "once" | "always" | "reject" }); break; case "session.error": console.error("Session error:", event.properties.error); break; } } ``` ## SDK v2 Client - Project and Configuration Management Manage projects, configuration settings, and AI provider authentication. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // Get current project const project = await client.project.current(); console.log("Project:", project.data.name, "at", project.data.worktree); // List all projects const projects = await client.project.list(); // Update project settings await client.project.update({ projectID: project.data.id, name: "My Project", icon: { color: "#3498db" }, commands: { start: "npm run dev" } }); // Initialize git in project await client.project.initGit(); // Get configuration const config = await client.config.get(); console.log("Current model:", config.data?.model); // Update configuration await client.config.update({ config: { model: "anthropic/claude-sonnet-4-20250514", permission: { edit: { "*": "allow" }, bash: { "*": "ask" } } } }); // List configured providers const providers = await client.config.providers(); providers.data?.forEach(p => { console.log(`Provider: ${p.id}, Default model: ${p.defaultModelID}`); }); // Get global configuration (system-wide) const globalConfig = await client.global.config.get(); // Set provider authentication await client.auth.set({ providerID: "anthropic", auth: { type: "api_key", key: process.env.ANTHROPIC_API_KEY } }); ``` ## SDK v2 Client - File Operations and Search Search files, read content, and get file status within the project context. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // List files in directory const files = await client.file.list({ path: "src" }); files.data?.forEach(f => console.log(f.name, f.type)); // Read file content const content = await client.file.read({ path: "src/index.ts" }); console.log("File content:", content.data?.content); // Get file status (git status, diagnostics) const status = await client.file.status({ path: "src/index.ts" }); console.log("Git status:", status.data?.git); console.log("LSP diagnostics:", status.data?.diagnostics); // Find files by glob pattern const tsFiles = await client.find.files({ pattern: "**/*.ts" }); tsFiles.data?.forEach(f => console.log("Found:", f)); // Search text in files const matches = await client.find.text({ pattern: "function.*authenticate", path: "src", glob: "*.ts" }); matches.data?.forEach(m => console.log(`${m.file}:${m.line}: ${m.text}`)); // Find symbols (functions, classes, etc.) const symbols = await client.find.symbols({ query: "User", kind: "class" }); symbols.data?.forEach(s => console.log(`${s.name} at ${s.location.file}:${s.location.line}`)); // Get session diff (file changes from a message) const diff = await client.session.diff({ sessionID: "session_123", messageID: "msg_456" }); diff.data?.forEach(d => { console.log(`${d.file}: +${d.additions} -${d.deletions}`); }); ``` ## SDK v2 Client - MCP Server Management Connect and manage Model Context Protocol (MCP) servers for extended functionality. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // Get MCP server status const status = await client.mcp.status(); status.data?.forEach(server => { console.log(`Server: ${server.name}, Connected: ${server.connected}`); }); // Add a local MCP server await client.mcp.add({ mcpConfig: { type: "local", name: "my-mcp-server", command: "node", args: ["/path/to/server.js"], env: { DEBUG: "true" } } }); // Add a remote MCP server await client.mcp.add({ mcpConfig: { type: "remote", name: "remote-server", url: "https://mcp.example.com/sse" } }); // Connect to an MCP server await client.mcp.connect({ serverID: "my-mcp-server" }); // Disconnect from an MCP server await client.mcp.disconnect({ serverID: "my-mcp-server" }); // Get available MCP resources const resources = await client.experimental.resource.list(); resources.data?.forEach(r => { console.log(`Resource: ${r.name} from ${r.server}`); }); // Handle MCP authentication const authUrl = await client.mcp.auth.start({ serverID: "oauth-server", callbackUrl: "http://localhost:3000/callback" }); // Redirect user to authUrl.data.url for OAuth flow ``` ## SDK v2 Client - PTY (Pseudo-Terminal) Management Create and manage interactive terminal sessions programmatically. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // List all PTY sessions const ptySessions = await client.pty.list(); ptySessions.data?.forEach(p => console.log(`PTY ${p.id}: ${p.title}`)); // Create a new PTY session const pty = await client.pty.create({ command: "/bin/bash", args: ["-l"], cwd: "/path/to/project", title: "Development Shell", env: { TERM: "xterm-256color" } }); // Get PTY session details const ptyInfo = await client.pty.get({ ptyID: pty.data.id }); // Update PTY session (resize terminal) await client.pty.update({ ptyID: pty.data.id, title: "Build Terminal", size: { rows: 24, cols: 80 } }); // Connect to PTY session (WebSocket) const connection = await client.pty.connect({ ptyID: pty.data.id }); // Handle WebSocket messages for terminal I/O // Remove PTY session await client.pty.remove({ ptyID: pty.data.id }); ``` ## SDK v2 Client - Permission and Question Handling Handle permission requests and interactive questions from the AI assistant. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // List pending permission requests const permissions = await client.permission.list(); permissions.data?.forEach(p => { console.log(`Permission: ${p.permission} for patterns: ${p.patterns.join(", ")}`); }); // Reply to a permission request await client.permission.reply({ requestID: "perm_123", reply: "always", // "once" | "always" | "reject" message: "Approved for all files" // Optional message }); // List pending questions const questions = await client.question.list(); questions.data?.forEach(q => { console.log(`Question: ${q.question}`); q.options.forEach((opt, i) => console.log(` ${i}: ${opt.label}`)); }); // Reply to a question await client.question.reply({ questionID: "q_123", answer: { selectedOptions: ["option1", "option2"], customInput: "Additional context" } }); // Reject a question await client.question.reject({ questionID: "q_123" }); ``` ## SDK v2 Client - Session Sharing and History Share sessions and manage message history with revert capabilities. ```typescript import { createOpencodeClient } from "@opencode-ai/sdk/v2"; const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }); // Share a session (create shareable link) const shared = await client.session.share({ sessionID: "session_123" }); console.log("Share URL:", shared.data?.share?.url); // Unshare a session await client.session.unshare({ sessionID: "session_123" }); // Get session messages const messages = await client.session.messages({ sessionID: "session_123", limit: 50, before: "msg_456" // Pagination cursor }); messages.data?.forEach(msg => { if (msg.role === "user") { console.log("User:", msg.id); } else { console.log("Assistant:", msg.modelID, "Cost:", msg.cost); console.log("Tokens:", msg.tokens.input, "in /", msg.tokens.output, "out"); } }); // Get specific message const message = await client.session.message({ sessionID: "session_123", messageID: "msg_456" }); // Revert a message (undo file changes) await client.session.revert({ sessionID: "session_123", messageID: "msg_456" }); // Restore reverted messages await client.session.unrevert({ sessionID: "session_123" }); // Summarize session (AI compaction) await client.session.summarize({ sessionID: "session_123", providerID: "anthropic", modelID: "claude-sonnet-4-20250514", auto: true }); // Get session todos const todos = await client.session.todo({ sessionID: "session_123" }); todos.data?.forEach(t => console.log(`[${t.status}] ${t.content}`)); ``` ## Configuration File Structure OpenCode uses JSON/JSONC configuration files with a hierarchical precedence system. ```jsonc // opencode.jsonc - Project or global configuration { "$schema": "https://opencode.ai/config.json", // Default model to use "model": "anthropic/claude-sonnet-4-20250514", // Provider configurations "provider": { "anthropic": { "disabled": false }, "openai": { "models": { "gpt-4o": { "disabled": false } } }, "openrouter": { "models": { "anthropic/claude-3.5-sonnet": {} } } }, // Permission rules "permission": { "edit": { "*": "allow" }, "bash": { "*": "ask", "git *": "allow", "npm *": "allow" }, "read": { "*": "allow", "*.env": "ask" } }, // Custom agents "agent": { "reviewer": { "name": "reviewer", "description": "Code review specialist", "mode": "primary", "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" }, "prompt": "You are a code reviewer. Focus on security and performance.", "permission": { "edit": { "*": "deny" } } } }, // Custom commands "command": { "review": { "description": "Review code changes", "prompt": "Review the staged changes and provide feedback" } }, // MCP servers "mcp": { "filesystem": { "type": "local", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed"] } }, // Additional instructions for all prompts "instructions": [ "Always use TypeScript for new files", "Follow the project's existing code style" ], // LSP server configurations "lsp": { "typescript-language-server": { "command": "typescript-language-server", "args": ["--stdio"] } }, // Session sharing settings "share": "auto", // "auto" | "manual" | "disabled" // Plugins to load "plugin": ["@opencode/plugin-git", "./local-plugin.ts"] } ``` ## Server API - Running Headless Server Start OpenCode as a headless HTTP server for remote access and integration. ```bash # Start server with default settings opencode serve # Start server with custom host and port opencode serve --hostname 0.0.0.0 --port 8080 # Server with authentication (set environment variable) OPENCODE_SERVER_PASSWORD=mysecretpassword opencode serve # Connect to remote server via CLI opencode run --attach http://localhost:4096 --password mysecretpassword "Hello" # Health check endpoint curl http://localhost:4096/global/health # Response: {"version":"1.3.0","status":"ok"} # Get configuration curl http://localhost:4096/config # List sessions curl http://localhost:4096/session # Create session curl -X POST http://localhost:4096/session \ -H "Content-Type: application/json" \ -d '{"title":"API Session"}' # Send message to session curl -X POST http://localhost:4096/session/{sessionID}/message \ -H "Content-Type: application/json" \ -d '{ "parts": [{"type":"text","text":"Explain this codebase"}], "model": {"providerID":"anthropic","modelID":"claude-sonnet-4-20250514"} }' # Subscribe to events (Server-Sent Events) curl http://localhost:4096/event ``` ## TUI Client - Launching Interactive Terminal Launch the TUI client programmatically with specific configurations. ```typescript import { createOpencodeTui } from "@opencode-ai/sdk/server"; // Launch TUI with options const tui = createOpencodeTui({ project: "/path/to/project", model: "anthropic/claude-sonnet-4-20250514", session: "session_123", // Resume specific session agent: "build", config: { permission: { edit: { "*": "allow" } } } }); // Handle cleanup process.on("SIGINT", () => { tui.close(); }); ``` ## Built-in Tools - Available AI Tools OpenCode provides a comprehensive set of built-in tools for the AI assistant. ```typescript // Tools available to AI agents: // Bash - Execute shell commands // Parameters: command, timeout?, workdir?, description // Example: { command: "npm test", timeout: 60000, description: "Run tests" } // Edit - Modify file content with diff // Parameters: filePath, oldString, newString, replaceAll? // Example: { filePath: "/src/app.ts", oldString: "const x = 1", newString: "const x = 2" } // Write - Create or overwrite files // Parameters: filePath, content // Example: { filePath: "/src/new.ts", content: "export const hello = 'world'" } // Read - Read file content // Parameters: filePath, offset?, limit? // Example: { filePath: "/src/app.ts", limit: 100 } // Glob - Find files by pattern // Parameters: pattern, path? // Example: { pattern: "**/*.ts", path: "src" } // Grep - Search file content // Parameters: pattern, path?, glob?, output_mode? // Example: { pattern: "TODO:", glob: "*.ts", output_mode: "content" } // Task - Launch subagent for complex tasks // Parameters: prompt, description, subagent_type // Example: { prompt: "Find all API endpoints", subagent_type: "explore" } // WebFetch - Fetch and process web content // Parameters: url, prompt // Example: { url: "https://example.com/docs", prompt: "Summarize the API" } // LSP - Language Server Protocol operations // Parameters: operation, filePath, line, character // Example: { operation: "goToDefinition", filePath: "/src/app.ts", line: 10, character: 15 } // TodoWrite - Manage task list // Parameters: todos[] // Example: { todos: [{ content: "Fix bug", status: "in_progress", activeForm: "Fixing bug" }] } ``` ## Agents Configuration OpenCode supports multiple agents with different capabilities and permission sets. ```typescript // Built-in agents: // "build" - Default agent for development work // - Full access to edit, bash, and all tools // - Can enter/exit plan mode // - Permissions follow user configuration // "plan" - Read-only agent for analysis // - Denies edit tools by default // - Good for code exploration and planning // - Can only edit files in .opencode/plans/ // "general" - Subagent for complex parallel tasks // - Used internally by primary agents // - Invoked via @general in messages // - No todo list access // "explore" - Fast read-only subagent // - Only grep, glob, list, read, bash // - Used for codebase exploration // - No file modification capabilities // Custom agent in opencode.jsonc: { "agent": { "security-reviewer": { "name": "security-reviewer", "description": "Security-focused code reviewer", "mode": "primary", "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" }, "temperature": 0.3, "prompt": "You are a security expert. Review code for vulnerabilities.", "permission": { "edit": { "*": "deny" }, "bash": { "*": "deny" } } } } } ``` OpenCode is designed for developers who want full control over their AI coding assistant while maintaining the flexibility to switch between providers and models. The client/server architecture makes it ideal for remote development scenarios, CI/CD integration, and building custom AI-powered development tools. The comprehensive SDK enables deep integration with existing workflows, while the TUI provides an immediate, powerful interface for interactive coding sessions. The project's emphasis on provider agnosticism ensures long-term viability as the AI landscape evolves, while LSP support and MCP integration provide extensibility for specialized development environments. Whether used as a standalone TUI, embedded in custom applications via the SDK, or deployed as a headless server for team collaboration, OpenCode offers a robust foundation for AI-assisted software development.