Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
# opencode-claude-bridge
# opencode-claude-bridge (mortonbaker fork)

Use your Claude Pro/Max subscription in [OpenCode](https://opencode.ai). If you're logged into the Claude CLI, it just works — no extra setup.

## Fork delta — slim pantheon support

This fork patches `claude-tools.ts` so Claude (as orchestrator) can spawn
[`oh-my-opencode-slim`](https://github.com/alvinunreal/oh-my-opencode-slim)
subagents (e.g. `@explorer`, `@fixer`, `@oracle` — typically bound to
MiniMax M2.7-highspeed for cost efficiency).

**Why it was needed:** upstream's Agent-tool description hardcodes the four
Claude Code SDK built-ins (`general-purpose`/`statusline-setup`/`Explore`/`Plan`).
Claude self-restricts to those because that's all the description tells it
exists. The slim plugin's `task` tool registers more roles, but the bridge
never told Claude about them.

**What the patch does:** at module load, the bridge reads
`~/.config/opencode/oh-my-opencode-slim.json`, finds the active preset, and
injects each non-orchestrator role into the Agent-tool description with its
model binding. Inbound tool-arg translation already passes unknown
`subagent_type` values through unchanged, so no other change is needed.

**Fallback:** if the slim config is absent or malformed, the description
falls back to the original (four built-ins only). Upstream behavior preserved.

Source of the change: `src/claude-tools.ts` — `readSlimSubagentRoles()` and
`buildAgentDescription()`.

---

## Install

Add to `~/.config/opencode/opencode.json`:
Expand Down
83 changes: 82 additions & 1 deletion src/claude-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/

import { createHash } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { CLI_VERSION } from "./constants.js";

// ── Types ──────────────────────────────────────────────────────────
Expand Down Expand Up @@ -343,16 +346,94 @@ export function translateArgsOpencodeToClaude(
return out;
}

// ── Dynamic Agent-Description (oh-my-opencode-slim integration) ────

/**
* Read the active oh-my-opencode-slim preset's subagent roles (excluding
* `orchestrator`, which is the caller itself) and return them as Agent-tool
* description lines.
*
* If the slim config is missing/malformed, returns "" so the Agent tool
* description falls back to its built-in (Claude Code SDK) types only.
*
* Why this exists: without it, Claude only knows about general-purpose /
* statusline-setup / Explore / Plan from its hardcoded description, and
* therefore never emits subagent_type values for the slim pantheon — even
* though the inbound translation layer would forward them unchanged to
* opencode's task tool. See README.md § "Slim pantheon support".
*/
function readSlimSubagentRoles(): string {
try {
const configPath = join(
homedir(),
".config/opencode/oh-my-opencode-slim.json",
);
if (!existsSync(configPath)) return "";
const raw = readFileSync(configPath, "utf8");
const config = JSON.parse(raw) as {
preset?: string;
presets?: Record<string, Record<string, { model?: string }>>;
};
const presetName = config.preset;
if (!presetName) return "";
const preset = config.presets?.[presetName];
if (!preset || typeof preset !== "object") return "";
const lines: string[] = [];
for (const [role, def] of Object.entries(preset)) {
if (role === "orchestrator") continue; // orchestrator is the caller
const model = def?.model ?? "(unknown model)";
lines.push(
`- ${role}: oh-my-opencode-slim specialist running on \`${model}\`. ` +
`Routes through opencode's task tool. Pass \`subagent_type: "${role}"\`.`,
);
}
return lines.join("\n");
} catch {
return "";
}
}

/**
* Inject slim-pantheon roles into the captured Agent-tool description so
* Claude knows they exist. The injection point is right after the last
* built-in (`- Plan: ...`) entry and before the `\n\nWhen using the Agent
* tool` paragraph that follows the bullet list.
*
* Falls back to the unmodified description if either the slim config is
* absent or the marker isn't found (e.g. upstream description changed).
*/
function buildAgentDescription(base: string): string {
const slim = readSlimSubagentRoles();
if (!slim) return base;
// Marker is the end of the Plan bullet (last built-in) immediately followed
// by the section break that precedes the usage prose. If the upstream
// string ever shifts, we silently fall back to `base`.
const marker = "NotebookEdit)\n\nWhen using the Agent tool";
if (!base.includes(marker)) return base;
return base.replace(
marker,
`NotebookEdit)\n${slim}\n\nWhen using the Agent tool`,
);
}

// ── Claude Code Tool Definitions (wire-captured) ───────────────────

/**
* Captured Agent-tool description from Claude Code 2.1.98. Held out as a
* const so {@link buildAgentDescription} can splice slim-pantheon roles
* into the agent-types bullet list at module load.
*/
const AGENT_TOOL_BASE_DESCRIPTION =
"Launch a new agent to handle complex, multi-step tasks. Each agent type has specific capabilities and tools available to it.\n\nAvailable agent types and the tools they have access to:\n- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)\n- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)\n- Explore: Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. \"src/components/**/*.tsx\"), search code for keywords (eg. \"API endpoints\"), or answer questions about the codebase (eg. \"how do API endpoints work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"very thorough\" for comprehensive analysis across multiple locations and naming conventions. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)\n- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)\n\nWhen using the Agent tool, specify a subagent_type parameter to select which agent type to use. If omitted, the general-purpose agent is used.\n\n## When not to use\n\nIf the target is already known, use the direct tool: Read for a known path, the Grep tool for a specific symbol or string. Reserve this tool for open-ended questions that span the codebase, or tasks that match an available agent type.\n\n## Usage notes\n\n- Always include a short description summarizing what the agent will do\n- Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses\n- When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.\n- You can optionally run agents in the background using the run_in_background parameter. When an agent runs in the background, you will be automatically notified when it completes — do NOT sleep, poll, or proactively check on its progress. Continue with other work or respond to the user instead.\n- **Foreground vs background**: Use foreground (default) when you need the agent's results before you can proceed — e.g., research agents whose findings inform your next steps. Use background when you have genuinely independent work to do in parallel.\n- To continue a previously spawned agent, use SendMessage with the agent's ID or name as the `to` field — that resumes it with full context. A new Agent call starts a fresh agent with no memory of prior runs, so the prompt must be self-contained.\n- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent\n- If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first.\n- If the user specifies that they want you to run agents \"in parallel\", you MUST send a single message with multiple Agent tool use content blocks. For example, if you need to launch both a build-validator agent and a test-runner agent in parallel, send a single message with both tool calls.\n- With `isolation: \"worktree\"`, the worktree is automatically cleaned up if the agent makes no changes; otherwise the path and branch are returned in the result.\n\n## Writing the prompt\n\nBrief the agent like a smart colleague who just walked into the room — it hasn't seen this conversation, doesn't know what you've tried, doesn't understand why this task matters.\n- Explain what you're trying to accomplish and why.\n- Describe what you've already learned or ruled out.\n- Give enough context about the surrounding problem that the agent can make judgment calls rather than just following a narrow instruction.\n- If you need a short response, say so (\"report in under 200 words\").\n- Lookups: hand over the exact command. Investigations: hand over the question — prescribed steps become dead weight when the premise is wrong.\n\nTerse command-style prompts produce shallow, generic work.\n\n**Never delegate understanding.** Don't write \"based on your findings, fix the bug\" or \"based on the research, implement it.\" Those phrases push synthesis onto the agent instead of doing it yourself. Write prompts that prove you understood: include file paths, line numbers, what specifically to change.\n\nExample usage:\n\n<example>\nuser: \"What's left on this branch before we can ship?\"\nassistant: <thinking>A survey question across git state, tests, and config. I'll delegate it and ask for a short report so the raw command output stays out of my context.</thinking>\nAgent({\n description: \"Branch ship-readiness audit\",\n prompt: \"Audit what's left before this branch can ship. Check: uncommitted changes, commits ahead of main, whether tests exist, whether the GrowthBook gate is wired up, whether CI-relevant files changed. Report a punch list — done vs. missing. Under 200 words.\"\n})\n<commentary>\nThe prompt is self-contained: it states the goal, lists what to check, and caps the response length. The agent's report comes back as the tool result; relay the findings to the user.\n</commentary>\n</example>\n\n<example>\nuser: \"Can you get a second opinion on whether this migration is safe?\"\nassistant: <thinking>I'll ask the code-reviewer agent — it won't see my analysis, so it can give an independent read.</thinking>\nAgent({\n description: \"Independent migration review\",\n subagent_type: \"code-reviewer\",\n prompt: \"Review migration 0042_user_schema.sql for safety. Context: we're adding a NOT NULL column to a 50M-row table. Existing rows get a backfill default. I want a second opinion on whether the backfill approach is safe under concurrent writes — I've checked locking behavior but want independent verification. Report: is this safe, and if not, what specifically breaks?\"\n})\n<commentary>\nThe agent starts with no context from this conversation, so the prompt briefs it: what to assess, the relevant background, and what form the answer should take.\n</commentary>\n</example>\n";

/**
* The 10 shared tools that exist in both Claude Code and OpenCode,
* using Claude Code's exact wire definitions.
*/
const SHARED_TOOLS: ToolDefinition[] = [
{
name: "Agent",
description: "Launch a new agent to handle complex, multi-step tasks. Each agent type has specific capabilities and tools available to it.\n\nAvailable agent types and the tools they have access to:\n- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)\n- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)\n- Explore: Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. \"src/components/**/*.tsx\"), search code for keywords (eg. \"API endpoints\"), or answer questions about the codebase (eg. \"how do API endpoints work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"very thorough\" for comprehensive analysis across multiple locations and naming conventions. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)\n- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)\n\nWhen using the Agent tool, specify a subagent_type parameter to select which agent type to use. If omitted, the general-purpose agent is used.\n\n## When not to use\n\nIf the target is already known, use the direct tool: Read for a known path, the Grep tool for a specific symbol or string. Reserve this tool for open-ended questions that span the codebase, or tasks that match an available agent type.\n\n## Usage notes\n\n- Always include a short description summarizing what the agent will do\n- Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses\n- When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.\n- You can optionally run agents in the background using the run_in_background parameter. When an agent runs in the background, you will be automatically notified when it completes \u2014 do NOT sleep, poll, or proactively check on its progress. Continue with other work or respond to the user instead.\n- **Foreground vs background**: Use foreground (default) when you need the agent's results before you can proceed \u2014 e.g., research agents whose findings inform your next steps. Use background when you have genuinely independent work to do in parallel.\n- To continue a previously spawned agent, use SendMessage with the agent's ID or name as the `to` field \u2014 that resumes it with full context. A new Agent call starts a fresh agent with no memory of prior runs, so the prompt must be self-contained.\n- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent\n- If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first.\n- If the user specifies that they want you to run agents \"in parallel\", you MUST send a single message with multiple Agent tool use content blocks. For example, if you need to launch both a build-validator agent and a test-runner agent in parallel, send a single message with both tool calls.\n- With `isolation: \"worktree\"`, the worktree is automatically cleaned up if the agent makes no changes; otherwise the path and branch are returned in the result.\n\n## Writing the prompt\n\nBrief the agent like a smart colleague who just walked into the room \u2014 it hasn't seen this conversation, doesn't know what you've tried, doesn't understand why this task matters.\n- Explain what you're trying to accomplish and why.\n- Describe what you've already learned or ruled out.\n- Give enough context about the surrounding problem that the agent can make judgment calls rather than just following a narrow instruction.\n- If you need a short response, say so (\"report in under 200 words\").\n- Lookups: hand over the exact command. Investigations: hand over the question \u2014 prescribed steps become dead weight when the premise is wrong.\n\nTerse command-style prompts produce shallow, generic work.\n\n**Never delegate understanding.** Don't write \"based on your findings, fix the bug\" or \"based on the research, implement it.\" Those phrases push synthesis onto the agent instead of doing it yourself. Write prompts that prove you understood: include file paths, line numbers, what specifically to change.\n\nExample usage:\n\n<example>\nuser: \"What's left on this branch before we can ship?\"\nassistant: <thinking>A survey question across git state, tests, and config. I'll delegate it and ask for a short report so the raw command output stays out of my context.</thinking>\nAgent({\n description: \"Branch ship-readiness audit\",\n prompt: \"Audit what's left before this branch can ship. Check: uncommitted changes, commits ahead of main, whether tests exist, whether the GrowthBook gate is wired up, whether CI-relevant files changed. Report a punch list \u2014 done vs. missing. Under 200 words.\"\n})\n<commentary>\nThe prompt is self-contained: it states the goal, lists what to check, and caps the response length. The agent's report comes back as the tool result; relay the findings to the user.\n</commentary>\n</example>\n\n<example>\nuser: \"Can you get a second opinion on whether this migration is safe?\"\nassistant: <thinking>I'll ask the code-reviewer agent \u2014 it won't see my analysis, so it can give an independent read.</thinking>\nAgent({\n description: \"Independent migration review\",\n subagent_type: \"code-reviewer\",\n prompt: \"Review migration 0042_user_schema.sql for safety. Context: we're adding a NOT NULL column to a 50M-row table. Existing rows get a backfill default. I want a second opinion on whether the backfill approach is safe under concurrent writes \u2014 I've checked locking behavior but want independent verification. Report: is this safe, and if not, what specifically breaks?\"\n})\n<commentary>\nThe agent starts with no context from this conversation, so the prompt briefs it: what to assess, the relevant background, and what form the answer should take.\n</commentary>\n</example>\n",
description: buildAgentDescription(AGENT_TOOL_BASE_DESCRIPTION),
input_schema: {
"$schema": "https://json-schema.org/draft/2020-12/schema",
type: "object",
Expand Down