5 min read

OpenCode: The Open-Source Coding Agent for Any Model (GitHub, Scanned)

An open-source terminal coding agent that works with any model, with a desktop app and a read-only plan mode.

OpenCode logo
✅
Scan: safe. Nothing malicious. Know that the default build agent has full access to your files and shell, that the dependency check could not read its Bun lockfile, and that one script in patches/ installs a contributor's fork. Scanned Sep 26, 2026; the full report is below.

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 | bash

2. Or with npm (bun, pnpm and yarn work too)

npm i -g opencode-ai@latest

3. Start it in a project, add a provider, then press Tab to switch between the build and plan agents

cd your-project && opencode

The 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

CheckResult
Secrets1 candidate found and read; see the notes above.
Suspicious code8 pattern hits found and read; every one is listed under the raw findings.
Install-time code2 npm lifecycle scripts. 1 installer script (one fetches and runs a remote script)
Committed binariesNone.
CI workflows27 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 hosts40 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 vulnerabilitiesNo lockfile to check: dependencies are declared as ranges, so what gets installed is whatever is current on the day.
Project hygieneHas security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL.
OpenSSF ScorecardNot 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)
WhereRuleMatch
packages/http-recorder/test/record-replay.test.ts:138google-api-keyAIzaSy…qCE (39 chars)
Pattern hits (8)
WhereRuleMatch
install:23download-piped-to-shellcurl -fsSL https://opencode.ai/install | bash
install:24download-piped-to-shellcurl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180
packages/containers/bun-node/Dockerfile:21download-piped-to-shellcurl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}"; \
packages/containers/rust/Dockerfile:11download-piped-to-shellcurl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}"; \
packages/desktop/src/main/wsl/runtime.ts:271download-piped-to-shell["bash", "-lc", `curl -fsSL https://opencode.ai/install | bash -s -- --version ${shellEscape(version)}`],
packages/httpapi-codegen/src/index.ts:537very-long-line6412 chars
patches/install-korean-ime-fix.sh:11download-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:117download-piped-to-shellecho " curl -fsSL https://opencode.ai/install | bash"
URLs to bare IP addresses (14)
WhereRuleMatch
packages/core/test/tool-webfetch.test.ts:171ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "markdown" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:175ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "text" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:192ip-literal-urlconst url = "https://1.1.1.1/deep-html"
packages/core/test/tool-webfetch.test.ts:211ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1/declared", format: "text" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:213ip-literal-urlvalue: "Unable to fetch https://1.1.1.1/declared",
packages/core/test/tool-webfetch.test.ts:220ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1/streamed", format: "text" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:222ip-literal-urlvalue: "Unable to fetch https://1.1.1.1/streamed",
packages/core/test/tool-webfetch.test.ts:232ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1/image", format: "html" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:234ip-literal-urlvalue: "Unable to fetch https://1.1.1.1/image",
packages/core/test/tool-webfetch.test.ts:238ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1/file", format: "html" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:240ip-literal-urlvalue: "Unable to fetch https://1.1.1.1/file",
packages/core/test/tool-webfetch.test.ts:257ip-literal-urlexpect(yield* executeTool(registry, call({ url: "https://1.1.1.1", format: "text" }))).toEqual({
packages/core/test/tool-webfetch.test.ts:274ip-literal-urlcall({ url: "https://1.1.1.1/slow", format: "text", timeout: 1 }),
packages/core/test/tool-webfetch.test.ts:278ip-literal-urlexpect(yield* Fiber.join(fiber)).toEqual({ type: "error", value: "Unable to fetch https://1.1.1.1/slow" })
npm lifecycle scripts (2)
  • package.json postinstall: bun run --cwd packages/core fix-node-pty
  • package.json prepare: husky
Installer scripts (1)
Workflows worth a look

By the numbers

Stars210.3K
Forks27.8K
Contributors1,017
Commits15.8K
Open issues4,690
Open pull requests1,511
Releases874
Latest releasev1.18.32
LicenceMIT
Main languageTypeScript
Project age1 year
Last pushSep 27, 2026
Tracked files6,578
Lines of code1.2M
Checkout size132 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.