@oaktreeai/cli
v0.1.39
Published
OakTree AI terminal coding agent.
Readme
OakTree AI CLI
The OakTree AI terminal coding agent — an interactive, AI-powered coding agent that runs in your terminal.
Installation
Install globally so the oaktree command is on your PATH (requires Node.js 22+):
npm install -g @oaktreeai/cliTo upgrade later, run npm install -g @oaktreeai/cli@latest. To run it without a global install, use npx @oaktreeai/cli.
A plain
npm install @oaktreeai/cli(without-g) only adds the package to a localnode_modulesand does not putoaktreeon yourPATH— you'd getcommand not found: oaktree. Use-gfor the command, or invoke it vianpx.
Usage
The CLI uses React Ink for the interactive terminal renderer and defaults to the local ASP.NET Core API at http://127.0.0.1:5050, using versioned REST routes under /api/v1 and the SignalR hub at /realtime/agent-session for live sessions:
oaktree auth login
oaktree
oaktree --api-url http://127.0.0.1:5050 --ws-url http://127.0.0.1:5050/realtime/agent-session doctorHeadless one-shot execution is available without importing the Ink renderer:
oaktree exec --print "summarize this repo"
oaktree exec --json --model fast --max-turns 40 --max-tokens 200000 --message-file prompt.md
oaktree exec --permission-mode accept-all --print "update the changelog and run the unit tests"exec is read-only by default. --permission-mode accept-edits permits workspace file tools, accept-all also permits sandboxed shell commands, and bypass disables permission checks and sandboxing and should only be used inside an isolation boundary you control. --model pins the advertised model tier without reading or writing interactive settings. --max-turns and --max-tokens provide server-enforced automation budgets, while --continue resumes the newest restorable cached session for the current workspace. --json emits newline-delimited events with schema_version: 2; --print emits a stable plain-text transcript suitable for normal terminal scrollback. The interactive app renders on the main terminal screen with native scrollback for the transcript; completed entries are committed to the terminal's scrollback buffer, and only the active assistant response stays in the live frame.
Permissions and sandboxing
Workspace reads are allowed automatically. Interactive sessions ask before edits and commands, with choices to allow once, allow that exact request for the session, or deny. Use --permission-mode read-only to deny all mutations, accept-edits to allow workspace edits while continuing to ask before commands, accept-all to allow edits and sandboxed commands, or bypass to disable permission checks and command sandboxing. Dedicated file tools remain confined to the selected workspace unless permission checks are explicitly bypassed.
Shell commands use best-effort OS isolation: Seatbelt on macOS and a probed system bwrap installation on Linux. Sandboxed commands can write only to the workspace and their temporary directory, and direct external network access is disabled. Windows does not expose an equivalent secure sandbox through Node.js, so the CLI labels shell execution as full computer access and asks before running it. Headless mode fails closed when a sandbox is unavailable unless --permission-mode bypass was supplied. The CLI never silently retries a failed sandboxed command without isolation.
The command sandbox is defense-in-depth, not a VM or a complete confidentiality boundary: commands retain read access needed for the operating system and installed toolchains. On Linux the CLI hides conventional runtime directories and common control-socket locations, but a custom pathname socket can still expose local IPC. Review the displayed command, and use a disposable VM or container when executing code you do not trust.
Run oaktree doctor or /status to see the effective sandbox provider and any degraded-mode explanation.
Windows support
Windows (x64) is a supported, CI-gated platform: the full lint, type-check, test, build, and packaged-installer smoke suite runs on windows-latest alongside the Linux job. The Windows build guarantees:
- Trusted executable resolution.
gitis resolved to an absolute path outside the workspace (PATH order is respected, but agit.exeplanted inside the workspace is never selected), and system binaries the CLI invokes itself —cmd.exe,taskkill.exe,powershell.exe— are resolved from%SystemRoot%\System32, never through a PATH or current-directory search. When no trusted system root is available, the affected operation fails closed instead of falling back to a bare executable name. - DPAPI-protected credentials. The login token and MCP OAuth credentials are encrypted at rest with Windows DPAPI (
CurrentUserscope, via PowerShell), so on-disk secrets are bound to the signed-in Windows user. An envelope that no longer decrypts is treated as no stored credential and simply requires signing in again. If DPAPI is unavailable, the login token falls back to the same plain-file store other platforms without a keychain use. - Full process-tree termination. Timeouts, cancellations,
shell_kill, MCP server disposal, and CLI shutdown terminate the entire process tree withtaskkill /t /f(with a direct kill as fallback), so wrapper chains likenpm.cmd → nodecannot leak children. - CRLF-aware file tools.
read_filepresents LF-normalized lines and reports the file'sline_endings;edit_filematches uniformly-CRLF files in LF space and writes them back in their own convention.
Explicitly not provided on Windows:
- No OS sandbox. Windows has no supported equivalent of Seatbelt/bwrap for a pure-Node CLI, so the sandbox layer fails closed: every shell command is classified as full computer access and requires explicit approval (headless mode refuses to run commands without
--permission-mode bypass). Process-tree termination usestaskkillrather than Job Objects, which would require a native addon — a child that spawns faster thantaskkillenumerates could in principle escape; the OS-level guarantee needsKILL_ON_JOB_CLOSEJob Objects. - No Windows ARM64 packaging or CI. The CLI is pure JavaScript and should run under the x64 emulator or native Node on ARM64, but nothing is built or tested for that target yet; an ARM64 runner can be added to the CI matrix when it becomes a supported platform.
Editing earlier turns and workspace restore
/edit forks the conversation from an earlier user turn. In a Git workspace, the CLI creates a private shadow commit at every turn boundary and requires that checkpoint to finish before any mutating tool can run. The shadow repository lives under the CLI state directory; capture and restore use raw Git plumbing and never write the repository's real index, object database, refs, stash, HEAD, or reflog. When you resubmit an edited turn, the CLI can rewind every tracked and non-ignored untracked file in the repository to its exact byte state before that turn, including changes made by shell commands, codemods, package managers, and generators. The confirmation is intentionally destructive: later Git-visible manual changes are rewound too. Ignored files (for example node_modules and build output), empty directories, submodule working-tree contents, Git history/index state, external processes, and writes outside the repository are not captured. A rewind aborts before mutation if a tracked restore target now conflicts with ignored content. OakTree-owned background commands are stopped before rewind; if checkpoint creation or safe process teardown fails, no mutation or rewind proceeds. A failed rewind is rolled back from a second byte-exact snapshot; if rollback also fails, that recovery ref is retained and reported instead of being garbage-collected. Editing a turn that has no usable checkpoint (a session recorded before checkpoints existed, an expired snapshot, or a boundary whose capture failed) submits without a rewind offer and notes that repository files were kept as they are.
Outside a Git repository, /edit retains the file-tool fallback: the CLI records the first pre-image for each path written by edit_file or write_file, skips files changed externally, and discloses that shell mutations cannot be restored. A failed restore never submits the edited turn.
Canonical assistant transcript rendering, composer input, slash commands, the / command picker, in-app /login and /logout, model switching, cancellation, session activation, deferred client-tool registration and execution, browser auth, file attachments, artifact actions, and cache-assisted session picking are available.
Attachments can be added with /attach <workspace-path>, /attach clipboard, /paste, or Ctrl+V when your terminal forwards that key to the app. Clipboard attachment paste supports copied screenshots/images and copied files through the native system clipboard on macOS, Windows, and supported Linux desktops. If a Linux compositor or CPU/libc target cannot load the native clipboard binding, oaktree doctor reports that limitation instead of crashing the CLI.
The CLI automatically sends compact local context with each prompt, including the workspace root, current date, OS, shell and shell version when available, terminal hints, Node.js version, and CLI version. This context is sent through hidden prompt metadata, so the visible transcript still shows only the message you typed.
MCP servers
The CLI is a Model Context Protocol client for the tools capability. Servers are declared under mcpServers in ~/.config/oaktreeai/settings.json, <workspaceRoot>/.oaktree/settings.json, or <workspaceRoot>/.oaktree/settings.local.json, using the same layering as permission rules. Discovered tools are registered as ordinary client tools named mcp__<server>__<tool> and run through the normal permission, preview, cancellation, and result-budget path.
{
"mcpServers": {
"github": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"},
"tools": {"allow": ["get_issue", "create_issue"]},
"readOnlyTools": ["get_issue"]
}
}
}transport is stdio, streamable-http, or sse. startupTimeoutMs (default 10000) bounds every connection attempt. requestTimeoutMs (default 60000) is the flat catalog-listing timeout and the maximum silence allowed during a tool call; each progress notification restarts that tool-call clock. maxRequestTimeoutMs (default 600000, or requestTimeoutMs when configured higher) is the hard ceiling for one tool call regardless of progress and must not be lower than requestTimeoutMs. env, headers, oauth.clientId, and oauth.clientSecret expand ${VAR} and ${VAR:-default} only after the declaring project file is trusted, and an unresolved required variable disables that one server with a diagnostic rather than starting it.
Project-supplied mcpServers execute code, so they stay inert until oaktree project trust pins that exact file's bytes; the trust prompt discloses the executable, arguments, resolved working directory, and referenced environment variable names without their values. Every MCP tool is treated as a write by default, asks for permission, and is blocked in plan mode; readOnlyTools is a local claim that opts specific tools into the read path. The permission panel offers a whole-tool mcp__<server>__<tool>(*) rule for durable approval.
Built-in and MCP tools share one registration budget, so tools.allow is the expected control for large catalogs; /mcp reports count and frame-byte overflow separately. Image, audio, and blob results are passed to the model as descriptors (kind, MIME type, byte count) rather than inlined bytes.
Startup never opens a browser. A remote server that needs authorization settles into an authentication_required state — distinct from degraded, and not a doctor failure — with a message pointing at oaktree mcp login <server>. Stored credentials are bound to the server's normalized URL and authorization issuer rather than to its config key. mcp logout attempts remote revocation where the provider supports it before deleting the record.
oaktree mcp list --json # configuration and trust only; starts nothing
oaktree mcp login docs # explicit browser authorization
oaktree mcp logout docs # remove stored credentials
oaktree --no-mcp exec --print "…" # skip MCP for one invocationInteractive sessions expose /mcp for live server state, admission pressure, and the pre-turn registration barrier, and summarize MCP in /status. oaktree doctor starts trusted servers, probes discovery and budget pressure, and disposes them. Headless runs share one composed connect + MCP settlement + registration deadline (default 120000 ms, OAKTREEAI_CLI_EXEC_CONNECT_TIMEOUT_MS), and report per-server mcp_status in --json output if it expires.
Agent skills
The CLI discovers agent skills from a .agents/skills directory in two locations:
- Project:
<workspaceRoot>/.agents/skills/ - User:
~/.agents/skills/
Each skill is a directory containing a SKILL.md file with YAML-style frontmatter (name, description) and an instruction body, plus optional bundled assets:
.agents/skills/
review-pr/
SKILL.md # frontmatter (name, description) + instructions
checklist.md # optional bundled asset---
name: review-pr
description: Review a pull request diff and report high-confidence bugs.
---
Step-by-step instructions the model loads on demand when it calls the `skill` tool.name must be a safe slug (^[a-z0-9][a-z0-9-]*$) and description is a single-line summary. Skills use progressive disclosure: only each skill's name and description are injected up front, and the model calls the skill tool with a skill's name to load its full body on demand. On a name collision, project skills win over user skills. Discovered skills (and any discovery warnings) are surfaced in the status report.
Verify and update an existing install:
oaktree --version
npm install -g @oaktreeai/cli@latest # upgrade to the latest release
oaktree doctor
oaktree updateoaktree doctor checks local runtime, package metadata, auth, backend reachability, realtime connection.ready, Git root, tool registration, and MCP readiness, trust, and registration-budget pressure. Crash diagnostics are written to oaktreeai-cli.log under the CLI config directory with tokens and sensitive environment values redacted.
Development
When working on the CLI from a repository checkout:
npm ci
npm run build
npm run lint && npm run type-check
npm test
npm run smoke:pack # validate packaged tarball contents
npm run smoke:install # validate a real install of the packed tarballSee the repository root README.md and CLAUDE.md for the full release process.
