Skip to content

nagaoo0/code2opencode

Repository files navigation

code2opencode

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

What it converts

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__toolserver_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: PreToolUsetool.execute.before (exit code 2 still blocks the tool), PostToolUsetool.execute.after, SessionStartsession.created, Stopsession.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.jsonpermission, model Bash(npm run:*)bash: { "npm run*": "allow" }; Edit/Write family → edit; WebFetchwebfetch
.mcp.json opencode.jsonmcp 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.

What still needs a human (reported, never silent)

  • Hook events with no OpenCode equivalent (UserPromptSubmit, Notification, PreCompact, …) — the generated plugin lists them in a comment.
  • env in settings.json (use your shell or a .env file), status line, output styles, per-path edit specifiers, WebFetch domain filters.

Usage

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 disk

Typical flow:

code2opencode ~/my-project --dry-run                 # preview the report
code2opencode ~/my-project --report MIGRATION.md    # write + keep a checklist

Model mapping

Built-in aliases: sonnetanthropic/claude-sonnet-5, opusanthropic/claude-opus-4-8, haikuanthropic/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" } }

Reverse direction

opencode2claude (or code2opencode --reverse) maps .opencode/{skills,agents,commands}.claude/…, AGENTS.mdCLAUDE.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.

Use it as a Claude Code skill

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.

Programmatic API

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

Notes

  • OpenCode can already read .claude/skills/*/SKILL.md directly — 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.

Development

npm install
npm test        # node --test, no test framework needed

CI runs the suite on Node 18/20/22 (.github/workflows/test.yml).

License

MIT

About

Convert a project's Claude Code configuration to the OpenCode

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages