* fix(permissions): anchor the session plan-file match on its exact shape
isSessionPlanFile auto-allows the current session's plan file for both
read (checkReadableInternalPath) and un-prompted write
(checkEditableInternalPath). It matched with a bare
normalizedPath.startsWith(join(plansDir, planSlug)), which also accepts
any sibling whose name merely begins with the slug — {slug}nova.md,
{slug}-other.md, or a newly-created {slug}dir/ subtree. Those are not this
session's plan yet were silently readable and writable without a prompt.
Anchor on the two shapes getPlanFilePath actually emits: {slug}.md exactly,
or a {slug}-agent- prefix for subagent plans. Extract the decision into a
pure isPlanFilePath(plansDir, slug, path) helper so it can be unit-tested
without session state. normalize() still runs first, so traversal segments
can't escape the plans directory.
Same missing-separator class as the path-containment fix in #1974.
* fix(permissions): restrict the agent-plan branch to a single filename
The -agent- prefix check still matched any path beneath a lookalike
sibling directory: {plansDir}/{slug}-agent-evil/anything.md passed
startsWith and ended in .md, so both permission carve-outs granted
unprompted read and write to arbitrary files below it. The malformed
{slug}-agent-.md, which getPlanFilePath never emits, was accepted too.
Require the remainder after the prefix to be exactly one nonempty agent id
followed by .md — no path separators.
* fix(plans): keep separator-carrying agent ids in one filename component
The anchored predicate rejected any agent id containing a path separator,
but producers can emit one: TeamCreateTool accepts any nonblank team name
and teammate spawning only strips `@` from the teammate name, so a team
called `a/b` yields the path {plansDir}/{slug}-agent-writer@a/b.md. That
is a file in a subdirectory, not a plan file, so the teammate lost the
carve-out for its own plan and was blocked in plan mode.
Escape the separators where the path is built instead. Percent-escaping is
reversible, so two teammates can never collide on one plan file, and ids
without those characters are untouched -- existing plan files keep their
paths.
* fix(plans): recover plans written under the unescaped agent id
Escaping changes the pathname for teammates whose id already contains a
separator, and team names have always accepted arbitrary nonblank text --
so plans for ids like writer@a/b or writer@100% are already on disk under
the raw name. Every reader now builds the escaped name, so on upgrade the
teammate's plan reads as missing and a second file is created beside it.
getPlan falls back to the unescaped path on ENOENT and moves the file to
the escaped name. Moving rather than copying is what makes it stick: the
escaped name is the one the permission carve-out recognizes, so a plan left
at the old path would keep falling through to ordinary permission handling
on every later write. A failed move is not fatal, the content is already
read.
The recovery takes explicit paths so it is covered against a real temporary
directory rather than a mocked filesystem.
* fix(plans): confine legacy plan recovery to the plans directory
readLegacyUnescapedPlan builds the pre-escape path from the raw, unescaped
agent id so an existing file can be found. Team/agent names accept arbitrary
nonblank text, so a traversal-shaped id (`../../../etc/passwd`) collapses to
a path outside the plans directory -- which readAndMigrateLegacyPlan then
reads and renames, moving an arbitrary file. Refuse any resolved path the
plans directory does not contain before delegating.
* fix(plans): give escaped agent plans a collision-free namespace and harden recovery
The escaped filename shared a directory with legacy plans, so two distinct
teammates could map onto one file: `writer@a/b` writes the escaped
`{slug}-agent-writer@a%2Fb.md` while `writer@a%2Fb` already owns that exact
name as its raw legacy plan -- a cross-agent read and clobber. Store escaped
agent plans under a dedicated `agents/` subdirectory: a real path separator
is the one thing a raw single-component legacy name can never contain, so
the two namespaces are provably disjoint. The permission carve-out
(isPlanFilePath) recognizes the new location.
Harden legacy recovery, which reads then renames a file built from the raw
(unescaped) agent id:
- Reject any `..` segment before building the path, so `a/../{slug}` can no
longer collapse onto the main plan (or `a/../{slug}-agent-victim` onto a
sibling) and have recovery move another agent's file.
- Make migration no-clobber: never rename a legacy file over a plan already
present at the escaped path.
- Export readLegacyUnescapedPlan (with injectable plansDir/slug) so the guard
is covered through the recovery flow, not just isPathWithinPlansDir alone.
* test(plans): cover getPlan's ENOENT recovery wiring end to end
The recovery helpers are unit-tested, but nothing drove getPlan() itself
through the ENOENT fallback -- the whole user-visible fix. Add a test that
plants a legacy plan under a temp config dir and asserts getPlan() returns
its contents and migrates it into the agents/ subdirectory, serialized
under the shared mutation lock since it swaps OPENCLAUDE_CONFIG_DIR.
* fix(plans): anchor plan-file matching on the canonical encoding and harden recovery
Addresses review on the agent-plan permission carve-out.
isPlanFilePath accepted any `{slug}-agent-<x>.md` whose `<x>` had no raw
`/` or `\`, but getPlanFilePath emits only the canonical output of
encodeAgentIdForPlanFile (escapes `%`->`%25`, `/`->`%2F`, `\`->`%5C`). So a
raw-percent sibling such as `{slug}-agent-writer@100%.md` (canonical form
`...writer@100%25.md`) was auto-allowed for unprompted read/write even though
the producer never writes it. Add decodeAgentIdForPlanFile and
isCanonicalPlanFileEncoding (a component is canonical iff re-encoding its
decode reproduces it byte-for-byte) and anchor the agent branch on it. This
accepts every path the encoder can emit and rejects raw-`%`/raw-separator
lookalikes, subsuming the previous separator-only check.
Also harden legacy recovery, which reads and renames a path built from the
raw agent id:
- getPlan now treats an empty/whitespace escaped file as not-a-plan and falls
through to legacy recovery. isPlanFilePath permits a direct FileWrite/FileEdit
to the canonical escaped path before migration runs; such a stub would
otherwise permanently shadow a legacy plan that still holds content. Recovery's
no-clobber guard returns the legacy contents without renaming over the stub,
so a genuine concurrent escaped write is never lost.
- readAndMigrateLegacyPlan lstat-checks the legacy slot and refuses anything
that is not a regular file, so a symlink planted there cannot make recovery
read and rename an arbitrary target outside the plans directory.
Tests: canonical-vs-lookalike pairs for `%`/separator ids, getPlan driven
end-to-end for a separator id and for the empty-stub fallthrough, and a
symlinked legacy slot. The getPlan integration tests acquire the shared
mutation lock inside try/finally and clear the plan slug on teardown.
* fix(plans): close symlink and race gaps in plan-file recovery and the carve-out
Second review pass on the agent-plan hardening.
- Symlinked path components no longer bypass the lexical carve-out. The plan-file
permission grant (isSessionPlanFile) now resolves the deepest existing ancestor
of the target and requires it to stay within the *resolved* plans directory, so
a symlinked `agents` subdir (or plans dir) that redirects the real file outside
the plans directory is refused instead of auto-allowed. Legacy recovery gets the
same containment check, closing the slash-bearing-id case where a symlinked
intermediate `{slug}-agent-writer@a` parent passed the prefix checks and leaf
lstat.
- Migration is now a genuine no-clobber move: linkSync (atomic, fails EEXIST)
replaces the existsSync-then-renameSync check-then-act race that could replace a
concurrently-created live plan on POSIX. The escaped hard link pins the inode we
lstat'd, and we read through it, so a symlink swap of the legacy pathname cannot
redirect the read. Reads verify the inode/device are unchanged across the read.
- Traversal validation uses the host platform's real separators: on POSIX `\` is a
legal filename character, so a legacy id like `a\..\b` (persisted as one flat
filename) recovers again instead of being wrongly rejected; Windows still treats
both `/` and `\` as separators.
Tests: symlinked intermediate directory rejection (helper + recovery), POSIX
literal-backslash recovery, genuine-move semantics. All fail on the pre-fix code.
* refactor(permissions): reuse the shared plans-dir containment helper
Drop the duplicate isResolvedWithinPlansDir in the permission layer and route
the session plan-file carve-out through the exported isResolvedPathWithinPlansDir
from plans.ts, keeping the symlink-containment logic in one place. Guard the two
symlink-based tests on non-Windows so they skip where symlinkSync needs
privileges.
OpenClaude is an open-source coding-agent CLI for cloud and local model providers.
Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex OAuth, Codex, Ollama, Atomic Chat, and other supported backends while keeping one terminal-first workflow: prompts, tools, agents, MCP, slash commands, and streaming output.
OpenClaude is also mirrored to GitLawb: gitlawb.com/node/repos/z6MkqDnb/openclaude
Quick Start | Setup Guides | Providers | Development | VS Code Extension | Partners | Community
Partners
|
|
|
|
|
| GitLawb | Bankr.bot | Atomic Chat | Xiaomi MiMo | Atlas Cloud |
|
|
|
|||
| AI/ML API | Novita AI |
Why OpenClaude
- One CLI across cloud APIs and local model backends — no per-provider tooling
- Guided provider setup and saved profiles with
/provider - Coding-agent workflows in one place: bash, file tools, grep, glob, agents, tasks, MCP, and web tools
- A bundled VS Code extension for launch integration and theme support
- A pixel-art hero companion who fires an arrow every time you press Enter (really — see Meet your buddy)
Quick Start
Install
OpenClaude requires Node.js >=22.0.0 for npm installs and runtime. Bun is
only needed for source builds and local development.
npm install -g @gitlawb/openclaude@latest
If you're on Arch Linux, you can install OpenClaude from the community-maintained AUR package:
paru -S openclaude
If the install later reports ripgrep not found, install ripgrep system-wide and confirm rg --version works in the same terminal before starting OpenClaude.
Verify / troubleshoot installed version:
openclaude --version
npm view @gitlawb/openclaude dist-tags
npm install -g @gitlawb/openclaude@latest
Start
openclaude
Inside OpenClaude:
- run
/providerfor guided provider setup and saved profiles - run
/onboard-githubfor GitHub Models onboarding
Note: OpenClaude does not automatically load project
.envfiles. We recommend using the/providercommand for setup, which saves provider profiles and credentials in.openclaude-profile.json. If you prefer environment variables, export them explicitly or runopenclaude --provider-env-file .envfor provider/setup variables. Export runtime/debug knobs from your shell or launcher.
Resume or fork a conversation
Resume an existing conversation by session ID, or continue the most recent conversation in the current directory:
openclaude --resume <session-id>
openclaude --continue
Add --fork-session to branch the conversation history into a new session ID
instead of reusing the original transcript:
openclaude --resume <session-id> --fork-session
openclaude --continue --fork-session
Forking is conversation branching only. It does not create filesystem isolation, copy your working tree, or create a git worktree branch.
Background sessions
Run long non-interactive prompts detached from the current terminal:
openclaude --bg "fix failing tests"
openclaude --bg --name auth-refactor "refactor auth middleware"
openclaude ps
openclaude logs auth-refactor
openclaude logs auth-refactor -f
openclaude kill auth-refactor
Background sessions are local child processes. OpenClaude does not start a daemon
or network service, and permission/provider/model/settings flags are passed to
the child process the same way they are for a foreground --print run. Session
metadata and logs are stored under the resolved OpenClaude config directory,
usually ~/.openclaude/bg-sessions/; OPENCLAUDE_CONFIG_DIR can point
OpenClaude somewhere else. CLAUDE_CONFIG_DIR is ignored for OpenClaude
background-session storage. Session names can be reused after older sessions
reach a terminal state; use the session ID to inspect older logs with the same
name.
openclaude attach <id-or-name> currently reports the matching session and
points to openclaude logs <id> -f; full terminal reattach is not implemented
for local background sessions yet.
OpenClaude config cutover
OpenClaude stores its own config under ~/.openclaude and ~/.openclaude.json
by default. It does not read ~/.claude, project .claude/ directories, or
CLAUDE_CONFIG_DIR; new users can start with an empty OpenClaude config and do
not need Claude Code installed.
If you previously used OpenClaude with .claude paths, migrate intentionally:
copy only the settings, commands, agents, skills, scheduled tasks, or other files
you personally created for OpenClaude into the matching .openclaude location.
Do not blanket-copy .claude, and do not copy Claude Code credentials or auth
files. For provider authentication, prefer running OpenClaude's provider setup
again or exporting provider-specific environment variables.
Fastest OpenAI setup
macOS / Linux:
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-your-key-here
export OPENAI_MODEL=gpt-4o
openclaude
Windows PowerShell:
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_API_KEY="sk-your-key-here"
$env:OPENAI_MODEL="gpt-4o"
openclaude
Fastest local Ollama setup
macOS / Linux:
export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_MODEL=qwen2.5-coder:7b
openclaude
Windows PowerShell:
$env:CLAUDE_CODE_USE_OPENAI="1"
$env:OPENAI_BASE_URL="http://localhost:11434/v1"
$env:OPENAI_MODEL="qwen2.5-coder:7b"
openclaude
For Ollama, OpenClaude uses Ollama's native chat API and requests a 32768-token
context window on each chat request so same-session history is not silently
truncated by Ollama's OpenAI-compatible shim. Set OPENCLAUDE_OLLAMA_NUM_CTX
or OLLAMA_CONTEXT_LENGTH if you need a different request-level context size.
See Advanced Setup for
verification with ollama ps.
Setup Guides
Beginner-friendly guides:
Advanced and source-build guides:
- Advanced Setup
- Smart Auto-Routing
- Agent Routing and Step Limits
- Headless gRPC Server
- Repo Map (codebase intelligence)
- Android Install
Supported Providers
| Provider | Setup Path | Notes |
|---|---|---|
| OpenAI-compatible | /provider or env vars |
Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and other compatible /v1 servers |
| Z.AI GLM Coding Plan | /provider or OpenAI-compatible env vars |
Uses OPENAI_API_KEY at https://api.z.ai/api/coding/paas/v4 and defaults to glm-5.2 |
| AI/ML API | /provider or AIMLAPI_API_KEY (setup guide) |
Uses https://api.aimlapi.com/v1, auto-detects the OpenAI-compatible route from AIMLAPI_API_KEY, sends OpenClaude attribution headers, and discovers chat-capable models from the public /models catalog |
| Hicap | /provider or OpenAI-compatible env vars |
Uses api-key auth, discovers models from unauthenticated /models, and supports Responses mode for gpt- models |
| Fireworks AI | /provider or env vars |
First-class provider with 276 curated models (DeepSeek, Qwen, Llama, Gemma, and more); uses FIREWORKS_API_KEY |
| LongCat | /provider or env vars |
Meituan LongCat OpenAI-compatible API at https://api.longcat.chat/openai/v1; uses LONGCAT_API_KEY and defaults to LongCat-2.0 |
| ClinePass | /provider or env vars |
AI model gateway with usage limits (5hr, weekly, monthly); uses CLINE_API_KEY at https://api.cline.bot/api/v1 |
| Gemini | /provider or env vars |
Supports API key only |
| GitHub Models | /onboard-github |
Interactive onboarding with saved credentials |
| Codex OAuth | /provider |
Opens ChatGPT sign-in in your browser and stores Codex credentials securely |
| Codex | /provider |
Uses existing Codex CLI auth, OpenClaude secure storage, or env credentials |
| Gitlawb Opengateway | Startup default, /provider, or env vars |
Smart gateway at https://opengateway.gitlawb.com/v1; requires an API key from https://gitlawb.com/opengateway/keys and routes Xiaomi MiMo and GMI Cloud partner models by OPENAI_MODEL |
| OpenCode Zen | /provider or env vars |
Pay-as-you-go AI gateway (48 models); uses OPENCODE_API_KEY via https://opencode.ai/zen/v1; shared key with OpenCode Go |
| OpenCode Go | /provider or env vars |
$10/mo subscription for open models (13 models); uses OPENCODE_API_KEY via https://opencode.ai/zen/go/v1; shared key with OpenCode Zen |
| Xiaomi MiMo | /provider or env vars |
OpenAI-compatible API at https://mimo.mi.com; uses MIMO_API_KEY and defaults to mimo-v2.5-pro |
| NEAR AI | /provider or env vars |
Unified gateway (Claude, GPT, Gemini + TEE open models); uses NEARAI_API_KEY at https://cloud-api.near.ai/v1 |
| Cloudflare Workers AI | /provider or env vars |
OpenAI-compatible API at https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1; uses CLOUDFLARE_API_TOKEN. Replace <ACCOUNT_ID> with your Cloudflare account id. |
| Ollama | /provider or env vars |
Local inference with no API key |
| Atomic Chat | /provider, env vars, or bun run dev:atomic-chat |
Local Model Provider; auto-detects loaded models |
| Bedrock / Vertex / Foundry | env vars | Anthropic-family cloud routes; Vertex is for Claude on Vertex AI, not arbitrary Model Garden models |
What Works
- Tool-driven coding workflows: Bash, file read/write/edit, grep, glob, agents, tasks, MCP, and slash commands
- Streaming responses: Real-time token output and tool progress
- Tool calling: Multi-step tool loops with model calls, tool execution, and follow-up responses
- Images: URL and base64 image inputs for providers that support vision
- Provider profiles: Guided setup plus saved user-level provider profile support
- Local and remote model backends: Cloud APIs, local servers, and Apple Silicon local inference
- Codebase intelligence (repo map): Structural map of the repository ranked by PageRank importance, auto-injected into context when the
REPO_MAPflag is enabled or theREPO_MAPenvironment variable is set. Inspect with/repomap(2048-token default). See docs/repo-map.md for details. - A companion with signature moves: A truecolor pixel-art hero who lives beside your prompt and reacts when you work. See below.
Meet Your Buddy
Run /buddy to hatch a companion — a truecolor pixel-art hero who stands
beside your prompt, idles, blinks, and fires their signature move every time
you submit a message:
/buddy hatch (first run) or pet your companion
/buddy set robinhood the green archer — arrow shot on every Enter
/buddy set kaio gold-haired warrior — charges a full-width energy wave
/buddy set strawhat stretchy punch that snaps back
/buddy set merlin twinkling sparkle stream
/buddy set kage spinning shuriken
/buddy set ember dragon fire with a real heat gradient
/buddy set corsair cannonball with smoke trail
/buddy name Robin rename your companion
/buddy set random back to your rolled hero
Companions respect prefersReducedMotion, degrade gracefully to line art in
low-color terminals, and can be silenced with /buddy mute. Requires a
terminal at least 100 columns wide for the full sprite.
Provider Notes
OpenClaude supports multiple providers, but behavior is not identical across all of them.
- Anthropic-specific features may not exist on other providers
- Tool quality depends heavily on the selected model
- Smaller local models can struggle with long multi-step tool flows
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
- AI/ML API uses the OpenAI-compatible route, defaults to
gpt-4o, and only surfaces chat-capable models from its public catalog - Gitlawb Opengateway is the fresh-install startup default and requires an API key from https://gitlawb.com/opengateway/keys. It uses one OpenAI-compatible base URL; switch between
mimo-*andgoogle/gemini-3.1-flash-lite-previewwith/model, and do not pin the base URL to/v1/xiaomi-mimo. - Z.AI GLM Coding Plan uses
https://api.z.ai/api/coding/paas/v4withglm-5.2by default. Useglm-5.2?reasoning=highfor enhanced reasoning,glm-5.2?reasoning=xhighto request Z.AIreasoning_effort=max, orglm-5.2?thinking=disabledfor faster direct answers. - Xiaomi MiMo uses
api-keyheader auth on the direct OpenAI-compatible route and currently does not support/usagereporting in OpenClaude - GitHub Copilot serializes sub-agent execution by default to reduce Premium Request consumption — see Agent Routing and Step Limits for tuning
For best results, use models with strong tool/function calling support.
Agents
Route different agents to different models (cost optimization, splitting work
by model strength), cap sub-agent tool steps with maxSteps, and tune GitHub
Copilot sub-agent behavior. All settings-driven:
- per-agent provider/model overrides via
agentModels+agentRoutingin~/.openclaude.json - model-only routes that reuse your current provider's credentials
- built-in agents (
Explore,Plan,verification) routable by type name
See Agent Routing and Step Limits for the full guide.
Web Search and Fetch
By default, WebSearch works on non-Anthropic models using DuckDuckGo. This gives GPT-4o, DeepSeek, Gemini, Ollama, and other OpenAI-compatible providers a free web search path out of the box.
Note: DuckDuckGo fallback works by scraping search results and may be rate-limited, blocked, or subject to DuckDuckGo's Terms of Service. If you want a more reliable supported option, configure Firecrawl.
For Anthropic-native backends and Codex responses, OpenClaude keeps the native provider web search behavior.
WebFetch works, but its basic HTTP plus HTML-to-markdown path can still fail on JavaScript-rendered sites or sites that block plain HTTP requests.
Set a Firecrawl API key if you want Firecrawl-powered search/fetch behavior:
export FIRECRAWL_API_KEY=your-key-here
With Firecrawl enabled:
WebSearchcan use Firecrawl's search API while DuckDuckGo remains the default free path for non-Claude modelsWebFetchuses Firecrawl's scrape endpoint instead of raw HTTP, handling JS-rendered pages correctly
Free tier at firecrawl.dev includes 500 credits. The key is optional.
Headless gRPC Server
OpenClaude can run as a headless gRPC service with bidirectional streaming —
integrate its agentic capabilities into other applications, CI/CD pipelines,
or custom UIs. Start it with npm run dev:grpc; a test CLI client ships with
the repo. See Headless gRPC Server for configuration
and client generation from src/proto/openclaude.proto.
Development
Use Node.js >=22.0.0 and Bun 1.3.13 or newer for source builds.
bun install
bun run build
node dist/cli.mjs
Day-to-day commands:
bun run dev— build and launch from sourcebun test— full unit suite (Bun's built-in runner)bun test path/to/file.test.ts— focused runs for the areas you touchbun run test:coverage— coverage tocoverage/lcov.infoplus a visual report atcoverage/index.html(bun run test:coverage:uirebuilds just the UI)bun run smoke— smoke checksbun run doctor:runtime,bun run verify:privacy,bun run security:pr-scan -- --base origin/main
Focused suites: bun run test:provider, bun run test:provider-recommendation.
Recommended validation before opening a PR:
bun run buildbun run smokebun run test:coveragewhen your change affects shared runtime or provider logic- focused
bun test ...runs for the files and flows you changed
Repository Structure
src/- core CLI/runtimescripts/- build, verification, and maintenance scriptsdocs/- setup, contributor, and project documentationvscode-extension/openclaude-vscode/- VS Code extension.github/- repo automation, templates, and CI configurationbin/- CLI launcher entrypoints
VS Code Extension
The repo includes a VS Code extension in vscode-extension/openclaude-vscode for OpenClaude launch integration, provider-aware Control Center, in-editor chat, theme support, and optional Microsoft Foundry / Azure OpenAI configuration (endpoint, API version, deployment, API key via Secret Storage) injected into launched terminals. See that folder's README.
Security
If you believe you found a security issue, see SECURITY.md.
Community
- Use GitHub Discussions for Q&A, ideas, and community conversation
- Use GitHub Issues for confirmed bugs and actionable feature work
- Join the Discord to chat with the community in real time
- Follow @gitlawb on X for updates and announcements
Contributing
Contributions are welcome. For larger changes, open an issue first so the scope is clear before implementation. See Development for the build, test, and pre-PR validation commands.
Disclaimer
OpenClaude is an independent community project and is not affiliated with, endorsed by, or sponsored by Anthropic.
OpenClaude originated from the Claude Code codebase and has since been substantially modified to support multiple providers and open use. "Claude" and "Claude Code" are trademarks of Anthropic PBC. See LICENSE for details.
License
MIT for OpenClaude contributors' modifications; the derived Claude Code remains Anthropic's. See more.
