Coding Agents & IDEsDocumentedScanned

opencode-acp-control

Control OpenCode directly via the Agent Client Protocol (ACP).

Share:

Installation

npx clawhub@latest install opencode-acp-control

View the full skill documentation and source below.

Documentation

OpenCode ACP Skill

Control OpenCode directly via the Agent Client Protocol (ACP).

Metadata

  • For ACP Protocol Docs (for Agents/LLMs):
  • GitHub Repo:
  • If you have issues with this skill, please open an issue ticket here:

Quick Reference

ActionHow
Start OpenCodebash(command: "opencode acp", background: true)
Send messageprocess.write(sessionId, data: "\n")
Read responseprocess.poll(sessionId) - repeat every 2 seconds
Stop OpenCodeprocess.kill(sessionId)
List sessionsbash(command: "opencode session list", workdir: "...")
Resume sessionList sessions → ask user → session/load
Check versionbash(command: "opencode --version")

Starting OpenCode

bash(
  command: "opencode acp",
  background: true,
  workdir: "/path/to/your/project"
)

Save the returned sessionId - you'll need it for all subsequent commands.

Protocol Basics

  • All messages are JSON-RPC 2.0 format
  • Messages are newline-delimited (end each with \n)
  • Maintain a message ID counter starting at 0

Step-by-Step Workflow

Step 1: Initialize Connection

Send immediately after starting OpenCode:

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":true},"clientInfo":{"name":"clawdbot","title":"Clawdbot","version":"1.0.0"}}}

Poll for response. Expect result.protocolVersion: 1.

Step 2: Create Session

{"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/path/to/project","mcpServers":[]}}

Poll for response. Save result.sessionId (e.g., "sess_abc123").

Step 3: Send Prompts

{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"sess_abc123","prompt":[{"type":"text","text":"Your question here"}]}}

Poll every 2 seconds. You'll receive:

  • session/update notifications (streaming content)

  • Final response with result.stopReason


Step 4: Read Responses

Each poll may return multiple lines. Parse each line as JSON:

  • Notifications: method: "session/update" - collect these for the response
  • Response: Has id matching your request - stop polling when stopReason appears

Step 5: Cancel (if needed)

{"jsonrpc":"2.0","method":"session/cancel","params":{"sessionId":"sess_abc123"}}

No response expected - this is a notification.

State to Track

Per OpenCode instance, track:

  • processSessionId - from bash tool (clawdbot's process ID)

  • opencodeSessionId - from session/new response (OpenCode's session ID)

  • messageId - increment for each request you send


Polling Strategy

  • Poll every 2 seconds
  • Continue until you receive a response with stopReason
  • Max wait: 5 minutes (150 polls)
  • If no response, consider the operation timed out

Common Stop Reasons

stopReasonMeaning
end_turnAgent finished responding
cancelledYou cancelled the prompt
max_tokensToken limit reached

Error Handling

IssueSolution
Empty poll responseKeep polling - agent is thinking
Parse errorSkip malformed line, continue
Process exitedRestart OpenCode
No response after 5minKill process, start fresh

Example: Complete Interaction

1. bash(command: "opencode acp", background: true, workdir: "/home/user/myproject")
   -> processSessionId: "bg_42"

2. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":0,"method":"initialize",...}\n')
   process.poll(sessionId: "bg_42") -> initialize response

3. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/home/user/myproject","mcpServers":[]}}\n')
   process.poll(sessionId: "bg_42") -> opencodeSessionId: "sess_xyz789"

4. process.write(sessionId: "bg_42", data: '{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"sess_xyz789","prompt":[{"type":"text","text":"List all TypeScript files"}]}}\n')
   
5. process.poll(sessionId: "bg_42") every 2 sec until stopReason
   -> Collect all session/update content
   -> Final response: stopReason: "end_turn"

6. When done: process.kill(sessionId: "bg_42")

Resume Session

Resume a previous OpenCode session by letting the user choose from available sessions.

Step 1: List Available Sessions

bash(command: "opencode session list", workdir: "/path/to/project")

Example output:

ID                                  Updated              Messages
ses_451cd8ae0ffegNQsh59nuM3VVy      2026-01-11 15:30     12
ses_451a89e63ffea2TQIpnDGtJBkS      2026-01-10 09:15     5
ses_4518e90d0ffeJIpOFI3t3Jd23Q      2026-01-09 14:22     8

Step 2: Ask User to Choose

Present the list to the user and ask which session to resume:

"Which session would you like to resume?
 
1. ses_451cd8ae... (12 messages, updated 2026-01-11)
2. ses_451a89e6... (5 messages, updated 2026-01-10)
3. ses_4518e90d... (8 messages, updated 2026-01-09)

Enter session number or ID:"

Step 3: Load Selected Session

Once user responds (e.g., "1", "the first one", or "ses_451cd8ae..."):

  • Start OpenCode ACP:

  • bash(command: "opencode acp", background: true, workdir: "/path/to/project")

  • Initialize:

  • {"jsonrpc":"2.0","id":0,"method":"initialize","params":{...}}

  • Load the session:

  • {"jsonrpc":"2.0","id":1,"method":"session/load","params":{"sessionId":"ses_451cd8ae0ffegNQsh59nuM3VVy","cwd":"/path/to/project","mcpServers":[]}}

    Note: session/load requires cwd and mcpServers parameters.

    On load, OpenCode streams the full conversation history back to you.

    Resume Workflow Summary

    function resumeSession(workdir):
        # List available sessions
        output = bash("opencode session list", workdir: workdir)
        sessions = parseSessionList(output)
        
        if sessions.empty:
            notify("No previous sessions found. Starting fresh.")
            return createNewSession(workdir)
        
        # Ask user to choose
        choice = askUser("Which session to resume?", sessions)
        selectedId = matchUserChoice(choice, sessions)
        
        # Start OpenCode and load session
        process = bash("opencode acp", background: true, workdir: workdir)
        initialize(process)
        
        session_load(process, selectedId, workdir, mcpServers: [])
        
        notify("Session resumed. Conversation history loaded.")
        return process

    Important Notes

    • History replay: On load, all previous messages stream back
    • Memory preserved: Agent remembers the full conversation
    • Process independent: Sessions survive OpenCode restarts

    Updating OpenCode

    OpenCode auto-updates when restarted. Use this workflow to check and trigger updates.

    Step 1: Check Current Version

    bash(command: "opencode --version")

    Returns something like: opencode version 1.1.13

    Extract the version number (e.g., 1.1.13).

    Step 2: Check Latest Version

    webfetch(url: "", format: "text")

    The redirect URL contains the latest version tag:

    • Redirects to: - Extract version from the URL path (e.g., 1.2.0) ### Step 3: Compare and Update If latest version > current version: 1. **Stop all running OpenCode processes**: __CODE_BLOCK_15__ 2. **Restart instances** (OpenCode auto-downloads new binary on start): __CODE_BLOCK_16__ 3. **Re-initialize** each instance (initialize + session/load for existing sessions) ### Step 4: Verify Update __CODE_BLOCK_17__ If version still doesn't match latest: - Inform user: "OpenCode auto-update may have failed. Current: X.X.X, Latest: Y.Y.Y" - Suggest manual update: curl -fsSL | bash ### Update Workflow Summary __CODE_BLOCK_18__ ### Important Notes - **Sessions persist**: opencodeSessionId survives restarts — use session/load` to recover

    • Auto-update: OpenCode downloads new binary automatically on restart

    • No data loss: Conversation history is preserved server-side