Convert a project's Claude Code configuration to the OpenCode standard — skills, subagents, slash commands, hooks, rules, permissions, and MCP servers — in one command. Works in reverse too (opencode2claude).
npx code2opencode # convert the current project in place
npx code2opencode --global # convert ~/.claude -> ~/.config/opencode
npx opencode2claude # convert an OpenCode project back to Claude Code| Claude Code source | OpenCode output | Notes |
|---|---|---|
.claude/skills/*/SKILL.md |
.opencode/skills/*/SKILL.md |
Names sanitized to ^[a-z0-9]+(-[a-z0-9]+)*$ (collisions get numeric suffixes); scripts/, references/, assets/ copied with exec bits preserved; Claude-only fields moved to metadata; overlong descriptions truncated to 1024 chars |
.claude/agents/**/*.md |
.opencode/agents/**/*.md |
mode: subagent; tools: Read, Grep list → { read: true, grep: true } map; model aliases mapped; mcp__server__tool → server_tool |
.claude/commands/**/*.md |
.opencode/commands/**/*.md |
Bodies pass through unchanged — $ARGUMENTS, $1…$n, !`shell`, and @file placeholders are identical in both tools; argument-hint folded into the description |
.claude/settings*.json hooks |
.opencode/plugin/claude-hooks.js |
Generated plugin scaffold: PreToolUse → tool.execute.before (exit code 2 still blocks the tool), PostToolUse → tool.execute.after, SessionStart → session.created, Stop → session.idle; commands get the familiar JSON payload on stdin. Unmappable events (UserPromptSubmit, Notification, …) are flagged for manual porting |
CLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md |
AGENTS.md |
If AGENTS.md already exists, rules are referenced via instructions in opencode.json; re-runs are idempotent |
.claude/settings*.json permissions + model |
opencode.json → permission, model |
Bash(npm run:*) → bash: { "npm run*": "allow" }; Edit/Write family → edit; WebFetch → webfetch |
.mcp.json |
opencode.json → mcp |
stdio → { type: "local", command: [...] }; http/sse → { type: "remote", url } |
Claude plugin bundles (--plugin <dir>) |
same as above | Converts a plugin's skills/, agents/, commands/, and hooks/hooks.json into the project |
An existing opencode.json is merged, never clobbered — your keys always win on conflict. After writing, output is validated (skill naming rules, required fields, permission effects, MCP shapes); validation problems are reported and make the CLI exit non-zero.
- Hook events with no OpenCode equivalent (
UserPromptSubmit,Notification,PreCompact, …) — the generated plugin lists them in a comment. envin settings.json (use your shell or a.envfile), status line, output styles, per-path edit specifiers, WebFetch domain filters.
code2opencode [project-dir] [options]
Options:
--out <dir> write output under a different root (default: in place)
--global convert ~/.claude -> ~/.config/opencode instead
--plugin <dir> also convert a Claude Code plugin bundle (repeatable)
--model-map <a=b> override a model alias mapping (repeatable)
--reverse convert the other way: OpenCode -> Claude Code
--report <file> write a markdown migration checklist to <file>
--json print the report as JSON instead of text
--no-validate skip post-conversion validation
--dry-run show what would be written without touching the diskTypical flow:
code2opencode ~/my-project --dry-run # preview the report
code2opencode ~/my-project --report MIGRATION.md # write + keep a checklistBuilt-in aliases: sonnet → anthropic/claude-sonnet-5, opus → anthropic/claude-opus-4-8, haiku → anthropic/claude-haiku-4-5. Explicit claude-* ids get the anthropic/ prefix; provider/model ids pass through. Override per run with --model-map sonnet=anthropic/whatever, or persistently with a .code2opencoderc.json in the project root:
{ "modelMap": { "sonnet": "anthropic/claude-sonnet-5#high" } }opencode2claude (or code2opencode --reverse) maps .opencode/{skills,agents,commands} → .claude/…, AGENTS.md → CLAUDE.md, and opencode.json permission/mcp → .claude/settings.json/.mcp.json. Lossy spots (per-agent temperature, permission, command agent:/subtask:) are reported as warnings. Legacy singular directories (.opencode/agent/, .opencode/command/) are read too.
This repo ships its own skill at .claude/skills/code2opencode/SKILL.md. Copy that directory into ~/.claude/skills/ (or keep it per-project) and Claude Code will run the converter for you when you ask it to "convert this project to OpenCode" — dry-run first, then apply, relaying every warning.
import { convert, reverseConvert } from 'code2opencode';
const { report } = convert({ src: '/path/to/project', dryRun: true });
console.log(report.toText()); // or report.toMarkdown() / report.toJSON()
// report.converted / report.skipped / report.warnings / report.errors- OpenCode can already read
.claude/skills/*/SKILL.mddirectly — converting is still useful for fully leaving Claude Code behind, for sanitizing non-conforming skill names, and for migrating everything skills can't cover (agents, commands, hooks, permissions, MCP, rules). - Requires Node 18+. The only runtime dependency is
yaml.
npm install
npm test # node --test, no test framework neededCI runs the suite on Node 18/20/22 (.github/workflows/test.yml).
MIT