OpenCode is a coding agent with a polished terminal interface: it reads your project, edits files, runs commands and loops on the results, much like Claude Code or Codex, but with no ties to one model vendor. It connects to Anthropic, OpenAI, Google, GitHub Copilot, OpenRouter and local models, and ships two agents you switch between with Tab: build, which has full access, and plan, which cannot edit files and asks before running any command. A desktop app for macOS, Windows and Linux is in beta.
It is the most-starred repository on this list, with more than 200,000 stars and over a thousand contributors in under a year and a half, and releases most days. The code is MIT-licensed TypeScript run on Bun, maintained by the company Anomaly. The company's business is OpenCode Zen, an optional pay-as-you-go gateway of tested models, some of them free; the agent works the same with your own keys.
- Repository: github.com/anomalyco/opencode
- Licence: MIT (MIT License)
- Language: TypeScript. Stars: 210.3K. Forks: 27.8K. Last push: Sep 27, 2026.
- Scan: safe, Sep 26, 2026, commit b471c2b
Who it is for
Developers who want a Claude Code style agent without committing to one vendor's models, people with several subscriptions or API keys who want one tool for all of them, and anyone who wants to read or change the agent they run.
Getting started
1. Install with the script on macOS or Linux (or brew install anomalyco/tap/opencode; scoop or choco on Windows)
curl -fsSL https://opencode.ai/install | bash2. Or with npm (bun, pnpm and yarn work too)
npm i -g opencode-ai@latest3. Start it in a project, add a provider, then press Tab to switch between the build and plan agents
cd your-project && opencodeThe README labels the curl | bash line YOLO itself. The script, install in the repository, is 460 lines: it downloads the release from GitHub and adds its folder to your PATH through your shell config unless you pass --no-modify-path. The package managers are the quieter route.
Safety scan
We cloned anomalyco/opencode at commit b471c2b on Sep 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- One secret candidate, a Google API key string in packages/http-recorder/test/record-replay.test.ts. It is sample data in a test for the HTTP recorder that must scrub keys from saved sessions, next to an equally fake sk- key. No self-decoding code and no committed binaries; the bare IPs are 1.1.1.1 URLs in the web-fetch tool's tests.
- The pattern hits are the install script's own usage text, the desktop app's WSL support running the official installer inside WSL, and two container Dockerfiles installing Bun and Rust. The one that stands out is patches/install-korean-ime-fix.sh: it clones a contributor's fork (claudianus/opencode) from GitHub, builds it, and replaces your opencode binary with the result, to fix Korean input. It does what it says and runs only if you run it, but it installs code outside the main project's review.
- The root package.json has a postinstall that fixes node-pty's build and a husky prepare hook; both run for contributors working in the monorepo, not for users installing the published package.
- Dependencies could not be checked. The project uses Bun, whose bun.lock the scanner does not read, so there is no advisory count to report, and that is an absence of data, not a clean result.
- 27 workflows. Three were flagged for pull_request_target: two check out the base branch rather than the pull request, and review.yml actually runs on /review comments from owners and members. All 20 third-party actions are pinned to a commit. Security policy, licence and contributing guide present; no Dependabot or CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 1 candidate found and read; see the notes above. |
| Suspicious code | 8 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 2 npm lifecycle scripts. 1 installer script (one fetches and runs a remote script) |
| Committed binaries | None. |
| CI workflows | 27 workflows. 3 use pull_request_target, none check out the pull request head. 0 of 20 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often opencode.ai, github.com, example.test, raw.githubusercontent.com. 14 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | No lockfile to check: dependencies are declared as ranges, so what gets installed is whatever is current on the day. |
| Project hygiene | Has security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (1, redacted)
| Where | Rule | Match |
|---|---|---|
| packages/http-recorder/test/record-replay.test.ts:138 | google-api-key | AIzaSy…qCE (39 chars) |
Pattern hits (8)
| Where | Rule | Match |
|---|---|---|
| install:23 | download-piped-to-shell | curl -fsSL https://opencode.ai/install | bash |
| install:24 | download-piped-to-shell | curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180 |
| packages/containers/bun-node/Dockerfile:21 | download-piped-to-shell | curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}"; \ |
| packages/containers/rust/Dockerfile:11 | download-piped-to-shell | curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}"; \ |
| packages/desktop/src/main/wsl/runtime.ts:271 | download-piped-to-shell | ["bash", "-lc", `curl -fsSL https://opencode.ai/install | bash -s -- --version ${shellEscape(version)}`], |
| packages/httpapi-codegen/src/index.ts:537 | very-long-line | 6412 chars |
| patches/install-korean-ime-fix.sh:11 | download-piped-to-shell | # curl -fsSL https://raw.githubusercontent.com/claudianus/opencode/fix-zhipuai-coding-plan-thinking/patches/install-korean-ime-fix.sh | bash |
| patches/install-korean-ime-fix.sh:117 | download-piped-to-shell | echo " curl -fsSL https://opencode.ai/install | bash" |
URLs to bare IP addresses (14)
| Where | Rule | Match |
|---|---|---|
| packages/core/test/tool-webfetch.test.ts:171 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "markdown" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:175 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "text" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:192 | ip-literal-url | const url = "https://1.1.1.1/deep-html" |
| packages/core/test/tool-webfetch.test.ts:211 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1/declared", format: "text" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:213 | ip-literal-url | value: "Unable to fetch https://1.1.1.1/declared", |
| packages/core/test/tool-webfetch.test.ts:220 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1/streamed", format: "text" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:222 | ip-literal-url | value: "Unable to fetch https://1.1.1.1/streamed", |
| packages/core/test/tool-webfetch.test.ts:232 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1/image", format: "html" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:234 | ip-literal-url | value: "Unable to fetch https://1.1.1.1/image", |
| packages/core/test/tool-webfetch.test.ts:238 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1/file", format: "html" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:240 | ip-literal-url | value: "Unable to fetch https://1.1.1.1/file", |
| packages/core/test/tool-webfetch.test.ts:257 | ip-literal-url | expect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "text" }))).toEqual({ |
| packages/core/test/tool-webfetch.test.ts:274 | ip-literal-url | call({ url: "https://1.1.1.1/slow", format: "text", timeout: 1 }), |
| packages/core/test/tool-webfetch.test.ts:278 | ip-literal-url | expect(yield* Fiber.join(fiber)).toEqual({ type: "error", value: "Unable to fetch https://1.1.1.1/slow" }) |
npm lifecycle scripts (2)
package.jsonpostinstall:bun run --cwd packages/core fix-node-ptypackage.jsonprepare:husky
Installer scripts (1)
- patches/install-korean-ime-fix.sh, 121 lines, fetches and runs a remote script; talks to github.com, opencode.ai, raw.githubusercontent.com
Workflows worth a look
- .github/workflows/pr-management.yml: pull_request_target
- .github/workflows/pr-standards.yml: pull_request_target
- .github/workflows/review.yml: pull_request_target
By the numbers
| Stars | 210.3K |
|---|---|
| Forks | 27.8K |
| Contributors | 1,017 |
| Commits | 15.8K |
| Open issues | 4,690 |
| Open pull requests | 1,511 |
| Releases | 874 |
| Latest release | v1.18.32 |
| Licence | MIT |
| Main language | TypeScript |
| Project age | 1 year |
| Last push | Sep 27, 2026 |
| Tracked files | 6,578 |
| Lines of code | 1.2M |
| Checkout size | 132 MB |
Lines by language: TypeScript 686.6K, JSON 258.8K, Markdown 233.3K, CSS 43.2K, YAML 2,995, HTML 1,248.
Questions
Is OpenCode free?
Yes. OpenCode is MIT-licensed and free, and it works with keys or subscriptions you already have, or with local models. OpenCode Zen, the team's optional pay-as-you-go model gateway, bills per token and includes some free models. You never need it: bring-your-own-key works the same, and your only cost is the model you choose.
How does OpenCode compare with Claude Code?
They work the same way: a terminal agent that reads, edits and runs commands in your repository. Claude Code is closed source and uses Anthropic's models. OpenCode is open source and model-agnostic, so you can switch between Claude, GPT, Gemini and local models in one session, and it adds a desktop app and a read-only plan agent.
Does OpenCode upload my sessions?
Your code goes to the model provider you choose, as with any agent. Sessions stay local unless you run /share, which uploads the full conversation to OpenCode's servers and creates a public link. Sharing is manual by default, and setting "share": "disabled" in opencode.json turns it off, including for a whole team when committed to the repository.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
