Skip to main content

Claude Code and OpenCode: A 90-Minute Crash Course

15 Concepts, 80% of Real Use

A practical crash course. No filler, no upsell. By the end you'll know what to use, when to use it, and where to look when you need more, in either tool.

The single insight that makes everything else click: agentic coding is a context-management problem wearing the costume of a coding tool. Almost every "advanced technique" reduces to the same thing: getting the right information into the model at the right time, and keeping the wrong information out. Read each section through that lens.

Prerequisite: AI Prompting in 2026. This page assumes those thirteen concepts are already in your hands. Briefing like a colleague (concept 1), context as the whole game (concept 4), thinking mode (concept 5), neutral framing (concept 6), the iterate loop (concept 7), models checking models (concept 13): that's the discipline. This page is what the discipline looks like when the model can touch your filesystem.

Claude Code and OpenCode are two implementations of the same idea, with mostly the same vocabulary and slightly different keybinds.

Two tools, not one. On purpose. The discipline here has to outlive any single tool. Pricing changes, access restrictions, model preferences, and strategic shifts shouldn't strand the lessons you've learned. Showing each concept in both tools is also the strongest test of whether the concept is real: if a technique only works in Claude Code, it's a Claude Code trick; if it works in both (same shape, different keybinds), it's a piece of how agentic coding actually behaves. Reach for Claude Code when frontier model performance is the constraint; reach for OpenCode when flexibility, cost control, or openness is. OpenCode also comes with a set of free models that you can use without creating an account. The skills you build either way are the same. That's the point, not a compromise.

Current as of May 2026. Both tools ship fast: config schemas, command names, and install commands change. If you don't have the tools yet, installation instructions are on those same docs pages: Claude Code, OpenCode. When in doubt about anything else, those pages are also where to start.

Both tools update frequently; therefore, before you start any session, give the commands claude update and opencode upgrade to use the latest version.

Assumed background: you're comfortable on a command line, you've used git, and you've read or written JSON config before. You don't need prior experience with LLM coding agents. That's what this is for. If you're brand new to MCP, hooks, or the protocol layer between LLMs and external tools, you'll pick those up from context as we go.

Pick your tool, the page follows

Throughout this page, sections that diverge between Claude Code and OpenCode have a switcher. Pick one and every switcher on the page syncs to it; your choice persists across visits.

Want the deep version of any concept on this page?

This is a crash course — 80% of real use in one read. For the full treatment of every topic below (rules-file hierarchy, plan mode at depth, skill anatomy, subagent orchestration, hook events, CI/CD, session management), see Chapter 14: Working with General & Coding Agents and Chapter 17: Claude Code for Teams, CI/CD & Advanced Configuration. The crash course is the map; those chapters are the terrain.

There's a complete worked example in Part 6: one realistic task end-to-end, run twice, once in each tool. If you learn better from watching than from reading definitions, jump there first and come back.


Part 1: Foundations

These first three concepts apply identically in both tools. Where commands or keybinds differ, the difference is called out inline.

1. What these tools actually are

Most people's mental model is "a chatbot that knows code." That's wrong, and the wrongness is the problem.

A chatbot answers questions. Claude Code and OpenCode take actions. They read your files, edit them, run commands on your machine, hit the network, and chain all of that together until a task is done. You give them a brief, they work, you review.

The mindset shift: stop typing questions, start writing briefs. "How do I add auth?" is a chatbot prompt. "Add email/password auth to this Express app using bcrypt and JWT, store users in the existing Postgres users table, write tests, don't touch the OAuth code" is an agentic-coding prompt. The second one will work; the first one will produce a wandering monologue.

This is concept 1 of AI Prompting (novice vs. power user) with files instead of PDFs. Same shape of brief; higher stakes, because the model acts on it.

The two tools differ in defaults but not in fundamentals. Claude Code is Anthropic's, ships with Claude models, polished out of the box. OpenCode is open-source, model-agnostic (Claude, GPT, Gemini, local), and configurable down to the tool-level permission. Pick one based on your model preferences and how much config control you want; the rest of this crash course works in both.

Don't have either installed yet? Grab the canonical one-liner before you read any further; the sections after this assume you can press a key and see something happen. Auth, IDE plugins, and troubleshooting live on the official docs.

# macOS / Linux / WSL — recommended (auto-updates)
curl -fsSL https://claude.ai/install.sh | bash

# Windows PowerShell
irm https://claude.ai/install.ps1 | iex

# macOS Homebrew (no auto-update — run `brew upgrade claude-code` periodically)
brew install --cask claude-code

# npm fallback
npm install -g @anthropic-ai/claude-code

Full reference: code.claude.com/docs.

Once installed, open a terminal in any folder you want to work in and type claude (or opencode). That drops you into your first session. From here on, every concept in this page is something you can try in that session.

2. Plan mode (the most underused feature)

The implementations differ slightly but the idea is identical: before letting the model write anything, force it to produce a written plan you can review.

Press Shift+Tab to cycle through permission modes. The first press puts you in auto-accept; the second press puts you in plan mode. In plan mode, the model can read but cannot write: no file edits, no shell commands.

Either way, the move slows you down for two reasons that are actually speedups:

  1. It catches misunderstandings before they cost you. The plan is a contract. If the model misread the brief, you see it in the plan, not in a 200-line diff you have to revert.
  2. It produces better code on the second pass. A pre-written plan compresses the relevant context (file paths, function names, intended approach) into a clean artifact that the implementation phase can lean on. Without a plan, the model is reasoning and writing simultaneously, and reasoning loses.

Rule of thumb: anything more than a 10-minute change goes through plan mode first. For larger features, ask the model to save the plan to a markdown file (docs/plans/feature-x.md) so you can resume it later or feed it to a fresh session.

Plan mode is two prompting concepts in one move: think hard (concept 5) with a contract attached, and outline before drafting (concept 7) with a filesystem behind it. Edit the plan, not the diff.

3. Permissions discipline

Both tools ask for approval before taking action. Both let you skip approvals globally: Claude Code has --dangerously-skip-permissions, OpenCode lets you set "permission": "allow". Don't, at least not at first.

The pattern that works: start manual for a few sessions, notice which actions are safe enough to auto-approve, then write that down in config.

.claude/settings.json:

{
"permissions": {
"allow": [
"Read",
"Edit",
"Write",
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npm run build)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)"
],
"deny": ["Bash(rm -rf *)", "Bash(npm publish *)", "Bash(git push *)"]
}
}

Then install a notification helper. The pattern that makes agentic coding feel fast isn't faster execution: it's the ability to walk away. Set off a long task, switch to other work, come back when you're pinged. If you run multiple sessions in parallel (a real thing, and a real productivity unlock), notifications are how you keep track of which one needs you.

Claude Code has community helpers like cc-notify. OpenCode's desktop app sends system notifications natively; for the TUI, write a plugin that subscribes to session.idle and shells out to osascript (macOS) or notify-send (Linux).


Part 2: Context management

A session's context is a stack, not a bucket. Five layers from system prompt and rules file (always on, fixed at session start) through conversation history, referenced files, and active skills (loaded on demand). Subagents run with their own isolated context window. Every layer is paid for, every turn.

If you internalize only one section of this crash course, make it this one. Everything here applies identically in both tools, only the command names occasionally differ.

This is concept 4 of AI Prompting (context is the whole game) with the volume turned up. There, bad context cost you a worse answer. Here, it costs you a worse answer and a worse codebase. Same discipline, compounding consequences.

4. Context rot is real

The context window is the amount of text the model can hold at once. Modern models have huge windows: hundreds of thousands of tokens, sometimes a million. That sounds limitless. It isn't.

Two things to understand:

Real usage fills the window faster than you'd think. A few code files, a stack trace, some docs you pasted in, and a 30-turn conversation will eat tens of thousands of tokens before you notice. Your laptop doesn't show a fuel gauge.

Recall degrades long before the window is full. Studies (and your own experience, if you pay attention) show that as token counts climb, the model gets noticeably worse at remembering specific details from earlier in the context. The model doesn't forget exactly. It just stops weighting that information correctly. Symptoms: it ignores a constraint you stated 20 messages ago, repeats work it already did, hallucinates a function signature it had right earlier.

There's also a financial dimension. Every message re-bills the entire context window: a 50K-token conversation pinged twenty times during a debug session is a million tokens of input, charged each turn. On per-token APIs (most OpenCode setups, raw Anthropic API) that's wallet damage; on subscription tiers (Claude Code Pro/Max) it shows up as rate-limit hits. Either way, bloated context drains your budget right when you most need to keep working.

The takeaway is unromantic but practical: less context, used deliberately, beats more context dumped in hope.

Recall quality stays sharp from ~5K to ~20K tokens, solid through ~60K, then drifts from ~60–150K and is lost above 150K. The window stretches further than recall does; fix bloat before you hit the wall.

5. /clear and /compact

Two commands, two situations.

Wipe the conversation and start fresh. Use when switching to an unrelated task. Old context is irrelevant noise that will only confuse the new task.

  • Claude Code: /clear
  • OpenCode: /new (also aliased as /clear)

Summarize the conversation, keep the summary, discard the rest. Use when deep in a single long task and the context is getting bloated, but you can't lose the thread.

  • Both tools: /compact (in OpenCode, also aliased as /summarize)

You can guide what to keep in the compact: /compact keep the API contract decisions and current file paths.

These are not interchangeable. /clear is "new conversation." /compact is "same conversation, less baggage." Picking the wrong one either loses important context or keeps useless context.

6. Resume sessions

Conversations are saved. Both tools let you resume, same idea, different command:

  • Claude Code: claude --resume and pick a session from the list.
  • OpenCode: /sessions (also aliased as /resume) inside the TUI to switch between saved sessions.

Two patterns where this matters:

  • Pick up tomorrow. Long features rarely fit in one sitting. Resume lets you stop without losing the model's working memory of the project.
  • Run multiple branches. Resume any session, not just the last. You can have one session deep on a backend task and another working on the frontend, and switch between them.

If you have a saved plan file (docs/plans/feature-x.md), even a fresh session can be brought up to speed in one message: "read docs/plans/feature-x.md and continue from step 4." Resume is faster, but the plan file is the safety net. (For the full pattern — when to resume, when to fork, when to recover after a bad turn — see Chapter 17 § Session Management: Resume, Fork, and Recovery.)

Rewind exists in both, and both can roll back file changes. In Claude Code, press Esc twice (double-tap Esc), or run /rewind, to open a list of your previous user messages; pick one and you choose what to restore: code, conversation, or both. This works because Claude Code checkpoints the model's file edits before every prompt. In OpenCode, /undo rewinds the last user message and reverts the file changes from that turn; /redo reverses the last /undo. It uses git under the hood, so your project must be a git repo. The one real difference: Claude Code's checkpointing tracks the model's file edits but not files changed by bash commands (an rm or mv the model ran is not checkpointed); OpenCode's git-backed /undo, because it diffs the whole working tree, catches bash-driven changes too. Both rescue you from a bad prompt; reach for OpenCode's /undo specifically when the bad turn included shell commands that touched files.

Quick decision rule for the three commands:

Three command cards. /clear or /new (blue) when the task changes: wipes everything, no undo. /compact (green) for same task with less baggage: summarizes, then keeps only the summary; guide it with '/compact keep X'. /undo (purple, OpenCode only) for a bad turn: rewinds the message AND file changes via git. Reversible.

Diagnostic: when context goes bad

Context rot has visible symptoms. Watch for any of these:

  • The model starts apologizing repeatedly without making progress.
  • It rewrites the same block of code with no meaningful changes.
  • It references variables, files, or functions that don't exist.
  • It contradicts a constraint you stated earlier in the same session.
  • Each response gets longer, vaguer, and more deferential.

When you see this, stop typing. The instinct to "just one more clarifying prompt" is wrong; that just adds more polluted context to a context that's already polluted. The right move is to reset and re-brief. Run /compact if the conversation has a useful core to preserve, /clear (or OpenCode's /new) if you should genuinely start over. Five minutes of resetting beats an hour of arguing with a model whose context is poisoned.

Diagnostic: when context costs spike

Cost discipline is context discipline; you just feel one of them in your wallet. Every turn re-bills the whole context, so the same bloat that hurts recall also hurts your bill. Five symptoms cover most of the surprise bills you'll run into, regardless of which provider you're using:

Symptom: token usage spiked suddenly mid-session
→ Cause: rules file or system prompt changed, breaking cache hits.
Fix: revert the change, or accept the cache miss as a one-time cost.

Symptom: each turn costs noticeably more than the previous one
→ Cause: context is growing without bound. Files keep getting read in,
exploration output is accumulating, conversation is long.
Fix: /compact with a "keep architectural decisions and current
file paths" hint.

Symptom: model is over-explaining, producing walls of text
→ Cause: reasoning effort is too high for the task; or the prompt
invites narration.
Fix: turn the reasoning effort down; ask for "code only,
minimal commentary."

Symptom: monthly bill is much higher than expected
→ Cause: running a frontier-tier model on tasks an economy-tier
model would handle correctly.
Fix: route mechanical work (codemods, tests, formatting) to the
cheaper tier; reserve the frontier model for planning and
hard reasoning. See Part 8 Pattern 1 for the split.

Symptom: cache hits suddenly dropped from ~70% to ~10%
→ Cause: the system prompt, CLAUDE.md, or the first user message
changed structure. Cache matches prefixes byte-for-byte.
Fix: stabilize what comes first; let the variable content
come later.

Most are recoverable in a single config change. The mechanic underneath all five is the same one Part 2 has been arguing: stable, narrow context is cheaper and recalls better than sprawling context, every turn.


Part 3: The rules file

7. CLAUDE.md / AGENTS.md, done well

Both tools load a project-root markdown file into context at the start of every session. It's the closest thing to a project-level system prompt.

Don't write this file from scratch. In a fresh project, run /init in either tool. The model scans your folder and drafts the file for you. Your job after that is to ruthlessly delete the parts you don't need: this is an editing task, not an authoring task. Everything below is about what to keep and what to cut. (Beginner-level walkthrough: Chapter 14 § CLAUDE.md and AGENTS.md. Advanced — multi-file hierarchy, glob-scoped rules, team conventions: Chapter 17 § The CLAUDE.md Configuration Hierarchy.)

Claude Code uses CLAUDE.md. OpenCode uses AGENTS.md. For migrators, OpenCode also reads CLAUDE.md as a fallback when no AGENTS.md is present; if both files exist, AGENTS.md wins and CLAUDE.md is ignored. If you've already maintained a CLAUDE.md, OpenCode will pick it up unless you set OPENCODE_DISABLE_CLAUDE_CODE=1 to opt out entirely. For partial disable, OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1 skips only the rules-file fallback and OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 skips only the skills fallback. This is the single biggest portability win between the two tools.

The mistake nearly everyone makes is treating this file like documentation, stuffing in architecture overviews, full coding standards, and every quirk of the codebase. The result is a 20,000-token file that eats your context budget on every task, including tasks where 90% of it is irrelevant. Remember from Part 2: anything in this file is paid for on every single message, whether or not it's relevant.

The right model is table of contents, not encyclopedia. Aim for under ~2,500 tokens. Reference deeper context that loads on demand:

CLAUDE.md. The @filename syntax auto-loads referenced files when relevant:

# Project: my-app

## Stack

Next.js 14, TypeScript, Postgres, Drizzle ORM.

## Commands

- `npm run dev`: start local server
- `npm test`: run vitest
- `npm run db:migrate`: apply migrations

## Critical rules

- Never edit files in `src/generated/`. They're rebuilt by codegen.
- All API routes use the auth middleware in `src/lib/auth.ts`.
- See @docs/conventions.md for naming and folder rules.
- See @docs/db-schema.md for table structure.

The same shape works for non-software projects. A consultant writing reports, a teacher building lesson plans, a small-business owner running operations: a rules file is "table of contents, not encyclopedia" regardless of domain. Example for a writing/content project (CLAUDE.md or AGENTS.md, same shape):

# Project: blog-and-newsletter

## What this is

A folder of drafts, research, and published posts. I write a weekly newsletter and occasional long-form posts.

## Where things live

- `drafts/`: in-progress posts, one per file
- `research/`: source notes and clippings, organized by topic
- `published/`: shipped posts (do not edit)

## Critical rules

- Never edit anything in `published/`. Fixes go in a new draft with a correction note.
- Footnotes go in `[brackets]` inline; we resolve them to numbered footnotes only at publish time.
- Tone: conversational, no bullet lists in body copy.

Same principles as the my-app version: tight, declarative, only the things the model could not infer from looking at the folder.

For each line you're tempted to add, ask: "if I delete this, will the model make a mistake it wouldn't otherwise make?" If no, delete it. Don't tell the model to use TypeScript: it can see the .ts files. Tell it the things it can't infer: the codegen folder, the auth pattern, the things that have bitten you before.

This file should grow from real failures, not from imagined ones. Commit it to git. Let your team add to it.

Two ways to write a rules file. The Encyclopedia approach (red, ~18,000 tokens) crams architecture, full coding standards, git conventions, and testing philosophy into one file: paid for on every message, mostly irrelevant. The Table of contents approach (green, ~600 tokens) lists stack, critical rules, and on-demand @references that load only when needed: 30× cheaper, better recall, same coverage.


Part 4: Personalizing your tool

There are four extension types in both tools. The decision tree is identical:

Picking the right extension type. Four questions map to four mechanisms: 'Run the same prompt manually each time?' to Slash command. 'Should the model auto-invoke when a task matches?' to Skill. 'Must happen every time, no exceptions?' to Hook or Plugin. 'Isolate noisy reads from the main thread?' to Subagent.

Every one of these is a context-management tool. Custom commands and skills inject the right context on demand. Hooks and plugins enforce rules without spending tokens on the model "remembering" them. Subagents quarantine context so it doesn't leak into your main thread. Same problem, different shapes.

8. Slash commands

A slash command is a saved prompt with a memorable shortcut. Both tools support them, with very similar shapes.

One gotcha worth flagging up front: commands are loaded once, at session start. If you create a new command mid-session (whether you wrote the file yourself or asked the model to), it will not appear in autocomplete until you exit and re-enter the CLI. The two folders below behave the same way on this; restart the session after either.

Markdown files in .claude/commands/ (project) or ~/.claude/commands/ (personal). Example .claude/commands/review.md:

Review the current diff for:

1. Bugs and edge cases
2. Test coverage gaps
3. Naming and readability
4. Adherence to @docs/conventions.md

Be specific. Quote the lines you're commenting on.

This /review command is concept 6 of AI Prompting put to work.

AI models like to agree with you. Ask "review my diff" with no instructions and you get looks good, well-structured, nice naming, whether it's true or not.

The four-point checklist above stops that. The model can't just say "looks good" anymore. It has to check each point one by one, and point at the line where it fails.

Both invoke as /review. Both support $ARGUMENTS (and OpenCode adds positional $1, $2, etc.). OpenCode also supports !`shell command` to inject command output into the prompt and agent: plan / subtask: true frontmatter to route the command to a specific agent or run it in a subagent. This is useful for keeping review workflows out of your main context.

A non-software example. A /standup command for the daily ritual most teams have. Save this as .claude/commands/standup.md (or .opencode/commands/standup.md):

Read yesterday's note in `journal/`. Produce a 4-line standup:

1. What I shipped yesterday (1 line)
2. What I'm working on today (1 line)
3. Blockers (1 line, "none" if clear)
4. One thing I learned (1 line)

Tone: telegraphic. No filler.

Now /standup produces a clean status update from yesterday's notes whenever you type it. Same shape as /review above, just a different domain. The same trick works for /digest (turn a folder of emails into action items), /draft-reply (write a polite reply to the email at this path), or any chore you find yourself typing the same instructions for, more than twice.

The unifying test in both tools: if you find yourself typing the same instructions more than twice, it should be a command. The depth version (folder layout, frontmatter, argument passing, when to promote a command into a skill) is in Chapter 14 § Teach Claude Your Way of Working.

Note (Claude Code, 2026): As of 2026, slash commands and skills have been unified. Files in .claude/commands/ still work and will appear as slash commands, but new work is increasingly going into .claude/skills/ because skills support more (auto-invocation, subagent execution, file-path filters). Treat the two as a continuum.

9. Skills

A skill is a custom command's bigger sibling. Same idea, packaged expertise, but with three meaningful upgrades that are identical between the two tools:

  1. Auto-invocation. The model reads skill descriptions at the start of every session and invokes the right skill when a task matches.
  2. Progressive disclosure. A skill has a SKILL.md file (the entry point) and can reference other files in the same folder. Only SKILL.md loads up front; the rest loads on demand. This is the context-management principle made into a file format.
  3. Frontmatter controls. YAML at the top of SKILL.md configures behavior.

The file format converged. A skill written for one tool largely works in the other.

.claude/skills/extract-transcript/SKILL.md:

---
name: extract-transcript
description: Extract a clean transcript from a YouTube video URL. Use when the user provides a YouTube link and asks for the transcript, captions, or text of the video.
---

# Extract YouTube transcript

1. Take the URL from the user's message.
2. Run `yt-dlp --skip-download --write-auto-sub --sub-format vtt "$URL"`.
3. Convert the VTT to plain text: strip timestamps, deduplicate overlapping captions.
4. Save to `transcripts/{video-id}.txt`.

For formatting conventions (paragraph breaks, speaker labels), see `references/style.md`.

Yes, those are byte-identical. OpenCode also reads ~/.claude/skills/ and .claude/skills/ as fallbacks, so any skill you've already written for Claude Code works in OpenCode without changes.

The description is the most important field: it's what the model uses to decide whether the skill applies. Vague descriptions ("helps with videos") fire on everything; specific ones ("Use when the user provides a YouTube link...") fire only when relevant.

Keep SKILL.md itself short: under ~200 lines is a good rule. Push depth into reference files in the same directory and link to them. That way a quick task only pays for the overview; a deep task can pull in the references it needs. (For the foundational walkthrough (what a skill is, when it activates, how progressive disclosure works), see Chapter 14 § The Concept Behind Skills and Building Your Own Skills. For the team/CI variant (frontmatter fields, glob-scoped activation), see Chapter 17 § Custom Skills with Frontmatter.)

Don't write skills from scratch. Claude Code ships a skill-creator skill that scaffolds new skills correctly. Thanks to the same fallback, install it once and both tools can invoke it. (Note: file discovery is shared, but skill bodies that reference Claude Code-specific tools may need light edits to run cleanly in OpenCode. For new agents, which are a separate concept in OpenCode, use opencode agent create for an interactive agent scaffolder.)

One more pattern: chain small skills, don't build monoliths. A "weekly content digest" skill that researches, writes, formats, and reviews in one go is harder to maintain and worse at each step than four separate skills (research, draft, format, review) that hand off to each other. The handoff is usually filesystem-mediated: research writes its output to tmp/research.md, then draft is instructed to read from it; draft writes to tmp/draft.md, and so on. You can also just prompt the model to run them in sequence ("use the research skill, then draft a post from the output"), but the file-based pipeline is more durable across /clear and easier to debug. Either way, each skill stays focused on its own step. The reusability is nice, but the real win is per-step context isolation: the format skill doesn't need to load the research notes, and the research skill doesn't need to know about formatting conventions. Same principle as subagents, applied at a smaller granularity.

10. Hooks (Claude Code) / Plugins (OpenCode)

Skills are probabilistic: the model decides whether to invoke them. Hooks and plugins are deterministic: they fire on specific events, every time, no model judgment involved.

Same idea, different machinery. This is where the two tools diverge most. One naming note before the tabs: Claude Code also has a separate thing it calls "plugins" (bundles that package commands, skills, and hooks together). That is not what this section is about. Here, "hook" always means the Claude Code lifecycle hook, and "plugin" always means the OpenCode event module. They occupy the same slot in each tool: the deterministic guardrail.

Hooks are shell commands attached to lifecycle events in .claude/settings.json. The big events: SessionStart, UserPromptSubmit, PreToolUse (exit code 2 blocks), PostToolUse. The structure has three levels: the event (PreToolUse), a matcher group that filters by tool name (Bash), and the handler that runs. The handler's if field narrows further by matching the actual command, so the handler only spawns when it needs to. Example: block rm -rf no matter how the model is feeling:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm -rf *)",
"command": "echo 'Blocked dangerous command' >&2; exit 2"
}
]
}
]
}
}

The if: "Bash(rm -rf *)" filter is what does the matching; by the time the command runs, the tool call is already a known rm -rf, so the command just has to refuse (exit code 2 blocks). When a hook genuinely needs to inspect the tool's input (not just match on it), Claude Code passes that input as JSON on stdin, which the command reads with jq. You'll see that in the placeholder example below.

A non-software example: do not ship drafts that still have placeholder markers. Suppose you sometimes leave [TODO] or [FIX] markers in drafts and forget to resolve them before publishing. A hook can block any write into published/ if the file still contains a placeholder:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "jq -e '(.tool_input.file_path | test(\"/published/\")) and (.tool_input.content | test(\"\\\\[TODO\\\\]|\\\\[FIX\\\\]\"))' >/dev/null && { echo 'Refusing to publish draft with TODO/FIX markers' >&2; exit 2; }; exit 0"
}
]
}
]
}
}

This one needs to look inside the file, so it does more than the rm -rf block. The matcher catches every Write; the command pipes the JSON Claude Code passes on stdin straight into one jq test that checks two things at once, and blocks only when both are true: the file_path is under published/, and the content contains a placeholder marker. A [TODO] in a drafts/ file passes; a clean file in published/ passes; only a marked file headed for published/ is stopped with exit code 2. The model writes the file, the hook checks, and if a placeholder leaked through, the model has to resolve it before the file ships.

The trade is real: OpenCode plugins are more capable; Claude Code hooks are simpler.

Hooks vs plugins comparison. A lifecycle timeline shows events: SessionStart, UserPrompt, PreToolUse (can block), PostToolUse, SessionIdle. Claude Code Hooks are a shell command in settings.json with exit-code-2 blocking; strengths are five-minute setup and bash-only authoring; limits are smaller event set and awkward structured logic; best for simple guardrails. OpenCode Plugins are JS/TS modules subscribing to events with throw-to-block semantics; strengths are rich events including idle and edited, plus async work and structured logging; limits are needing Bun/Node and being overkill for one-line checks; best for complex workflows. Same outcome: match the tool to what you're actually building.

Anyone who can write bash can write a Claude Code hook in five minutes and ship it to a repo without a Node toolchain. OpenCode plugins need Bun or Node, npm dependencies, and TypeScript or JavaScript fluency. For a one-line check ("block rm -rf"), shell wins on cost. For anything that needs structured logging, async work, or multiple event subscriptions, the JS/TS module is genuinely cleaner. Pick the tool whose constraints match what you're building.

A useful pattern from people running these tools on production codebases: don't block at write time, block at commit time. Let the model finish its work (interrupting mid-edit confuses it) and run a hook/plugin on the commit step that checks tests pass, types check, formatter is happy. If something fails, force the model back into a fix loop.

Use hooks/plugins for the things you need to be true 100% of the time. Use skills for the things you'd like the model to remember most of the time.

11. Subagents

A subagent is an isolated agent instance with its own context window. You delegate a task to it; it works in private; it returns a summary. Its file searches, log dumps, and exploratory reads never touch your main thread.

Why this matters, in context-management terms: the most context-poisoning thing you can do is "explore the codebase to find where X happens." That kind of task pulls dozens of files into context, most of which you don't need. Doing it in a subagent means your main session sees only the conclusion ("X happens in src/services/billing.ts:142"), not the search.

Both tools ship with a read-only Explore subagent (among others; OpenCode also ships a General subagent for broader delegated work) that handles codebase exploration without polluting your main thread. In Claude Code, plan mode auto-delegates to Explore: you'll mostly use it without thinking about it, and the model itself can even self-enter plan mode when asked. In OpenCode, the Plan agent can auto-invoke subagents the same way (subagent delegation is a normal primary-agent capability), or you can call it explicitly with @explore; entering Plan mode itself, however, is a deliberate user action via Tab.

For custom subagents:

.claude/agents/doc-fetcher.md:

---
name: doc-fetcher
description: Fetches and summarizes external library documentation. Use when the user references a library and we need to understand its current API.
tools: WebFetch, Read, Write
---

You are a documentation researcher. Given a library name and a topic, fetch the official docs, extract only the API surface relevant to the topic, and write a focused summary to `tmp/docs-{library}.md`. Don't paste full pages: extract the patterns and signatures we need.

A non-software example: a research subagent. Suppose you have a folder of long PDFs (industry reports, legal contracts, academic papers) and you want the agent to extract a focused summary without dragging the whole document into your main thread:

---
name: pdf-summarizer
description: Reads a long PDF and writes a focused summary. Use when the user references a PDF and wants the key points without dumping the whole document into the conversation.
tools: Read, Write
---

You are a research summarizer. Given a PDF path and a question, read the document, extract only the passages relevant to the question, and write a focused summary to `tmp/summary-{pdf-name}.md`. Do not paste full pages: pull out the patterns, claims, and quotes the user actually needs.

Same shape as the doc-fetcher above, different domain. The 200-page PDF stays out of your main conversation; only the answer comes back.

Invocation is the same in both tools. Both auto-invoke based on the subagent's description (this is the path you'll use most often: write a good description, and the model picks the right helper when a task matches). Both also support explicit invocation with @subagent-name in the prompt; OpenCode surfaces this in autocomplete more prominently, but Claude Code accepts it just as well. Use auto-invocation by default, reach for @name when you want to be sure a specific subagent runs (or when the description is ambiguous between several candidates).

The general rule, identical in both: if a task involves a lot of reading that won't be relevant to the final answer, it belongs in a subagent. (Chapter 14 § Subagents and Orchestration walks through writing your own and chaining them together.)

Why subagents matter: context isolation. Without a subagent, an exploration task pulls middleware.ts, session.ts, cors.ts, twelve route files, redis.ts, tests/setup.ts, and eight more files into the main conversation: ~45,000 tokens used, most of which won't matter for the answer. With a subagent, all those reads happen in an isolated context window that gets discarded after the task completes; only a focused summary returns ('Auth middleware is in src/auth/middleware.ts. Pattern: per-route via app.use().'): ~200 tokens used.


Part 5: Connecting to the world

12. MCP, used honestly

MCP (Model Context Protocol) is a standardized way to expose external tools (Slack, Notion, your database, GitHub, whatever) to the agent. Both Claude Code and OpenCode support MCP using the same protocol. A server written for one works in the other.

Configuration shape differs cosmetically:

Typically configured via the CLI (claude mcp add ...) or in .claude/settings.json.

The pitch is real in both tools: connecting the agent to your tools means it can act on your work, not just talk about it. Reading a Linear ticket, posting a Slack update, querying production read-replicas: all reasonable.

The honest qualification, which applies in both tools: MCP is not always the right answer, and some experienced users have moved away from heavy MCP use in favor of simple CLIs. The reasoning: an MCP server abstracts the underlying tool into a fixed set of operations, and every operation pays a context cost up front in tool descriptions. A CLI is just a CLI: the agent can read its --help, compose flags, pipe outputs, and improvise. For stateless tools (GitHub, AWS, Jira), gh, aws, and jira CLIs are often more flexible than their MCP equivalents.

OpenCode's docs explicitly call this out, warning that MCP servers add to your context and that you should be careful which ones you enable: "Certain MCP servers, like the GitHub MCP server, tend to add a lot of tokens and can easily exceed the context limit."

A useful working model: use MCP for stateful or auth-heavy services where the protocol layer is genuinely doing work (Playwright is the canonical example: managing a browser session is hard). Use CLIs for everything else. Don't install ten MCP servers because they exist; install one when there's a real reason.


Part 6: A complete worked example, twice

This is the section the rest of the crash course points at. One realistic task, every concept, both tools. The same eight decisions, shown once in Claude Code and once in OpenCode, side by side, so you can see how little actually differs.

You will not write a single line of code. Anyone with a laptop and a folder of files can follow along: a manager, a teacher, a small-business owner, a student. The task happens to use meeting notes from a small team, but it works the same way for a school committee, a volunteer group, a freelance practice, or any group of people who keep notes and need to pull action items out of them.

Your task

You have a folder called notes/ with five files in it: last week's meeting notes from your team. They are a mess. Some meetings list action items under ## Action Items. Some use ## Todos. One uses lowercase ## todo. A few hide action items deep inside other sections. Some bullets are tagged [private] or [HR] and should not go anywhere public.

You want one clean file, weekly-actions.md, that lists every action item, grouped by who owns it, with a link back to the meeting it came from. Nothing private should leak through. Nothing should get lost. And if any deadlines fall on a public holiday, you want a warning.

📁 Get the five starter files first (click to expand)

Copy these into a notes/ folder before you start. The example is engineered around them: each file plays a specific role, so if you skip one, one of the steps below stops working. (Prefer to skip folder setup? Paste the five files directly into your tool's chat and ask it to treat them as the contents of notes/.)

notes/2026-12-07-monday-team-meeting.md

# Monday Team Meeting - 2026-12-07

Attendees: @sara, @diego, @priya, @marcus

## Discussion

Reviewed last month's customer satisfaction scores. Renewals are down slightly in the small-business segment.

## Action Items

- @sara: draft a revised welcome email by Dec 18
- @diego: check spam-folder reports with our email vendor by Dec 16
- @marcus: pull renewal numbers for the last two quarters and share with the team
- @sara: finalize offer terms for the new account manager [HR]

notes/2026-12-08-customer-feedback.md

# Customer Feedback Review - 2026-12-08

Attendees: @sara, @amara, @marcus

## Summary

Read through the top twenty customer comments from last week. Three themes came up: pricing page is confusing, the call-back service is slow, and the FAQ on the website is out of date.

## Todos

- rewrite the pricing page in plain language
- @marcus: log a complaint with the call center vendor about response times
- look into why the FAQ has not been updated in six months
- @amara: draft a customer note explaining the upcoming changes
- review all printed brochures for outdated photos and pricing

notes/2026-12-08-q1-planning.md

# Q1 2027 Planning - 2026-12-08

Attendees: @sara, @diego, @amara, @lukas

## Initiatives

We walked through the four candidate initiatives for Q1.

### Year-end promotional campaign

Time-bound: wraps before the holiday break.

#### Action Items

- @amara: finalize the year-end promotional brochure by Dec 25
- @lukas: confirm placement with paid media partners by Dec 20

### Onboarding overhaul

Top priority next quarter.

## todo

- @diego: write the project brief by Jan 15
- @sara: own messaging and visual direction

notes/2026-12-09-all-hands-prep.md

# All-Hands Prep - 2026-12-09

Attendees: @sara, @marcus

## Agenda

Walked through the December all-hands agenda. Most slides are in good shape; a couple still need owners.

## Next Steps

- @marcus: set up the video call and record a practice run
- @sara: finalize the Q4 numbers slide
- @sara: prepare the bonus and compensation talking points [private]
- @marcus: book the venue for the team holiday dinner

notes/2026-12-11-vendor-review.md

# Vendor Review - 2026-12-11

Attendees: @sara, @amara, external partner

## Summary

Confidential contract negotiation with our printing and fulfillment vendor. Details under NDA.

## Action Items

- @sara: circulate the revised contract to the vendor [private]
- @amara: draft the internal announcement once terms are agreed [private]
- @sara: review the legal redlines on the new pricing schedule [private]

How to read this section

Each of the eight context-management decisions from this chapter gets one numbered step below. Every step has the same shape:

  • What you do: the exact thing you type or press
  • Why: one paragraph on what this decision is buying you
  • What changes in OpenCode: the version for the other tool

If a step says "identical in OpenCode" you can skip that line and move on.


Decision 1: Trim the rules file

What you do (Claude Code). Open Claude Code in your project folder. Run /init. It creates a file called CLAUDE.md that is much too long. Delete most of it. Leave only this:

# weekly-rollup

## Layout

- `notes/`: meeting notes, one file per meeting
- `weekly-actions.md`: the rollup we are creating
- `plans/weekly-rollup-plan.md`: the plan we will save and reuse

## Critical rules

- Action items can appear under `## Action Items`, `## Todos`, `## Next Steps`, or `## todo` (one meeting uses lowercase). Look at all heading levels, not just the top.
- Owners are written as `@name`. Items with no owner go to an "Unassigned" section.
- Never include any bullet tagged `[private]` or `[HR]`.

Why. This file is read at the start of every message. Anything you put here costs you, every turn. So you keep it short: only the things the model cannot figure out by looking at the folder itself. The privacy rule, especially, is something it cannot guess.

What changes in OpenCode. The file is named AGENTS.md instead of CLAUDE.md. Same content, same purpose. (And if you already have a CLAUDE.md from before, OpenCode will read that too; you do not have to rename anything.)


Decision 2: Plan before writing

What you do (Claude Code). Press Shift+Tab twice. This puts the model in plan mode: it can read your files but cannot change anything. Then type:

Read every file in notes/. Produce weekly-actions.md grouped by
owner. For each item include the action, the source filename, and
the meeting date. Skip anything tagged [private] or [HR]. Do not
lose any action items.

The model thinks for a minute, then comes back with a written plan describing exactly what it intends to do.

Why. This is the most underused feature in either tool. Reading a plan takes 30 seconds. Reverting a bad change takes much longer. You always want the plan first. (Foundations: Chapter 14 § Hello Claude introduces plan mode in both tools; Chapter 17 § Plan Mode vs Direct Execution goes deeper into when it pays off and when it just slows you down.)

What changes in OpenCode. Press Tab to switch from the Build agent to the Plan agent. The brief and the result are identical; only the keystroke differs.


Decision 3: Push back on the plan

What you do (Claude Code). Read the plan carefully. Two things will probably be wrong with it.

The first: it will only mention the top-level headings (## Action Items, ## Todos). But one of your meeting files (the Q1 planning notes) hides action items inside ### Year-end promotional campaign, with a #### Action Items underneath. The plan would miss those.

The second: it will not say what to do with action items that have no owner (no @name next to them, like the customer-feedback file's "rewrite the pricing page in plain language"). Those would silently disappear.

You push back:

Two changes. (1) Look at all heading levels, not just the top.
Some action items live under sub-headings. (2) Items without an
owner go to an "Unassigned" section at the bottom; never drop
them.

The model updates the plan. You ask it to save the final plan to plans/weekly-rollup-plan.md so you can reuse it next Friday.

Why. The plan is a contract. You catch problems here, when they cost you nothing, not after the file has already been written and you have to clean up.

What changes in OpenCode. Identical.


Decision 4: Let it do the work

What you do (Claude Code). Press Shift+Tab to leave plan mode. Tell the model to go ahead.

It reads the five files. It writes weekly-actions.md. It groups items by owner. It drops the [private] and [HR] bullets. It puts orphan items in an Unassigned section. You watch this happen.

Why. Up to this point you have been careful. Now you let it work. Because you planned, the work is mostly correct on the first try.

Check where the file landed

weekly-actions.md should be at the project root, next to notes/, not inside notes/. If the model wrote it into notes/ instead, that is the signal that the CLAUDE.md Layout section from Decision 1 is missing or being ignored. Fix that now: the Decision 6 safety net greps for filenames in a root-level weekly-actions.md, and it will not find a file in the wrong place.

What changes in OpenCode. You will be asked to approve each file write the first time. You can say yes, or you can pre-approve common actions in opencode.json to make later runs smoother.


Decision 5: Summarize when the conversation gets long

What you do (Claude Code). The conversation is now long. The model has read every meeting note, and a lot of that raw content is still sitting in its memory, including some of the private bullets it correctly skipped. You do not need any of that anymore. You type:

/compact keep the heading rules, the owner list, and the
private/HR exclusion rule

The conversation gets summarized down to just the parts you told it to keep.

Why. A long, cluttered conversation makes the model worse, not better. It also costs more. You clear out what is no longer relevant, especially private content, which should not sit in memory longer than it needs to.

What changes in OpenCode. Identical command.


Decision 6: Set a safety net for committing

What you do (Claude Code). Before you save your work to git, you want to make sure no meeting got accidentally skipped. You will add a Claude Code hook: a small safety net that runs automatically before every commit.

Don't ask the model to write this one: paste it in

This is the one place in the chapter where you paste a config block yourself instead of letting the model write it. There are two reasons, both learned the hard way.

(1) "Hook" is ambiguous. If you tell the model "create a hook," Claude Code may reach for a git hook (a file in .git/hooks/) instead of a Claude Code hook (an entry in .claude/settings.json). Those are different things; you want the second one.

(2) The model will try to be clever and silently break the safety net. Your CLAUDE.md says to skip [private] and [HR] items. If you ask vaguely, the model reads that rule and "helpfully" bakes the same filter into the hook script, which means the vendor-review meeting (every bullet is [private]) gets quietly ignored and the hook never fires. The whole point of the hook is to catch that case. Asking the model to write its own watchdog against rules it's already been told to honour is a losing game.

The safe move is just to paste the configuration in.

Open .claude/settings.json (create the file if it doesn't exist) and paste this exactly:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"command": "for f in notes/*.md; do grep -q \"$(basename $f)\" weekly-actions.md || { echo \"Missing: $f\" >&2; exit 2; }; done"
}
]
}
]
}
}

In plain English: the matcher says "watch the Bash tool"; the if narrows that to git commit commands only; the command then loops over every .md file in notes/, greps for its basename inside weekly-actions.md, and if any file isn't mentioned, exits with code 2 (which blocks the commit). No content filters, no [private] checks, no exceptions. Filenames only. That is the whole point.

Now tell the model to commit. The safety net fires: Missing: notes/2026-12-11-vendor-review.md. The reason is logical: every action item in that meeting was tagged [private], and your rules in Decision 1 say to drop those; so when the rollup was built it had nothing to write from this file, and the filename never made it in. The hook does not know any of that; it only sees that the file is unmentioned, and it blocks.

The model reads the error and patches the rollup with a placeholder entry that names the file explicitly, e.g.:

## From 2026-12-11-vendor-review.md

- All items confidential — see meeting owner.

The filename is now present in weekly-actions.md, the hook's grep finds it, the commit goes through. Nothing private leaked into the rollup; the audit trail is intact.

You did nothing during this loop. The safety net caught the mistake and the model fixed itself.

Why. Things you want to be true every single time should not depend on the model remembering. They should be checked automatically. And the check should live somewhere the model cannot quietly rewrite to please itself: that is why this one is a paste, not a prompt.

What changes in OpenCode. OpenCode calls these plugins instead of hooks, and they are written as a small JavaScript file. Same idea, same outcome, same paste-don't-prompt logic. Create .opencode/plugins/check-rollup-complete.js (the folder may not exist yet) and paste this in:

// .opencode/plugins/check-rollup-complete.js
import { readdirSync, readFileSync } from "fs";

export const CheckRollupCompletePlugin = async ({ $ }) => {
return {
"tool.execute.before": async (input, output) => {
if (
input.tool === "bash" &&
output.args.command?.startsWith("git commit")
) {
const rollup = readFileSync("weekly-actions.md", "utf8");
const missing = readdirSync("notes")
.filter((f) => f.endsWith(".md"))
.filter((f) => !rollup.includes(f));
if (missing.length) {
throw new Error(`Missing from rollup: ${missing.join(", ")}`);
}
}
},
};
};

Same shape: list every file in notes/, check each basename is in the rollup, throw if any is missing. No [private] filter inside. The behaviour is identical to the Claude Code version: missing file, commit blocked, model fixes itself.

The hook/plugin material at depth (exit codes, matcher syntax, the full event list) is in Chapter 14 § Hooks and Extensibility (and Plugins: Putting It All Together for the OpenCode side).


Decision 7: Send a side-task to a helper

What you do (Claude Code). Some action items have deadlines like "by Dec 25" or "before year-end." You want to flag any that fall on a public holiday so the owners can adjust. You type:

Use the doc-fetcher helper to look up the 2026 international
public holidays list and write the dates to tmp/holidays-2026.md.

A separate helper goes off, fetches a long page from the web, extracts only the dates, and writes them to a small file. Your main conversation only sees the small file, not the long page.

The model then reads the deadlines in your rollup, cross-references the holidays, and adds a note: ⚠ falls on Christmas Day next to the brochure deadline.

Why. Going to fetch a web page directly would have dumped pages of text into your conversation, making it slow and cluttered. Sending a helper to do it instead means only the answer comes back, not the noise.

What changes in OpenCode. You can type @doc-fetcher directly to call the helper; it shows up in autocomplete. Same helper, easier to invoke.


Decision 8: Save what you learned

What you do (Claude Code). This is a Friday ritual; you will do it every week. You do not want to remember all of these decisions next Friday. So you ask the model to save them as a skill:

Create a skill at ~/.claude/skills/weekly-meeting-rollup/SKILL.md
based on what we just did. Include: the four heading variants,
the all-headings rule, the private/HR exclusion, the Unassigned
section, the missing-file check, and the holiday cross-reference.

Next Friday, you will just type "do the weekly rollup" and the model will pull this skill in automatically. Every decision you made today, applied for free.

Why. This is the payoff. The work you did this week was not just for this week; it taught the model how to do this task forever.

What changes in OpenCode. The skill saves to a slightly different folder (~/.config/opencode/skills/...), but the file itself is byte-for-byte identical. Copy it between the two tools and it works in both. That is the strongest possible proof of the chapter's thesis.


What just happened

Look back over the eight steps. None of them required you to write code. None of them required you to know what an API is. What you actually did was eight small acts of managing the model's attention: telling it what to look at, when to plan, when to forget, when to delegate, when to stop trusting itself, and when to remember.

That is the entire chapter.

And look at how little differed between the two tools:

  • A different filename for the rules file (CLAUDE.md vs AGENTS.md)
  • A different keystroke to enter plan mode (Shift+Tab vs Tab)
  • A different language for the safety-net file (JSON vs JavaScript)
  • A different folder for the skill at the end (but the same file)

That is it.

Eight context decisions across both tools. Decision 1 trim rules file (CLAUDE.md / AGENTS.md). Decision 2 plan before writing (Shift+Tab / Tab to Plan agent). Decision 3 use Explore subagent (built-in / built-in @explore). Decision 4 save plan to file (plans/.md / plans/.md). Decision 5 compact mid-task (/compact / /compact). Decision 6 quality gate on commit (Hook in shell / Plugin in JS or TS). Decision 7 subagent for outside lookup (.claude/agents/ / .opencode/agents/). Decision 8 save skill at the end (SKILL.md / SKILL.md, same format). Each row shows how small the Claude-Code-to-OpenCode delta is: a filename, a keybind, a config language.

The thinking is the tool. The configs are decoration. Learn to think this way and your skills survive whichever tool wins.


Part 7: Where to run it, and how to grow

13. Terminal, IDE, or desktop?

Both tools run in several places.

Claude Code: terminal, VS Code / JetBrains plugins, the Claude desktop app, and cloud-hosted variants for async work.

OpenCode: terminal (TUI is the flagship), desktop app (in beta on macOS/Windows/Linux), IDE extension via ACP support, web interface via the SDK, plus GitHub and GitLab integrations.

Pick based on what you're doing. For greenfield development with lots of file editing, IDE plugins are hard to beat: you watch the diff happen. For long-running tasks where you want notifications and parallel sessions, the terminal is fine. For non-coding work and scheduled jobs, the desktop apps are more pleasant.

The strong recommendation, in either tool: start in the terminal or an IDE plugin. Once you understand what's happening underneath (which files the model reads, which commands it runs, which decisions it makes), every other interface becomes a thin wrapper you can read through. If you start in a heavily abstracted UI, you'll struggle to debug when things go sideways, because you won't know what can go sideways.

14. Build a personal context library, slowly

Once you have a few projects using either tool, you'll notice you're writing similar things into every rules file: your code style, your commit conventions, your testing philosophy. Stop copy-pasting.

Put the shared parts in your home config and reference them from each project's rules file:

~/.claude/:

# CLAUDE.md

@~/.claude/style/typescript.md
@~/.claude/style/commits.md

## Project-specific

[only the things unique to this project]

Same idea works for skills. A commit-message skill or code-review skill in ~/.claude/skills/ (which OpenCode also reads) or ~/.config/opencode/skills/ is available everywhere automatically.

The discipline: don't pre-build this. It's tempting to spend a Saturday designing the perfect personal setup. Resist. Every entry should come from a real failure: a moment where the model got something wrong, or you typed the same thing for the third time. Build from regret, not from imagination. The library that grows from real friction is small and useful; the one designed in advance is large and ignored.

15. Memory beyond the basics

Built-in resume + a well-tended rules file covers most people most of the time. If you need more (search across past conversations, persistent project notes, knowledge that survives /clear), you have options:

  • A notes/ folder in the project, with the agent writing structured notes after major work. Cheap, durable, greppable. Surprisingly effective. Works in both tools without any setup.
  • A memory MCP server. Several exist; they give the agent an explicit save/recall API. Works in both tools.
  • Vector search over your conversation history for "have I solved this before" queries. Tool-specific implementations exist; check the ecosystem pages.

Pick based on what you actually want to remember. If it's "decisions and gotchas for this codebase," a notes/ folder is enough. If it's "everything I've ever told the model across all projects," you want something heavier. Don't install infrastructure you don't have a problem for.


Part 8: Composing Claude Code and OpenCode

The earlier parts treated Claude Code and OpenCode as alternatives: pick one, learn it. That framing was deliberate: every concept needs to make sense in either tool before you complicate things. But once you have the fundamentals, the more interesting question is composition. Two tools used together on the same project are not just a fallback in case one fails. They're a different way of working, and the mechanics of using them together are worth their own part.

The substrate: git worktrees

Before patterns, the operational layer that makes any of this safe.

Both tools support parallel sessions, but if two agents edit the same file at the same time, the last write wins and you get stomped diffs. The fix is git worktrees: each worktree is a checked-out copy of your repo at a different branch, sharing the same .git directory. Two agents in two worktrees can edit different files in parallel without ever touching the same path.

# from your main repo directory
git worktree add ../myproject-backend feature/auth-core
git worktree add ../myproject-frontend feature/auth-ui

# now you have two parallel working dirs, two branches
cd ../myproject-backend && claude
# in another terminal
cd ../myproject-frontend && opencode

Remove worktrees with git worktree remove ../myproject-backend when the work is done.

The file-edit rule, in three tiers:

PatternVerdict
Two sessions editing the same file simultaneouslyBad: last write wins, the other session's edits are lost
Two sessions editing different directoriesGood: no contention, parallel speedup is real
One session edits, commits, hands off to the otherAcceptable: the commit is the handoff artifact

Rule of thumb: start with one session per task. Add a second only when you can clearly point at which files each session owns. The cognitive cost of context-switching between two agentic sessions is real; it's only a win when the work genuinely partitions.

The shared layer that makes any of this work is the project's rules and skills. Both tools read the same CLAUDE.md and the same .claude/skills/ directory (Concepts 7 and 9 cover this). One source of truth for project conventions; two tools consuming it.

Pattern 1: Plan / Execute split

The expensive cognitive work in agentic coding is planning: reading the codebase, deciding the approach, anticipating edge cases. The cheap mechanical work is implementing a plan that already exists. Pricing reflects this: frontier-tier models cost roughly an order of magnitude more per token than economy-tier models, and they earn that price on planning, not on implementation.

The composition:

  1. Open Claude Code in plan mode (Concept 2). State the brief. Let it produce a plan.
  2. Save the plan to docs/plans/feature-x.md.
  3. Open OpenCode in a separate worktree, pointed at an economy-tier model (see opencode.ai/docs/providers for the current setup). First message: read docs/plans/feature-x.md and implement it.
  4. OpenCode does the file edits, runs tests, fixes lint.
  5. (Optional) Open Claude Code again to review the diff before merge.

The plan file is the contract. It survives session loss, encodes the architectural decisions, and lets the cheap session work without redoing the expensive thinking. This pattern alone often delivers most of the cost savings teams hope to get from "switching to a cheaper model," without losing the quality of frontier-model planning.

Pattern 2: Cross-model review

This is concept 13 of AI Prompting promoted from a habit to an architecture. There, you ran the same draft through two chat windows; here, two coding tools cross-check each other's diffs.

Different models have different blind spots. A model that wrote a piece of code is the worst possible reviewer for that code, because it has the same blind spots that produced the original. A model from a different family (different training data, different reasoning habits) will catch things the author missed.

The composition:

  1. Tool A (any model) implements a feature on its own worktree.
  2. Tool B (different model, ideally different family) reads the diff and writes a critique to docs/reviews/feature-x.md. The reviewer does not edit; it produces text.
  3. Tool A reads the review and decides which points to act on.

This is more useful than it sounds. Reviewers running on cheap economy models catch surprising amounts of real value: missing edge cases, security oversights, naming inconsistencies, dead code. The dollar cost of running a review pass is small; the time cost of catching a bug after merge is large.

The pattern works best when reviewer and implementer come from different model families: Claude reviewing Claude misses what GPT or DeepSeek would catch, and vice versa. Same-family review is still better than no review, but the value is in the diversity, not the act.

What stays single-tool

Composition has overhead. Two tools means two configs, two permission lists, two notification setups. For most short tasks (a single function, a quick refactor, a bug fix) a single session is faster, simpler, and cheaper than orchestrating across tools.

Reach for composition when the task is large enough that the planning is genuinely separable from the implementation, when the cost of a frontier model on the entire task would be wasteful, or when the value of an independent review pass exceeds its cost. For everything else, the single-session discipline from Parts 1–7 is the right move.

Composition is just what happens when you stop treating one tool and one model as the unit of work, and start treating the agentic coding session as the unit instead.


How to actually get good at this

Reading this crash course does not make you good at agentic coding. Using it does, and the path looks like this:

You start manual. You feel the friction: every approval prompt, every "wait, why doesn't it know X." That friction is the curriculum. Each piece of friction maps to one of the fifteen concepts above:

  • "Why does it keep forgetting the auth pattern?" → rules file is missing or bloated.
  • "Why did it just delete my migrations folder?" → permissions weren't tight enough.
  • "Why is it so slow after an hour?" → context rot; you needed /compact.
  • "Why am I typing the same thing every Monday?" → that's a skill.
  • "Why did my tests pass locally but break in CI?" → that's a hook or plugin.
  • "Why does exploring the codebase always pollute my conversation?" → that's a subagent.

Build the response to each problem when you hit it, not before. Your rules file should be ten lines, then twelve, then twenty: each line earned by a mistake it now prevents. Your skills folder should have one skill before it has ten. Your hooks/plugins should exist because something broke without them, not because someone said hooks were powerful.

The 80/20 isn't memorizing fifteen concepts. It's noticing which one a given problem belongs to, fast enough that you reach for the right tool. That noticing is the skill, and it only comes from time spent watching agentic coding tools succeed and fail on real work.

The portability dividend. Once you've built that noticing for one tool, it transfers. The friction-to-concept map above is identical in Claude Code and OpenCode. Pick one to start, learn its specific config, and the moment you decide to switch (for cost reasons, model preference, license, or a new tool that doesn't exist yet) your knowledge comes with you. The configs change. The thinking doesn't.

Start with one project. Use plan mode for everything non-trivial. Watch your context. The rest builds itself.


Quick reference

The 15 concepts in one line each

  1. Tools take actions, not just answers. Write briefs, not questions.
  2. Plan mode (Shift+Tab in CC, Tab to Plan agent in OC). Read-only investigation before any writes.
  3. Permissions discipline. Start manual, codify allow/deny over time. Never auto-approve rm -rf.
  4. Context rot is real. Recall degrades long before the window fills, and every turn re-bills the whole context.
  5. /clear vs /compact. New task vs same task lighter. Pick wrong and you lose the wrong thing.
  6. Resume sessions. Pick up where you left off without re-explaining; pair with saved plan files.
  7. CLAUDE.md / AGENTS.md is a table of contents. Under ~2,500 tokens, references load on demand.
  8. Slash commands. Saved prompts you trigger by name when you keep typing the same thing.
  9. Skills. Saved expertise the model auto-invokes by description; progressive disclosure via SKILL.md plus references.
  10. Hooks (CC) / Plugins (OC). Deterministic guardrails on lifecycle events. Not optional for production.
  11. Subagents. Isolated context windows for noisy reads (codebase exploration, doc fetching).
  12. MCP, used carefully. Standard protocol for external tools; CLIs often beat MCP for stateless services.
  13. Where to run. Start in terminal or IDE plugin so you can see what the agent is doing.
  14. Personal context library, slowly. Share configs across projects; build from regret, not imagination.
  15. Memory beyond basics. Built-in resume + good rules file is enough for most. notes/ folder if you need more.

Command quick-ref

Want to...Claude CodeOpenCode
Initialize project rules file/init/init
Enter plan modeShift+Tab (twice)Tab (cycle to Plan agent)
Wipe conversation, start fresh/clear/new (or /clear)
Summarize and continue/compact/compact (or /summarize)
Resume a saved sessionclaude --resume/sessions (or /resume)
Jump back to a previous messageEsc Esc (or /rewind)/undo
Roll back the model's file editsEsc Esc (checkpointing)/undo (needs git)
Roll back bash-command file changes(not tracked)/undo (whole-tree diff)
Redo (reverse the last /undo)(not bundled)/redo
Share session/share/share

File location quick-ref

WhatClaude CodeOpenCode
Project rulesCLAUDE.mdAGENTS.md (also reads CLAUDE.md as fallback)
Project permissions.claude/settings.jsonopencode.json
Project commands.claude/commands/*.md.opencode/commands/*.md
Project skills.claude/skills/<name>/SKILL.md.opencode/skills/<name>/SKILL.md (also reads .claude/skills/)
Project hooks/plugins.claude/settings.json (hooks block).opencode/plugins/*.{js,ts}
Project subagents.claude/agents/*.md.opencode/agents/*.md
Personal/global rules~/.claude/CLAUDE.md~/.config/opencode/AGENTS.md
Personal commands/skills/agents~/.claude/{commands,skills,agents}/~/.config/opencode/{commands,skills,agents}/
MCP servers.claude/settings.json (mcp block)opencode.json (mcp block)

Extension type decision tree

Need to do the same thing repeatedly, manually?
→ Slash command (CC) / Custom command (OC)

Want the model to apply expertise automatically when a task matches?
→ Skill

Need something to happen every single time, no model judgment?
→ Hook (CC) / Plugin (OC)

Need a chunk of work done in isolation so it doesn't pollute main context?
→ Subagent

When something feels wrong

Model apologizing without progress, rewriting the same code,
hallucinating variables, contradicting earlier constraints?
→ Context is poisoned. Stop typing. Run /compact or /clear.
Don't try to fix it with another prompt.

Flashcards Study Aid


Quiz: 50 Questions on the 15 Concepts

A comprehensive bank covering every concept in the crash course. Each session shows a fresh batch of 18 questions, so retakes give you new material. Pick your tool above to ground the answers in your toolchain.

Checking access...