Using Era Code with Claude Code
Era Code supports two AI coding harnesses: OpenCode (launched with era-code start) and Claude Code (launched with era-code claude). Both run the same governance, agents, commands, skills, and MCP integrations — Era maintains a single OpenCode-format template source and translates it into Claude Code format automatically.
Global Setup vs Per-Repo Init
There are two ways to wire Era into Claude Code, and they layer rather than compete:
era-code setup(recommended, run once per machine) installs the full Era baseline at user scope — agents, skills, commands, MCP servers, permissions, governance hooks, and the constitution + universal directives — into~/.claude/and~/.config/opencode/, plus global governance memory at~/.era/memory/. After this, every repo inherits Era with no per-repo step. Seeera-code setup.era-code init(optional, per repo) writes a project.era/directory with project-specific directives and syncs the project surface (.claude/,CLAUDE.md,.mcp.json).
Claude Code natively merges user-scope and project-scope config, with project config taking precedence. So per-repo init directives override the global baseline where they overlap. Run setup once for the org-wide baseline; run init only when a repo needs its own directives.
The sections below describe the project-scope surface that era-code claude syncs. The global-scope equivalents installed by era-code setup mirror the same layout under ~/.claude/ (per-item symlinks, a CLAUDE.md governance block with absolute @imports, and merged settings.json).
What Gets Installed Where
Global resources
Every era-code command keeps ~/.era/era-code/ up to date, gated on the CLI version (a no-op when the installed version matches). Alongside the existing opencode/ tree, the same template source now installs a translated Claude tree:
~/.era/era-code/claude/
├── agents/ # 72 subagents (incl. 20 personas), translated from the OpenCode canon
├── commands/ # 21 slash commands
├── skills/ # 17 skills (SKILL.md + references/)
└── _shared/ # 5 shared context files (ethos, output compression, Linear workflow, ...)
The translation:
- Agents — OpenCode tool flags become Claude Code
tools:allowlists for read-only agents; agents with any write/exec tool (or no tools block) inherit all tools. MCP tool keys translate tomcp__<server>__<tool>form. Agents declaring an Era compression profile get an explicit "Output discipline" section appended. - Commands — OpenCode
agent:/subtask:routing frontmatter is dropped; commands that routed to a subagent gain a preamble delegating the work to that subagent via the Task tool. - Skills — same
SKILL.md+references/layout in both harnesses; copied through with frontmatter validation. - Primary agents are not ported — see Behavioral deltas.
Project files
era-code claude syncs these project files on every launch:
| Path | Ownership | Contents |
|---|---|---|
.claude/agents, .claude/commands, .claude/skills, .claude/era-shared | Era (symlinks) | Links to the global ~/.era/era-code/claude/ directories |
CLAUDE.md | Shared | An Era-managed marker block that @imports the constitution, DIRECTIVES.md, and the shared context files; everything outside the markers is yours |
.mcp.json | Era | MCP servers in Claude Code format |
.claude/settings.json | Era | Permissions (sensitive-file read denies) and governance/session hooks |
.gitignore | Era section | Covers the generated Claude files |
User files in .claude/ (e.g. settings.local.json) are never touched. If a real, non-empty file or directory occupies an Era-managed path, Era warns and skips it rather than deleting your content. All era-generated Claude paths are gitignored; CLAUDE.md stays committed so the governance imports travel with the repo.
Permissions and hooks
.claude/settings.json is derived from your Era governance settings:
require_permissions: true→defaultMode: "default"(interactive prompts);false→defaultMode: "acceptEdits"- Read denies for
**/*.env,**/*.env.*,**/*.pem,**/*.keyare always emitted - A
PreToolUsehook on Bash runsera-code hook governance; aSessionStarthook runsera-code hook session-start(directives recompile)
The governance hook is a faithful port of the OpenCode era-governance plugin's rule engine — hardcoded safety denials, default GitOps/IaC denials, .era/governance.yaml overrides, and compound-command tokenization. Hooks fail open: a broken hook never blocks the session. See the claude command page for details.
Feature Mapping
| Capability | OpenCode | Claude Code |
|---|---|---|
| Subagents | 75 agents (3 primary + 72 subagents) | 72 subagents in .claude/agents |
| Primary agents | orchestration / plan selectable, Tab-cycling | Not ported — Claude Code's main agent and plan mode cover them |
| Personas | Auto-registered as primary agents on repo detection | Same files installed as subagents; opt-in via era-code claude --persona <name> |
| Slash commands | 21 in .opencode/command | Same 21 in .claude/commands; subagent-routed commands delegate via the Task tool |
| Skills | 17 in .opencode/skill | Same 17 in .claude/skills (identical format) |
| Shared context | Loaded via opencode.jsonc instructions: | @imports in the CLAUDE.md Era block |
| Governance | era-governance plugin (in-process) | PreToolUse hook → era-code hook governance (same rule engine) |
| Directives rebuild | On era-code start | On launch and on every SessionStart hook |
| MCP config | opencode.jsonc mcp: block, {env:VAR} interpolation | .mcp.json, ${VAR} interpolation; disabled servers omitted (no per-server enabled flag) |
| Permissions | opencode.jsonc permission: block | .claude/settings.json permissions (defaultMode + deny rules) |
| Claude Max auth | Meridian proxy | Native — no proxy |
Behavioral deltas
Governed behavior is equivalent, but the harnesses are not identical. The full set of known OpenCode ↔ Claude Code behavioral deltas:
| Delta | OpenCode | Claude Code |
|---|---|---|
| Primary agents | orchestration, plan, and push-agent are selectable primaries (Tab cycling); personas auto-register as primaries | Not ported — Claude Code's built-in main agent and plan mode cover them. Personas stay subagents; era-code claude --persona <name> injects one at launch, with no mid-session Tab cycling |
| Output compression | era-compression plugin reinforces the doctrine per turn at runtime | Static "Output discipline" section appended to each translated agent plus the shared output-compression.md import — no runtime reinforcement |
.env.example | Readable — an explicit allow override inside the *.env.* deny set | Denied — Claude deny rules have no allow-override, so .env.example is caught by Read(**/*.env.*). Strictly safer |
| Auth tokens | Refreshed per command via the era-auth plugin's shell.env hook | Exported once into the environment at launch — very long sessions can outlive the token TTL (relaunch to refresh) |
| Context handover | era-handover plugin tracks context-window usage and drives a two-phase handover tool | Not ported — Claude Code has native context management |
| Bash output compression | rtk-optimizer plugin rewrites bash commands through RTK to compress shell output | Not ported |
| Claude Max auth | Meridian proxy | Not needed — native Max auth |
| Disabled MCP servers | Emitted with enabled: false in opencode.jsonc | Omitted entirely from .mcp.json (no per-server enabled flag) |
| Governance scope | era-governance plugin no-ops when the project has no .era/ directory; audit_mode deny logging supported | The era-code hook governance hook always evaluates (hardcoded and default denials apply even outside Era projects); audit-mode logging is not ported |
| Command routing | Commands route to subagents via agent: frontmatter | Routing frontmatter is dropped; a delegation preamble in the command body delegates to the subagent via the Task tool |
Baseline MCP Servers
Linear, Notion Docs Reader, and Slack are baseline servers — the connections every Era engineer is expected to have. For both harnesses they are:
- Shown in the
era-code initcheckbox, checked by default, with the normal auth/token prompting - Force-re-added to existing project manifests when the CLI upgrades, so they appear on the next launch without re-running init
- Gracefully disabled with a one-line warning at launch when the credential is missing (instead of failing on every session):
Warning: Slack MCP server disabled — no credential found (set SLACK_USER_TOKEN or run "era-code init" to connect)
The manifest is not modified when a server is degraded — provide the token (re-run era-code init or export the env var) and the next launch re-enables it.
Slack setup
Slack uses the official Slack-hosted MCP server (https://mcp.slack.com/mcp), bridged over stdio with mcp-remote. It is token-only: the Slack MCP endpoint requires a confidential OAuth client, so a generic MCP client cannot complete a browser OAuth flow — you mint a user token instead:
- Go to api.slack.com/apps and create a new app for your Slack workspace (internal apps may use MCP)
- In the app settings, enable the "Slack Model Context Protocol (MCP) Server" toggle (under Agents & AI Apps)
- Under "OAuth & Permissions", add the User Token Scopes you need (e.g.
search:read.public,channels:history,chat:write) - Install the app to your workspace and copy the User OAuth Token (starts with
xoxp-) - Enter the token when
era-code initprompts for it, or export it asSLACK_USER_TOKEN
See the MCP Servers guide for Linear and Notion setup.
Choosing a Harness
Both harnesses are first-class and share one template source, so governed behavior is equivalent. Practical differences:
- Claude Code authenticates with Claude Max natively (no Meridian proxy) and brings its own main agent and plan mode.
- OpenCode offers Era's primary agents (
orchestration,plan) with Tab cycling and plugin-enforced governance.
You can switch freely — era-code start and era-code claude each sync their own harness config from the same .era/ source of truth.