Skip to main content

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. See era-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 to mcp__<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:

PathOwnershipContents
.claude/agents, .claude/commands, .claude/skills, .claude/era-sharedEra (symlinks)Links to the global ~/.era/era-code/claude/ directories
CLAUDE.mdSharedAn Era-managed marker block that @imports the constitution, DIRECTIVES.md, and the shared context files; everything outside the markers is yours
.mcp.jsonEraMCP servers in Claude Code format
.claude/settings.jsonEraPermissions (sensitive-file read denies) and governance/session hooks
.gitignoreEra sectionCovers 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: truedefaultMode: "default" (interactive prompts); falsedefaultMode: "acceptEdits"
  • Read denies for **/*.env, **/*.env.*, **/*.pem, **/*.key are always emitted
  • A PreToolUse hook on Bash runs era-code hook governance; a SessionStart hook runs era-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

CapabilityOpenCodeClaude Code
Subagents75 agents (3 primary + 72 subagents)72 subagents in .claude/agents
Primary agentsorchestration / plan selectable, Tab-cyclingNot ported — Claude Code's main agent and plan mode cover them
PersonasAuto-registered as primary agents on repo detectionSame files installed as subagents; opt-in via era-code claude --persona <name>
Slash commands21 in .opencode/commandSame 21 in .claude/commands; subagent-routed commands delegate via the Task tool
Skills17 in .opencode/skillSame 17 in .claude/skills (identical format)
Shared contextLoaded via opencode.jsonc instructions:@imports in the CLAUDE.md Era block
Governanceera-governance plugin (in-process)PreToolUse hook → era-code hook governance (same rule engine)
Directives rebuildOn era-code startOn launch and on every SessionStart hook
MCP configopencode.jsonc mcp: block, {env:VAR} interpolation.mcp.json, ${VAR} interpolation; disabled servers omitted (no per-server enabled flag)
Permissionsopencode.jsonc permission: block.claude/settings.json permissions (defaultMode + deny rules)
Claude Max authMeridian proxyNative — no proxy

Behavioral deltas

Governed behavior is equivalent, but the harnesses are not identical. The full set of known OpenCode ↔ Claude Code behavioral deltas:

DeltaOpenCodeClaude Code
Primary agentsorchestration, plan, and push-agent are selectable primaries (Tab cycling); personas auto-register as primariesNot 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 compressionera-compression plugin reinforces the doctrine per turn at runtimeStatic "Output discipline" section appended to each translated agent plus the shared output-compression.md import — no runtime reinforcement
.env.exampleReadable — an explicit allow override inside the *.env.* deny setDenied — Claude deny rules have no allow-override, so .env.example is caught by Read(**/*.env.*). Strictly safer
Auth tokensRefreshed per command via the era-auth plugin's shell.env hookExported once into the environment at launch — very long sessions can outlive the token TTL (relaunch to refresh)
Context handoverera-handover plugin tracks context-window usage and drives a two-phase handover toolNot ported — Claude Code has native context management
Bash output compressionrtk-optimizer plugin rewrites bash commands through RTK to compress shell outputNot ported
Claude Max authMeridian proxyNot needed — native Max auth
Disabled MCP serversEmitted with enabled: false in opencode.jsoncOmitted entirely from .mcp.json (no per-server enabled flag)
Governance scopeera-governance plugin no-ops when the project has no .era/ directory; audit_mode deny logging supportedThe era-code hook governance hook always evaluates (hardcoded and default denials apply even outside Era projects); audit-mode logging is not ported
Command routingCommands route to subagents via agent: frontmatterRouting 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 init checkbox, 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:

  1. Go to api.slack.com/apps and create a new app for your Slack workspace (internal apps may use MCP)
  2. In the app settings, enable the "Slack Model Context Protocol (MCP) Server" toggle (under Agents & AI Apps)
  3. Under "OAuth & Permissions", add the User Token Scopes you need (e.g. search:read.public, channels:history, chat:write)
  4. Install the app to your workspace and copy the User OAuth Token (starts with xoxp-)
  5. Enter the token when era-code init prompts for it, or export it as SLACK_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.