Compare commits

...
Author SHA1 Message Date
Dax Raad a2f58b891a feat(tui): persist permission mode in cli config 2026-09-11 15:34:38 -04:00
Dax bb564f96a3 feat(cli): rename command to opencode 2026-09-11 15:03:45 -04:00
57 changed files with 1376 additions and 457 deletions
+1 -1
View File
@@ -168,7 +168,7 @@ jobs:
fi
found=0
for file in packages/cli/dist/cli-darwin-*/bin/opencode2; do
for file in packages/cli/dist/cli-darwin-*/bin/opencode; do
if [ ! -f "$file" ]; then
continue
fi
+2 -2
View File
@@ -8,9 +8,9 @@
## Live V2 TUI Testing
- Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode2` background server and live sessions.
- Run `bun run dev:live` from a development worktree to test its TUI against the currently elected `opencode` background server and live sessions.
- Pass a directory after the script when needed, for example `bun run dev:live /path/to/project`.
- The script discovers the server with `opencode2 service status`, injects its private local credential from `opencode2 service get password`, and uses the `dev` TUI storage channel so tabs and other client-local state match the installed client.
- The script discovers the server with `opencode service status`, injects its private local credential from `opencode service get password`, and uses the `dev` TUI storage channel so tabs and other client-local state match the installed client.
- Prefer `dev:live` over plain `bun run dev` for this workflow. An implicit managed-service connection may replace the live server when the worktree client version differs; explicit `--server` warns and continues without replacing it.
## V2 TUI Stories
+44 -15
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
APP=opencode2
APP=opencode
SOURCE_APP=opencode
MUTED='\033[0;2m'
RED='\033[0;31m'
@@ -22,7 +23,7 @@ Options:
Examples:
curl -fsSL https://opencode.ai/v2/install | bash
curl -fsSL https://opencode.ai/v2/install | bash -s -- --version 0.0.0-beta-17236
./install --binary /path/to/opencode2
./install --binary /path/to/opencode
EOF
}
@@ -201,9 +202,9 @@ else
filename="cli-$target-$specific_version.tgz"
url="https://registry.npmjs.org/$package_name/-/$filename"
binary_name="$APP"
binary_name="$SOURCE_APP"
if [ "$os" = "windows" ]; then
binary_name="$APP.exe"
binary_name="$SOURCE_APP.exe"
fi
fi
@@ -230,12 +231,7 @@ check_version() {
installed_version="${installed_version##* }"
installed_version="${installed_version#v}"
if [[ "$installed_version" != "$specific_version" ]]; then
print_message info "${MUTED}Installed version: ${NC}$installed_version."
else
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
exit 0
fi
print_message info "${MUTED}Installed version: ${NC}$installed_version."
fi
}
@@ -340,15 +336,47 @@ download_and_install() {
fi
tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
mv "$tmp_dir/package/bin/$binary_name" "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/$binary_name"
local installed_binary="$APP"
if [ "$os" = "windows" ]; then
installed_binary="$APP.exe"
fi
mv "$tmp_dir/package/bin/$binary_name" "$INSTALL_DIR/$installed_binary"
chmod 755 "$INSTALL_DIR/$installed_binary"
rm -rf "$tmp_dir"
}
install_from_binary() {
print_message info "\n${MUTED}Installing ${NC}$APP ${MUTED}from: ${NC}$binary_path"
cp "$binary_path" "${INSTALL_DIR}/$APP"
chmod 755 "${INSTALL_DIR}/$APP"
local installed_binary="$APP"
case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*) installed_binary="$APP.exe" ;;
esac
cp "$binary_path" "$INSTALL_DIR/$installed_binary"
chmod 755 "$INSTALL_DIR/$installed_binary"
}
install_legacy_shim() {
local shim_os="${os:-}"
if [[ -z "$shim_os" ]]; then
case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*) shim_os="windows" ;;
esac
fi
rm -f "$INSTALL_DIR/opencode2" "$INSTALL_DIR/opencode2.exe" "$INSTALL_DIR/opencode2.cmd"
if [[ "$shim_os" == "windows" ]]; then
cat > "$INSTALL_DIR/opencode2.cmd" <<'EOF'
@echo off
echo opencode2 is now just opencode. run opencode
exit /b 1
EOF
return
fi
cat > "$INSTALL_DIR/opencode2" <<'EOF'
#!/bin/sh
echo 'opencode2 is now just opencode. run opencode'
exit 1
EOF
chmod 755 "$INSTALL_DIR/opencode2"
}
if [ -n "$binary_path" ]; then
@@ -357,6 +385,7 @@ else
check_version
download_and_install
fi
install_legacy_shim
add_to_path() {
@@ -453,7 +482,7 @@ echo -e ""
echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
echo -e ""
echo -e "cd <project> ${MUTED}# Open directory${NC}"
echo -e "opencode2 ${MUTED}# Run command${NC}"
echo -e "opencode ${MUTED}# Run command${NC}"
echo -e ""
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/v2/docs"
echo -e ""
+2 -2
View File
@@ -8,9 +8,9 @@
"packageManager": "bun@1.4.2",
"scripts": {
"dev": "bun run --cwd packages/cli src/index.ts",
"dev:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode2 service get password)\" exec bun run dev \"$@\" --server \"$(opencode2 service status)\"' --",
"dev:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev \"$@\" --server \"$(opencode service status)\"' --",
"dev:vite": "bun run --cwd packages/cli --conditions=browser dev/vite.ts",
"dev:vite:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode2 service get password)\" exec bun run dev:vite \"$@\" --server \"$(opencode2 service status)\"' --",
"dev:vite:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev:vite \"$@\" --server \"$(opencode service status)\"' --",
"dev:desktop": "bun --cwd packages/desktop dev",
"dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
@@ -285,7 +285,7 @@ export const DESKTOP_NATIVE_ENGLISH = {
"desktop.updater.dialog.later": "Later",
"desktop.cli.installed.title": "CLI Installed",
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode2' command.",
"desktop.cli.installed.message": "CLI installed to {{path}}\n\nRestart your terminal to use the 'opencode' command.",
"desktop.cli.failed.title": "Installation Failed",
"desktop.cli.failed.message": "Failed to install CLI: {{error}}",
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env node
const childProcess = require("child_process")
const fs = require("fs")
const path = require("path")
const os = require("os")
const forwardedSignals =
process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGHUP"] : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
function run(target) {
const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
child.on("error", (error) => {
console.error(error.message)
process.exit(1)
})
const forwarders = {}
for (const signal of forwardedSignals) {
forwarders[signal] = () => {
try {
child.kill(signal)
} catch {}
}
process.on(signal, forwarders[signal])
}
child.on("exit", (code, signal) => {
for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal])
if (signal) return process.kill(process.pid, signal)
process.exit(typeof code === "number" ? code : 0)
})
}
const envPath = process.env.OPENCODE_BIN_PATH
const scriptDir = path.dirname(fs.realpathSync(__filename))
const command = path.basename(__filename).replace(/\.cjs$/, "")
const nodeBuild = command === "opencode-node"
const sourceCommand = nodeBuild ? "opencode2-node" : "opencode"
const cached = path.join(scriptDir, `.${command}`)
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform()
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch()
const base = `@opencode/cli${nodeBuild ? "-node" : ""}-` + platform + "-" + arch
const binary = platform === "windows" ? `${sourceCommand}.exe` : sourceCommand
function supportsAvx2() {
if (arch !== "x64") return false
if (platform === "linux") {
try {
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
} catch {
return false
}
}
if (platform === "darwin") {
try {
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 })
return result.status === 0 && (result.stdout || "").trim() === "1"
} catch {
return false
}
}
if (platform === "windows") {
const command =
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
try {
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
encoding: "utf8",
timeout: 3000,
windowsHide: true,
})
if (result.status !== 0) continue
const output = (result.stdout || "").trim().toLowerCase()
if (output === "true" || output === "1") return true
if (output === "false" || output === "0") return false
} catch {
continue
}
}
}
return false
}
const names = (() => {
if (nodeBuild) return [base]
const baseline = arch === "x64" && !supportsAvx2()
if (platform === "linux") {
const musl = (() => {
try {
if (fs.existsSync("/etc/alpine-release")) return true
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl")
} catch {
return false
}
})()
if (musl)
return arch === "x64"
? baseline
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
: [`${base}-musl`, base]
return arch === "x64"
? baseline
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
: [base, `${base}-musl`]
}
return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base]
})()
function findBinary(startDir) {
let current = startDir
for (;;) {
const modules = path.join(current, "node_modules")
if (fs.existsSync(modules))
for (const name of names) {
const candidate = path.join(modules, name, "bin", binary)
if (fs.existsSync(candidate)) return candidate
}
const parent = path.dirname(current)
if (parent === current) return
current = parent
}
}
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
if (!resolved) {
console.error(
`It seems that your package manager failed to install the right ${command} CLI package. Try manually installing ` +
names.map((name) => `"${name}"`).join(" or ") +
" package",
)
process.exit(1)
}
run(resolved)
+2 -132
View File
@@ -1,134 +1,4 @@
#!/usr/bin/env node
const childProcess = require("child_process")
const fs = require("fs")
const path = require("path")
const os = require("os")
const forwardedSignals =
process.platform === "win32" ? ["SIGINT", "SIGTERM", "SIGHUP"] : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
function run(target) {
const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })
child.on("error", (error) => {
console.error(error.message)
process.exit(1)
})
const forwarders = {}
for (const signal of forwardedSignals) {
forwarders[signal] = () => {
try {
child.kill(signal)
} catch {}
}
process.on(signal, forwarders[signal])
}
child.on("exit", (code, signal) => {
for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal])
if (signal) return process.kill(process.pid, signal)
process.exit(typeof code === "number" ? code : 0)
})
}
const envPath = process.env.OPENCODE_BIN_PATH
const scriptDir = path.dirname(fs.realpathSync(__filename))
const command = path.basename(__filename).replace(/\.cjs$/, "")
const nodeBuild = command === "opencode2-node"
const cached = path.join(scriptDir, `.${command}`)
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform()
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch()
const base = `@opencode/cli${nodeBuild ? "-node" : ""}-` + platform + "-" + arch
const binary = platform === "windows" ? `${command}.exe` : command
function supportsAvx2() {
if (arch !== "x64") return false
if (platform === "linux") {
try {
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
} catch {
return false
}
}
if (platform === "darwin") {
try {
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 })
return result.status === 0 && (result.stdout || "").trim() === "1"
} catch {
return false
}
}
if (platform === "windows") {
const command =
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
try {
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
encoding: "utf8",
timeout: 3000,
windowsHide: true,
})
if (result.status !== 0) continue
const output = (result.stdout || "").trim().toLowerCase()
if (output === "true" || output === "1") return true
if (output === "false" || output === "0") return false
} catch {
continue
}
}
}
return false
}
const names = (() => {
if (nodeBuild) return [base]
const baseline = arch === "x64" && !supportsAvx2()
if (platform === "linux") {
const musl = (() => {
try {
if (fs.existsSync("/etc/alpine-release")) return true
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl")
} catch {
return false
}
})()
if (musl)
return arch === "x64"
? baseline
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
: [`${base}-musl`, base]
return arch === "x64"
? baseline
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
: [base, `${base}-musl`]
}
return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base]
})()
function findBinary(startDir) {
let current = startDir
for (;;) {
const modules = path.join(current, "node_modules")
if (fs.existsSync(modules))
for (const name of names) {
const candidate = path.join(modules, name, "bin", binary)
if (fs.existsSync(candidate)) return candidate
}
const parent = path.dirname(current)
if (parent === current) return
current = parent
}
}
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
if (!resolved) {
console.error(
`It seems that your package manager failed to install the right ${command} CLI package. Try manually installing ` +
names.map((name) => `"${name}"`).join(" or ") +
" package",
)
process.exit(1)
}
run(resolved)
console.log("opencode2 is now just opencode. run opencode")
process.exit(1)
+1
View File
@@ -5,6 +5,7 @@
"type": "module",
"license": "MIT",
"bin": {
"opencode": "./bin/opencode.cjs",
"opencode2": "./bin/opencode2.cjs"
},
"files": [
+2 -2
View File
@@ -12,7 +12,7 @@ import { verifyArtifact, verifySimulationGraph } from "./verify-artifact"
import { resolveOpencodePty } from "./opencode-pty"
const dir = path.resolve(import.meta.dirname, "..")
const binary = "opencode2"
const binary = "opencode"
const outdir = path.resolve(
dir,
process.argv.find((arg) => arg.startsWith("--outdir="))?.slice("--outdir=".length) ?? "dist",
@@ -149,7 +149,7 @@ export default { path: file, version: ${JSON.stringify(opencodePty.version)}, sh
},
define: {
OPENCODE_VERSION: `'${Script.version}'`,
OPENCODE_CLI_NAME: `'${binary}'`,
OPENCODE_CLI_NAME: "'opencode'",
OPENCODE_CHANNEL: `'${Script.channel}'`,
OPENCODE_ARTIFACT: `'cli'`,
OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "undefined",
+2 -1
View File
@@ -12,10 +12,11 @@ const require = createRequire(import.meta.url)
const packageJson = JSON.parse(fs.readFileSync(path.join(directory, "package.json"), "utf8"))
const command = Object.keys(packageJson.bin ?? {})[0]
if (!command) throw new Error("OpenCode package does not declare a binary")
const sourceCommand = packageJson.opencodeSourceBinary ?? command
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] ?? os.platform()
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] ?? os.arch()
const sourceBinary = platform === "windows" ? `${command}.exe` : command
const sourceBinary = platform === "windows" ? `${sourceCommand}.exe` : sourceCommand
const targetBinary = path.resolve(directory, packageJson.bin[command])
const dependencies = packageJson.optionalDependencies ?? {}
const base = Object.keys(dependencies).find((name) => name.endsWith(`-${platform}-${arch}`))
+14 -16
View File
@@ -6,15 +6,10 @@ import path from "node:path"
import { fileURLToPath } from "node:url"
import { UpdateArtifact } from "../../../script/update-artifact"
if (Script.channel !== "beta" && Script.channel !== "latest") {
throw new Error("AUR publishing requires the beta or latest channel")
}
const beta = Script.channel === "beta"
const name = beta ? "opencode-beta" : "opencode-bin"
const command = beta ? "opencode2" : "opencode"
if (!(beta ? /^\d+\.\d+\.\d+-beta[.-]\d+(?:\.\d+)?$/ : /^\d+\.\d+\.\d+$/).test(Script.version)) {
throw new Error(`Expected a ${Script.channel} release version`)
}
if (Script.channel !== "beta") throw new Error("AUR publishing requires the beta channel")
const name = "opencode-beta"
const command = "opencode"
if (!/^\d+\.\d+\.\d+-beta[.-]\d+(?:\.\d+)?$/.test(Script.version)) throw new Error("Expected a beta release version")
const dir = fileURLToPath(new URL("..", import.meta.url))
const root = path.resolve(process.env.OPENCODE_CLI_DIST ?? path.join(dir, "dist"))
@@ -22,6 +17,7 @@ const outdir = path.join(root, `aur-${name}`)
const dryRun = process.argv.includes("--dry-run")
const pkgver = Script.version.replaceAll("-", ".")
const license = Bun.file(path.join(dir, "..", "..", "LICENSE"))
const shim = "#!/bin/sh\necho 'opencode2 is now just opencode. run opencode'\nexit 1\n"
await rm(outdir, { recursive: true, force: true })
await mkdir(path.dirname(outdir), { recursive: true })
@@ -48,6 +44,7 @@ const sources = await Promise.all(
)
await Bun.write(path.join(outdir, "LICENSE"), license)
await Bun.write(path.join(outdir, "opencode2"), shim)
await Bun.write(
path.join(outdir, "PKGBUILD"),
[
@@ -55,21 +52,22 @@ await Bun.write(
`pkgname=${name}`,
`pkgver=${pkgver}`,
"pkgrel=1",
`pkgdesc='OpenCode${beta ? " V2 beta" : ""} - the AI coding agent for the terminal'`,
"pkgdesc='OpenCode beta - the AI coding agent for the terminal'",
"url='https://github.com/anomalyco/opencode'",
"arch=('x86_64' 'aarch64')",
"license=('MIT')",
"depends=('glibc' 'gcc-libs' 'ripgrep')",
`provides=('${command}')`,
`conflicts=('${command}')`,
`provides=('${command}' 'opencode2')`,
`conflicts=('${command}' 'opencode2')`,
// Stripping a compiled Bun executable can damage its embedded application.
"options=('!strip' '!debug')",
"source=('LICENSE')",
`sha256sums=('${new Bun.CryptoHasher("sha256").update(await license.arrayBuffer()).digest("hex")}')`,
"source=('LICENSE' 'opencode2')",
`sha256sums=('${new Bun.CryptoHasher("sha256").update(await license.arrayBuffer()).digest("hex")}' '${new Bun.CryptoHasher("sha256").update(shim).digest("hex")}')`,
...sources,
"",
"package() {",
` install -Dm755 "$srcdir/package/bin/opencode2" "$pkgdir/usr/bin/${command}"`,
` install -Dm755 "$srcdir/package/bin/opencode" "$pkgdir/usr/bin/${command}"`,
' install -Dm755 "$srcdir/opencode2" "$pkgdir/usr/bin/opencode2"',
' install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"',
"}",
"",
@@ -79,7 +77,7 @@ await Bun.write(path.join(outdir, ".SRCINFO"), await $`makepkg --printsrcinfo`.c
console.log(`Prepared ${name} ${pkgver} in ${outdir}`)
if (dryRun) process.exit(0)
await $`git add PKGBUILD .SRCINFO LICENSE`.cwd(outdir)
await $`git add PKGBUILD .SRCINFO LICENSE opencode2`.cwd(outdir)
if ((await $`git diff --cached --quiet`.cwd(outdir).nothrow()).exitCode !== 0) {
await $`git commit -m ${`chore: update ${name} to ${pkgver}`}`.cwd(outdir)
await $`git push origin master`.cwd(outdir)
+17 -4
View File
@@ -29,6 +29,8 @@ async function publish(dir: string, name: string, version: string) {
async function publishDistribution(input: {
root: string
name: string
command: string
legacyCommand?: string
binary: string
packagePrefix: string
artifact: string
@@ -48,7 +50,7 @@ async function publishDistribution(input: {
await $`mkdir -p ${input.root}/${input.name}/bin`
await $`cp ./script/postinstall.mjs ${input.root}/${input.name}/postinstall.mjs`
await Bun.file(`${input.root}/${input.name}/bin/${input.binary}.exe`).write(
await Bun.file(`${input.root}/${input.name}/bin/${input.command}.exe`).write(
[
`echo "Error: ${input.name}'s postinstall script was not run." >&2`,
'echo "" >&2',
@@ -58,11 +60,19 @@ async function publishDistribution(input: {
"",
].join("\n"),
)
if (input.legacyCommand)
await Bun.file(`${input.root}/${input.name}/bin/${input.legacyCommand}.cjs`).write(
`#!/usr/bin/env node\n\nconsole.log(${JSON.stringify(`opencode2 is now just opencode. run ${input.command}`)})\nprocess.exit(1)\n`,
)
await Bun.file(`${input.root}/${input.name}/package.json`).write(
JSON.stringify(
{
name: input.name,
bin: { [input.binary]: `./bin/${input.binary}.exe` },
bin: {
[input.command]: `./bin/${input.command}.exe`,
...(input.legacyCommand ? { [input.legacyCommand]: `./bin/${input.legacyCommand}.cjs` } : {}),
},
...(input.command !== input.binary ? { opencodeSourceBinary: input.binary } : {}),
scripts: { postinstall: "node ./postinstall.mjs" },
version,
license: pkg.license,
@@ -120,7 +130,9 @@ async function publishDistribution(input: {
await publishDistribution({
root,
name: pkg.name,
binary: "opencode2",
command: "opencode",
legacyCommand: "opencode2",
binary: "opencode",
packagePrefix: "@opencode/cli-",
artifact: "cli",
})
@@ -128,13 +140,14 @@ if (existsSync(path.join(root, "node"))) {
await publishDistribution({
root: path.join(root, "node"),
name: "@opencode/cli-node",
command: "opencode2-node",
binary: "opencode2-node",
packagePrefix: "@opencode/cli-node-",
artifact: "cli-node",
})
}
if ((Script.channel === "beta" || Script.channel === "latest") && Script.release) {
if (Script.channel === "beta" && Script.release) {
await $`bun ./script/publish-aur.ts ${dryRun ? ["--dry-run"] : []}`.env({ ...process.env, OPENCODE_CLI_DIST: root })
}
+4 -1
View File
@@ -11,7 +11,10 @@ import path from "node:path"
const nodeBuild = process.argv.includes("--node")
const target = `cli${nodeBuild ? "-node" : ""}-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}`
const directory = path.join(import.meta.dir, "..", "dist", ...(nodeBuild ? ["node"] : []), target, "bin")
const binary = path.join(directory, `opencode2${nodeBuild ? "-node" : ""}${process.platform === "win32" ? ".exe" : ""}`)
const binary = path.join(
directory,
`${nodeBuild ? "opencode2-node" : "opencode"}${process.platform === "win32" ? ".exe" : ""}`,
)
if (!(await Bun.file(binary).exists())) throw new Error(`Missing compiled CLI in ${directory}`)
const root = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-smoke-")))
+1 -1
View File
@@ -36,7 +36,7 @@ const PermissionParams = {
}
const Root = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
description: "OpenCode 2.0 preview command line interface",
description: "OpenCode command line interface",
params: {
...ServerParams,
...PermissionParams,
+5 -2
View File
@@ -105,7 +105,7 @@ const make = Effect.gen(function* () {
global.home,
".opencode",
"bin",
process.platform === "win32" ? "opencode2.exe" : "opencode2",
process.platform === "win32" ? "opencode.exe" : "opencode",
)
if (path.resolve(process.execPath) === path.resolve(binary)) return "curl"
if (!installedPackage) return
@@ -186,7 +186,10 @@ const make = Effect.gen(function* () {
"npm",
"install",
"--global",
...(installedPackage && packageName !== installedPackage ? ["--force"] : []),
...((OPENCODE_ARTIFACT === "cli" && !installedPackage?.endsWith("/cli-node")) ||
(installedPackage && packageName !== installedPackage)
? ["--force"]
: []),
target,
],
pnpm: ["pnpm", "add", "--global", `--allow-build=${packageName}`, target],
+1 -1
View File
@@ -100,7 +100,7 @@ function fixture(
}
const installs = [
{ method: "npm", command: ["npm", "install", "--global", "@opencode/cli@2.3.4-beta.1"] },
{ method: "npm", command: ["npm", "install", "--global", "--force", "@opencode/cli@2.3.4-beta.1"] },
{
method: "pnpm",
command: ["pnpm", "add", "--global", "--allow-build=@opencode/cli", "@opencode/cli@2.3.4-beta.1"],
+11 -11
View File
@@ -111,13 +111,13 @@ for themselves without limiting it to the current project; omit it when they
explicitly want project-local configuration.
```sh
opencode2 mcp add <name> --global --url <remote-url>
opencode2 mcp list
opencode mcp add <name> --global --url <remote-url>
opencode mcp list
```
Remote servers use OAuth by default. If `mcp list` reports that a server needs
authentication, tell the user to run `/mcps`, select the server, and sign in.
Do not run `opencode2 mcp auth` through the shell tool: it starts an interactive
Do not run `opencode mcp auth` through the shell tool: it starts an interactive
flow whose authorization link can be hidden in background process output.
Use the user-facing MCP interface instead.
@@ -168,13 +168,13 @@ OpenCode normally discovers or starts the shared background service
automatically. If the service is stuck or unhealthy, restart it:
```sh
opencode2 service restart
opencode service restart
```
Check its status after restarting:
```sh
opencode2 service status
opencode service status
```
## [API](https://opencode.ai/v2/docs/api)
@@ -190,15 +190,15 @@ HTTP method and path or an OpenAPI operation ID.
Call an endpoint with an HTTP method and path:
```sh
opencode2 api get /api/health
opencode api get /api/health
```
Pass a request body with `--data` or `-d`, and additional headers with
`--header` or `-H`:
```sh
opencode2 api post /api/example --data '{"key":"value"}'
opencode2 api get /api/example --header 'X-Example:value'
opencode api post /api/example --data '{"key":"value"}'
opencode api get /api/example --header 'X-Example:value'
```
Request bodies default to `Content-Type: application/json`. When OpenCode is
@@ -240,9 +240,9 @@ Effect applications. For Cloudflare Durable Objects, use the
OpenCode runs a client and a background server. Start by determining whether a
problem belongs to the client, the shared server, or one project.
- Check the service with `opencode2 service status` and verify the API with
`opencode2 api get /api/health`.
- Compare with `opencode2 --standalone`, which runs the TUI with a private
- Check the service with `opencode service status` and verify the API with
`opencode api get /api/health`.
- Compare with `opencode --standalone`, which runs the TUI with a private
server, to isolate shared-service issues.
- Inspect `~/.local/share/opencode/log/opencode.log`. Filter `role=cli` for
client startup and `role=server` for sessions, providers, plugins,
+1 -2
View File
@@ -26,8 +26,7 @@ title/body for the user.
Collect these values when possible:
- opencode version: run `opencode --version` or `opencode2 --version`,
depending on the executable in use.
- opencode version: run `opencode --version`.
- Operating system: run `uname -a` on Unix-like systems, or `ver` on Windows.
- Terminal: inspect `$TERM`, `$TERM_PROGRAM`, `$COLORTERM`, and any obvious
terminal app context the user provides.
+2 -2
View File
@@ -76,7 +76,7 @@ export async function downloadCliToResources(version = CLI_VERSION, dest = windo
try {
await $`bun install --no-save --cwd ${directory} ${`${cli.package}@${version}`} ${`--os=${cli.os}`} ${`--cpu=${cli.cpu}`}`
await copyCliToResources(
join(directory, "node_modules", cli.package, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"),
join(directory, "node_modules", cli.package, "bin", cli.os === "win32" ? "opencode.exe" : "opencode"),
dest,
)
} finally {
@@ -89,7 +89,7 @@ export async function downloadCliToResources(version = CLI_VERSION, dest = windo
export async function copyBuiltCliToResources(root: string, dest = windowsify("resources/opencode-cli")) {
const cli = getCurrentCli()
const directory = cli.package.replace("@opencode/", "")
await copyCliToResources(join(root, directory, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"), dest)
await copyCliToResources(join(root, directory, "bin", cli.os === "win32" ? "opencode.exe" : "opencode"), dest)
}
async function copyCliToResources(source: string, dest: string) {
+7 -7
View File
@@ -44,8 +44,8 @@ posix(
const home = path.join(dir, "home with ' quotes")
yield* fs.makeDirectory(path.join(home, ".opencode/bin"), { recursive: true })
yield* fs.makeDirectory(path.join(dir, "bin"))
const managed = path.join(home, ".opencode/bin/opencode2")
const external = path.join(dir, "bin/opencode2")
const managed = path.join(home, ".opencode/bin/opencode")
const external = path.join(dir, "bin/opencode")
yield* fs.writeFileString(managed, "#!/bin/sh\nprintf 'OpenCode v2.0.0\\n'\n", { mode: 0o755 })
yield* fs.writeFileString(external, "#!/bin/sh\nprintf 'OpenCode v2.1.0\\n'\n", { mode: 0o755 })
const run = (script: string) =>
@@ -79,7 +79,7 @@ posix(
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "remote-install-" })
yield* fs.makeDirectory(path.join(dir, "package/bin"), { recursive: true })
yield* fs.writeFileString(path.join(dir, "package/bin/opencode2"), "#!/bin/sh\nprintf 'OpenCode v2.0.0\\n'\n", {
yield* fs.writeFileString(path.join(dir, "package/bin/opencode"), "#!/bin/sh\nprintf 'OpenCode v2.0.0\\n'\n", {
mode: 0o755,
})
const archive = path.join(dir, "archive.tgz")
@@ -101,13 +101,13 @@ posix(
}),
)
expect(yield* run({ version: "2.0.0", source: { type: "download", url: server.url.href } })).toBe(0)
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode2"))).toContain("2.0.0")
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode"))).toContain("2.0.0")
expect(
yield* run({ version: "2.0.0", directory: ".opencode/desktop-ssh/2.0.0", source: { type: "archive" } }),
).toBe(0)
expect(yield* fs.readFileString(path.join(dir, ".opencode/desktop-ssh/2.0.0/opencode2"))).toContain("2.0.0")
expect(yield* fs.readFileString(path.join(dir, ".opencode/desktop-ssh/2.0.0/opencode"))).toContain("2.0.0")
expect(yield* run({ version: "2.1.0", source: { type: "archive" } })).not.toBe(0)
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode2"))).toContain("2.0.0")
expect(yield* fs.readDirectory(path.join(dir, ".opencode/bin"))).toEqual(["opencode2"])
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode"))).toContain("2.0.0")
expect(yield* fs.readDirectory(path.join(dir, ".opencode/bin"))).toEqual(["opencode"])
}),
)
+8 -8
View File
@@ -24,12 +24,12 @@ export function requireVersion(version: string) {
}
export function discoverScript(options: { fromPath?: boolean; cache?: { directory: string; prefix: string } } = {}) {
return `cli=${options.fromPath ? "$(command -v opencode2 || true)" : '""'}
if [ -z "$cli" ] && [ -x "$HOME/.opencode/bin/opencode2" ]; then cli="$HOME/.opencode/bin/opencode2"; fi
return `cli=${options.fromPath ? "$(command -v opencode || true)" : '""'}
if [ -z "$cli" ] && [ -x "$HOME/.opencode/bin/opencode" ]; then cli="$HOME/.opencode/bin/opencode"; fi
${
options.cache
? `if [ -z "$cli" ]; then
for binary in "$HOME"/${quote(options.cache.directory)}/${quote(options.cache.prefix)}*/opencode2; do
for binary in "$HOME"/${quote(options.cache.directory)}/${quote(options.cache.prefix)}*/opencode; do
if [ -x "$binary" ]; then cli="$binary"; fi
done
fi
@@ -80,18 +80,18 @@ export function installScript(input: { version: string; directory?: string; sour
if (input.source.type === "installer")
return `set -eu
curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash -s -- ${input.source.binary ? `--binary ${input.source.binary}` : `--version ${quote(version)}`}
${verifyScript('"$HOME/.opencode/bin/opencode2"', version)}
${verifyScript('"$HOME/.opencode/bin/opencode"', version)}
`
return `set -eu
umask 077
destination="$HOME"/${quote(`${input.directory ?? ".opencode/bin"}/opencode2`)}
destination="$HOME"/${quote(`${input.directory ?? ".opencode/bin"}/opencode`)}
mkdir -p "$(dirname "$destination")"
stage=$(mktemp -d "$(dirname "$destination")/.install-XXXXXX")
trap 'rm -rf "$stage"' EXIT
${stageBinary(input.source)}
chmod 755 "$stage/package/bin/opencode2"
${verifyScript('"$stage/package/bin/opencode2"', version)}
mv "$stage/package/bin/opencode2" "$destination"
chmod 755 "$stage/package/bin/opencode"
${verifyScript('"$stage/package/bin/opencode"', version)}
mv "$stage/package/bin/opencode" "$destination"
`
}
@@ -37,7 +37,7 @@ export const layer = Layer.effect(
if (!cli.binary) return yield* Effect.fail(new Error("Bundled CLI executable is unavailable"))
const home = app.getPath("home")
yield* runInstaller(cli.binary, home)
return path.join(home, ".opencode", "bin", "opencode2")
return path.join(home, ".opencode", "bin", "opencode")
})
return Service.of({ resolve, install })
}),
@@ -20,7 +20,7 @@ posix(
const bin = path.join(dir, ".opencode/desktop-ssh", version)
yield* fs.makeDirectory(bin, { recursive: true })
yield* fs.writeFileString(
path.join(bin, "opencode2"),
path.join(bin, "opencode"),
`#!/bin/sh
set -eu
case "$1 $2" in
@@ -69,7 +69,7 @@ posix(
yield* fs.makeDirectory(path.join(dir, ".opencode/bin"), { recursive: true })
yield* fs.makeDirectory(path.join(dir, "state/opencode"), { recursive: true })
yield* fs.writeFileString(
path.join(dir, ".opencode/bin/opencode2"),
path.join(dir, ".opencode/bin/opencode"),
'#!/bin/sh\n[ "$1 $2" = "service status" ] || exit 66\nprintf "http://0.0.0.0:49374\\n"\n',
{ mode: 0o755 },
)
+1 -1
View File
@@ -53,7 +53,7 @@ export function parseRegistration(output: string) {
}
export function binaryPath(version: string) {
return `$HOME/.opencode/desktop-ssh/${RemoteCli.requireVersion(version)}/opencode2`
return `$HOME/.opencode/desktop-ssh/${RemoteCli.requireVersion(version)}/opencode`
}
function connectionAddress(address: string, password: string) {
+1 -1
View File
@@ -27,7 +27,7 @@ const make = Effect.fn("Ssh.make")(function* (cli: DesktopCli.Resolved) {
const controller = yield* createSshController({
version: cli.version,
development: !app.isPackaged && cli.binary === undefined,
binary: cli.binary ?? cli.command[0] ?? "opencode2",
binary: cli.binary ?? cli.command[0] ?? "opencode",
command: cli.command,
configs: stored._tag === "Some" ? stored.value : [],
save: (configs) => Effect.try({ try: () => getStore().set("ssh.servers", configs), catch: SshFailure.from }),
+2 -2
View File
@@ -33,7 +33,7 @@ export const buildLocalWslCli = Effect.fn("Wsl.buildLocalCli")(function* (input:
[
packageManager,
input.script,
`--target=opencode2-${target}`,
`--target=opencode-${target}`,
"--skip-install",
"--skip-web-ui",
`--outdir=${directory}`,
@@ -41,7 +41,7 @@ export const buildLocalWslCli = Effect.fn("Wsl.buildLocalCli")(function* (input:
{ cwd: root, env: { ...process.env, OPENCODE_VERSION: input.version }, windowsHide: true },
),
)
yield* fs.copyFile(path.join(directory, `cli-${target}`, "bin", "opencode2"), input.output)
yield* fs.copyFile(path.join(directory, `cli-${target}`, "bin", "opencode"), input.output)
return input.output
})
return yield* build.pipe(Effect.ensuring(fs.remove(directory, { recursive: true, force: true }).pipe(Effect.orDie)))
@@ -32,7 +32,7 @@ posix(
'#!/bin/sh\n[ "$1" = "-a" ] || exit 1\nprintf "%s" "$2" > "$HOME/wslpath-input"\nprintf "%s\\n" "$LOCAL_BINARY"\n',
{ mode: 0o755 },
)
const windows = "C:\\local build's\\opencode2"
const windows = "C:\\local build's\\opencode"
const command = wslCliInstallCommand({ version: "0.0.0-dev-16365", binary: windows })
expect(
yield* spawner.exitCode(
@@ -42,8 +42,8 @@ posix(
),
).toBe(0)
expect(yield* fs.readFileString(path.join(dir, "wslpath-input"))).toBe(windows)
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode2"))).toContain("0.0.0-dev-16365")
expect(yield* fs.readDirectory(path.join(dir, ".opencode/bin"))).toEqual(["opencode2"])
expect(yield* fs.readFileString(path.join(dir, ".opencode/bin/opencode"))).toContain("0.0.0-dev-16365")
expect((yield* fs.readDirectory(path.join(dir, ".opencode/bin"))).toSorted()).toEqual(["opencode", "opencode2"])
expect(yield* fs.readFileString(path.join(dir, ".bashrc"))).toContain(`export PATH=${dir}/.opencode/bin:$PATH`)
}),
)
@@ -57,7 +57,7 @@ test("installs and verifies the bundled CLI version", async () => {
installCli: async (distro, cli) => {
installs.push([distro, cli.version])
},
resolveCli: async () => "/home/me/.opencode/bin/opencode2",
resolveCli: async () => "/home/me/.opencode/bin/opencode",
}),
),
)
@@ -74,7 +74,7 @@ test("rejects a WSL CLI version that differs from the bundled version", async ()
createWslServersController(
testControllerOptions({
installCli: async () => undefined,
resolveCli: async () => "/home/me/.opencode/bin/opencode2",
resolveCli: async () => "/home/me/.opencode/bin/opencode",
readCliVersion: async () => "0.0.0-dev-older",
}),
),
@@ -166,7 +166,7 @@ test("probes addable distros in parallel before checking OpenCode", async () =>
},
resolveCli: async (distro) => {
opencode.push(distro)
return "/home/me/.opencode/bin/opencode2"
return "/home/me/.opencode/bin/opencode"
},
}),
),
@@ -201,7 +201,7 @@ test("does not check OpenCode in addable distros that cannot execute commands",
}),
resolveCli: async (distro) => {
opencode.push(distro)
return "/home/me/.opencode/bin/opencode2"
return "/home/me/.opencode/bin/opencode"
},
}),
),
@@ -238,7 +238,7 @@ function testControllerOptions(overrides: Partial<ControllerOptions> = {}): Cont
persistedServers = servers
},
readCliVersion: async () => "0.0.0-dev-16365",
resolveCli: async () => "/home/me/.opencode/bin/opencode2",
resolveCli: async () => "/home/me/.opencode/bin/opencode",
...overrides,
}
}
+5 -3
View File
@@ -1171,10 +1171,12 @@ function App(props: { pair?: DialogPairCredentials }) {
{
name: "permission.mode",
title:
local.permission.mode === "auto" ? "Disable auto-approve permissions" : "Enable auto-approve permissions",
category: "System",
local.permission.mode === "autoaccept"
? "Disable auto-approve permissions"
: "Enable auto-approve permissions",
category: "Session",
run: () => {
local.permission.toggle()
void local.permission.toggle().catch(toast.error)
dialog.clear()
},
},
@@ -120,6 +120,15 @@ export const settings: Setting[] = [
labels: ["launch directory", "active session"],
keywords: ["directory", "cwd", "inherit"],
},
{
title: "Permissions",
category: "Session",
path: ["session", "permissions"],
default: "prompt",
values: ["prompt", "autoaccept"],
labels: ["prompt", "auto accept"],
keywords: ["approve", "accept", "permission requests"],
},
{
title: "Enabled",
category: "Tabs",
+1 -1
View File
@@ -1849,7 +1849,7 @@ export function Prompt(props: PromptProps) {
<PromptMetadataRow
mode={store.mode}
agent={agentLabel()}
auto={local.permission.mode === "auto"}
auto={local.permission.mode === "autoaccept"}
model={promptDisplay().modelLabel}
provider={promptDisplay().providerLabel}
variant={promptDisplay().variant}
+6 -1
View File
@@ -168,6 +168,9 @@ export const Info = Schema.Struct({
new_location: Schema.optional(Schema.Literals(["launch", "inherit"])).annotate({
description: "Start new sessions in the TUI launch directory or inherit the active session location",
}),
permissions: Schema.optional(Schema.Literals(["prompt", "autoaccept"])).annotate({
description: "Prompt for permission requests or accept them automatically",
}),
}),
).annotate({ description: "Session transcript presentation settings" }),
tabs: Schema.optional(
@@ -254,8 +257,9 @@ export type Resolved = Omit<Info, "attention" | "cursor" | "keybinds" | "leader"
style: "block" | "underline" | "line" | "default"
blinking: boolean
}
session: Omit<NonNullable<Info["session"]>, "new_location" | "tps"> & {
session: Omit<NonNullable<Info["session"]>, "new_location" | "permissions" | "tps"> & {
new_location: "launch" | "inherit"
permissions: "prompt" | "autoaccept"
tps: boolean
}
tabs: {
@@ -302,6 +306,7 @@ export function resolve(input: Info, options: { terminalSuspend: boolean }): Res
session: {
...input.session,
new_location: input.session?.new_location ?? "launch",
permissions: input.session?.permissions ?? "prompt",
// Persistent terminal panes need the opencode-pty daemon, which does not ship Windows binaries.
terminal: input.session?.terminal ?? process.platform !== "win32",
tps: input.session?.tps ?? true,
+16 -4
View File
@@ -1,15 +1,24 @@
import { createStore } from "solid-js/store"
import { createEffect } from "solid-js"
import { useConfig } from "../config"
import { useArgs } from "./args"
import { createSimpleContext } from "./helper"
export type PermissionMode = "auto" | "normal"
export type PermissionMode = "prompt" | "autoaccept"
export const { use: usePermission, provider: PermissionProvider } = createSimpleContext({
name: "Permission",
init: () => {
const args = useArgs()
const [store, setStore] = createStore<{ mode: PermissionMode }>({
mode: args.auto ? "auto" : "normal",
const config = useConfig()
const [store, setStore] = createStore<{ mode: PermissionMode; configured: PermissionMode }>({
mode: args.auto ? "autoaccept" : config.data.session.permissions,
configured: config.data.session.permissions,
})
createEffect(() => {
const mode = config.data.session.permissions
if (mode === store.configured) return
setStore({ mode, configured: mode })
})
return {
get mode() {
@@ -17,9 +26,12 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
},
set(mode: PermissionMode) {
setStore("mode", mode)
return config.update((draft) => {
draft.session = { ...draft.session, permissions: mode }
})
},
toggle() {
setStore("mode", (mode) => (mode === "auto" ? "normal" : "auto"))
return this.set(store.mode === "autoaccept" ? "prompt" : "autoaccept")
},
}
},
+3 -3
View File
@@ -185,7 +185,7 @@ export function Session(props: {
(sessionID) => data.session.permission.list(sessionID) ?? [],
)
})
const promptedPermissions = createMemo(() => (local.permission.mode === "auto" ? [] : permissions()))
const promptedPermissions = createMemo(() => (local.permission.mode === "autoaccept" ? [] : permissions()))
const forms = createMemo(() => {
const global = data.session.form.list("global", location()) ?? []
if (session()?.parentID) return global
@@ -235,7 +235,7 @@ export function Session(props: {
const client = useClient()
const autoApproved = new Set<string>()
createEffect(() => {
if (local.permission.mode !== "auto") return
if (local.permission.mode !== "autoaccept") return
permissions().forEach((request) => {
if (autoApproved.has(request.id)) return
autoApproved.add(request.id)
@@ -2660,7 +2660,7 @@ function useToolPermission(part: () => SessionMessageAssistantTool | undefined)
const data = useData()
const local = useLocal()
return createMemo(() => {
if (local.permission.mode === "auto") return false
if (local.permission.mode === "autoaccept") return false
const request = data.session.permission.list(ctx.sessionID)?.[0]
return request?.source?.type === "tool" && request.source.id === part()?.id
})
+1 -1
View File
@@ -29,7 +29,7 @@ export function sessionEpilogue(input: { title: string; sessionID?: string }) {
...wordmark(" "),
"",
` ${weak("Session")}${bold}${input.title}${reset}`,
` ${weak("Continue")}${bold}opencode2 -s ${input.sessionID}${reset}`,
` ${weak("Continue")}${bold}opencode -s ${input.sessionID}${reset}`,
"",
].join("\n")
}
+1 -1
View File
@@ -335,7 +335,7 @@ test("session lifecycle updates the terminal title and prints the epilogue after
await task
expect(stdout).toContain("Renamed session")
expect(stdout).toContain("opencode2 -s dummy")
expect(stdout).toContain("opencode -s dummy")
expect(promptRequests).toBe(0)
} finally {
process.stdout.write = originalWrite
+1 -1
View File
@@ -4,5 +4,5 @@ import { sessionEpilogue } from "../../src/util/presentation"
test("formats session continuation summary", () => {
const epilogue = sessionEpilogue({ title: "A session", sessionID: "ses_123" })
expect(epilogue).toContain("A session")
expect(epilogue).toContain("opencode2 -s ses_123")
expect(epilogue).toContain("opencode -s ses_123")
})
+3 -3
View File
@@ -8,8 +8,8 @@ import config from "../../../config.mjs"
export const console = config.console
:::note
OpenCode 1 installs and runs as `opencode`. OpenCode 2 installs separately as `opencode2`, so you can keep both versions
installed and run them side by side. See the [OpenCode 2 docs](https://opencode.ai/v2/docs/) to install V2.
OpenCode 1 and OpenCode 2 both use the `opencode` command and cannot be installed side by side under their default command.
See the [OpenCode 2 docs](https://opencode.ai/v2/docs/) to migrate to V2.
:::
[**OpenCode**](/) is an open source AI coding agent. It's available as a terminal-based interface, desktop app, or IDE extension.
@@ -37,7 +37,7 @@ To use OpenCode in your terminal, you'll need:
## Install
The easiest way to install OpenCode 1 is through the install script. These installation methods provide the `opencode`
binary and do not replace an `opencode2` installation.
binary and can conflict with or replace an OpenCode 2 installation.
```bash
curl -fsSL https://opencode.ai/install | bash
+2 -2
View File
@@ -77,8 +77,8 @@ Both publishers upload signed/packaged outputs to the production bucket for
every release channel:
```text
R2 key: bin/0.0.0-dev-123/opencode2-linux-x64.tar.gz
URL: https://opencode.ai/files/bin/0.0.0-dev-123/opencode2-linux-x64.tar.gz
R2 key: bin/0.0.0-dev-123/opencode-linux-x64.tar.gz
URL: https://opencode.ai/files/bin/0.0.0-dev-123/opencode-linux-x64.tar.gz
R2 key: bin/2.0.0/opencode-desktop-mac-arm64.dmg
URL: https://opencode.ai/files/bin/2.0.0/opencode-desktop-mac-arm64.dmg
+98
View File
@@ -0,0 +1,98 @@
---
title: "ACP"
description: "Use OpenCode with an Agent Client Protocol client."
---
Configure your ACP client to launch `opencode acp`. For example, add OpenCode to Zed's
`~/.config/zed/settings.json`:
```json
{
"agent_servers": {
"OpenCode": {
"command": "opencode",
"args": ["acp"]
}
}
}
```
Other ACP clients use the same executable and `acp` argument, but their configuration file and field names may differ.
If a graphical client cannot find `opencode`, set `command` to the absolute path reported by `which opencode`.
## Transport
`opencode acp` is an [Agent Client Protocol](https://agentclientprotocol.com) agent. The client starts it as a child
process and exchanges newline-delimited JSON-RPC messages over stdin and stdout using ACP protocol version 1.
The command starts a private OpenCode server for that ACP process. It does not connect to the shared background service,
and it does not expose an ACP network port.
The process can serve multiple ACP sessions. It runs until the client closes stdin, then exits and stops its private
server.
## Sessions
When the client creates a session, it supplies the working directory. OpenCode loads that directory's configuration,
plugins, models, agents, commands, skills, instructions, and MCP configuration before creating the session.
The ACP session lifecycle supports:
- Creating, listing, loading, resuming, forking, closing, and deleting sessions
- Replaying saved messages when a client loads or forks a session
- Cancelling an active prompt without closing the session
- Streaming text, reasoning, tool calls, permission requests, and usage updates
The directory stored on an existing OpenCode session is authoritative. Loading, resuming, or forking that session uses
its stored directory rather than a different directory supplied by the client.
Closing a session interrupts active work and detaches it from the ACP process, but keeps the saved session. Deleting a
session removes it from OpenCode storage.
## Models
New sessions use the configured default model and primary agent for their directory. The client receives session options
for all enabled models and all visible agents that are not subagents.
ACP clients can change these options during a session:
- **Model** selects an enabled model as `provider/model`.
- **Effort** appears when the selected model provides variants.
- **Mode** selects a visible agent that is not a subagent.
Model, effort, and mode changes are applied to the OpenCode session, so later prompts use the new selection.
## Content
Prompts can contain text, images, embedded resources, and file resource links. OpenCode also advertises available slash
commands and skills to the client; `/compact` runs session compaction.
Clients may pass local or HTTP MCP servers when they create, load, resume, or fork a session. MCP over SSE and MCP over
ACP are not supported by this command.
## Authentication
ACP authentication uses the provider credentials already available to OpenCode. Sign in from a terminal before starting
the ACP client:
```bash
opencode auth login
```
Clients that support ACP terminal authentication can offer the OpenCode login command reported during initialization.
The ACP `authenticate` request does not collect credentials itself.
## Troubleshooting
Running `opencode acp` directly appears to wait because it expects ACP messages on stdin. Start it through an ACP client
for normal use.
If the client reports that the process exited or returned invalid protocol output, enable ACP and private-server logs on
stderr:
```bash
opencode --print-logs acp
```
Add `--print-logs` before `acp` in the client's argument list while diagnosing the problem. Stdout remains reserved for
ACP messages.
+122 -103
View File
@@ -1,548 +1,567 @@
---
title: "Commands"
description: "Reference for the opencode2 command line."
description: "Reference for the opencode command line."
---
Every command accepts `--help` for its full flag list, for example `opencode2 run --help`. Commands that talk to a server also accept `--standalone` to run a private server and `--server <url>` to target a specific one.
Every command accepts `--help` for its full flag list, for example `opencode run --help`. Commands that talk to a server also accept `--standalone` to run a private server and `--server <url>` to target a specific one.
## run
`opencode2 run` sends a message and prints the reply without opening the interactive interface.
`opencode run` sends a message and prints the reply without opening the interactive interface.
```bash
$ opencode2 run "Explain this repository"
$ opencode run "Explain this repository"
```
### CI
In CI, pass a provider API key as a secret and use standalone mode so the private server receives it. For example, this GitHub Actions job reviews the checked-out repository:
```yaml
name: OpenCode review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install --global @opencode/cli@beta
- run: opencode run --standalone --model anthropic/claude-sonnet-4-5 "Review this repository for correctness and summarize any issues."
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
```
Choose a model.
```bash
$ opencode2 run --model anthropic/claude-sonnet-4-5 "Refactor parseToken"
$ opencode run --model anthropic/claude-sonnet-4-5 "Refactor parseToken"
```
Continue the last session.
```bash
$ opencode2 run --continue "Now handle the expired case"
$ opencode run --continue "Now handle the expired case"
```
Emit newline-delimited JSON for scripts.
```bash
$ opencode2 run --format json "List the TODO comments"
$ opencode run --format json "List the TODO comments"
```
Attach files to the message.
```bash
$ opencode2 run --file src/server.ts --file src/client.ts "Review these for bugs"
$ opencode run --file src/server.ts --file src/client.ts "Review these for bugs"
```
Run with a specific agent.
```bash
$ opencode2 run --agent build "Fix the failing test"
$ opencode run --agent build "Fix the failing test"
```
View all subcommands and flags.
```bash
$ opencode2 run --help
$ opencode run --help
```
## mini
`opencode2 mini` starts the minimal interactive interface instead of the full-screen TUI.
`opencode mini` starts the minimal interactive interface instead of the full-screen TUI.
```bash
$ opencode2 mini
$ opencode mini
```
Continue the last session.
```bash
$ opencode2 mini --continue
$ opencode mini --continue
```
Start with a model and an initial prompt.
```bash
$ opencode2 mini --model anthropic/claude-sonnet-4-5 --prompt "Summarize this repository"
$ opencode mini --model anthropic/claude-sonnet-4-5 --prompt "Summarize this repository"
```
View all subcommands and flags.
```bash
$ opencode2 mini --help
$ opencode mini --help
```
## session
`opencode2 session` manages sessions.
`opencode session` manages sessions.
```bash
$ opencode2 session list
$ opencode session list
```
Limit the list and print JSON.
```bash
$ opencode2 session list --max-count 20 --format json
$ opencode session list --max-count 20 --format json
```
Delete a session and its child sessions.
```bash
$ opencode2 session delete ses_9c1b08
$ opencode session delete ses_9c1b08
```
Export session data as JSON.
```bash
$ opencode2 session export ses_4f2a1c
$ opencode session export ses_4f2a1c
```
Redact sensitive transcript and file data when exporting.
```bash
$ opencode2 session export ses_4f2a1c --sanitize
$ opencode session export ses_4f2a1c --sanitize
```
Import session data from a JSON file or URL.
```bash
$ opencode2 session import session.json
$ opencode session import session.json
```
Import into a specific directory.
```bash
$ opencode2 session import session.json --directory ~/code/project
$ opencode session import session.json --directory ~/code/project
```
View all subcommands and flags.
```bash
$ opencode2 session --help
$ opencode session --help
```
## auth
`opencode2 auth` manages AI providers and credentials.
`opencode auth` manages AI providers and credentials.
```bash
$ opencode2 auth list
$ opencode auth list
```
List them as JSON.
```bash
$ opencode2 auth list --format json
$ opencode auth list --format json
```
Log in to a provider.
```bash
$ opencode2 auth login anthropic
$ opencode auth login anthropic
```
Log in with a specific authentication method.
```bash
$ opencode2 auth login anthropic --method api-key
$ opencode auth login anthropic --method api-key
```
Log out of a saved account.
```bash
$ opencode2 auth logout anthropic work
$ opencode auth logout anthropic work
```
Switch the active account for an integration.
```bash
$ opencode2 auth switch anthropic work
$ opencode auth switch anthropic work
```
View all subcommands and flags.
```bash
$ opencode2 auth --help
$ opencode auth --help
```
## models
`opencode2 models` lists every available model.
`opencode models` lists every available model.
```bash
$ opencode2 models
$ opencode models
```
View all subcommands and flags.
```bash
$ opencode2 models --help
$ opencode models --help
```
## mcp
`opencode2 mcp` manages MCP (Model Context Protocol) servers.
`opencode mcp` manages MCP (Model Context Protocol) servers.
```bash
$ opencode2 mcp list
$ opencode mcp list
```
Add a remote server.
```bash
$ opencode2 mcp add context7 --url https://mcp.context7.com/mcp
$ opencode mcp add context7 --url https://mcp.context7.com/mcp
```
Add a local server to the global config.
```bash
$ opencode2 mcp add everything --global -- npx -y @modelcontextprotocol/server-everything
$ opencode mcp add everything --global -- npx -y @modelcontextprotocol/server-everything
```
Add a local server with an environment variable.
```bash
$ opencode2 mcp add everything --env LOG_LEVEL=debug -- npx -y @modelcontextprotocol/server-everything
$ opencode mcp add everything --env LOG_LEVEL=debug -- npx -y @modelcontextprotocol/server-everything
```
Add a remote server with a header.
```bash
$ opencode2 mcp add context7 --url https://mcp.context7.com/mcp --header CONTEXT7_API_KEY=secret
$ opencode mcp add context7 --url https://mcp.context7.com/mcp --header CONTEXT7_API_KEY=secret
```
Authenticate with an OAuth-capable remote server.
```bash
$ opencode2 mcp auth sentry
$ opencode mcp auth sentry
```
Remove stored OAuth credentials for a server.
```bash
$ opencode2 mcp logout sentry
$ opencode mcp logout sentry
```
View all subcommands and flags.
```bash
$ opencode2 mcp --help
$ opencode mcp --help
```
## plugin
`opencode2 plugin` manages plugins.
`opencode plugin` manages plugins.
```bash
$ opencode2 plugin list
$ opencode plugin list
```
Include built-in server plugins.
```bash
$ opencode2 plugin list --builtin
$ opencode plugin list --builtin
```
Install a plugin and add it to the global configuration.
```bash
$ opencode2 plugin add @example/opencode-tui
$ opencode plugin add @example/opencode-tui
```
Check package plugins for updates.
```bash
$ opencode2 plugin check
$ opencode plugin check
```
Update package plugins.
```bash
$ opencode2 plugin update
$ opencode plugin update
```
Remove a plugin from global configuration.
```bash
$ opencode2 plugin remove @example/opencode-tui
$ opencode plugin remove @example/opencode-tui
```
View all subcommands and flags.
```bash
$ opencode2 plugin --help
$ opencode plugin --help
```
## stats
`opencode2 stats` shows shareable usage statistics.
`opencode stats` shows shareable usage statistics.
```bash
$ opencode2 stats
$ opencode stats
```
Show the last 7 days.
```bash
$ opencode2 stats --days 7
$ opencode stats --days 7
```
Show model usage.
```bash
$ opencode2 stats --models
$ opencode stats --models
```
Show cost and token details.
```bash
$ opencode2 stats --cost
$ opencode stats --cost
```
Print JSON instead of a report.
```bash
$ opencode2 stats --json
$ opencode stats --json
```
View all subcommands and flags.
```bash
$ opencode2 stats --help
$ opencode stats --help
```
## serve
`opencode2 serve` starts the API and web server. See [Web](/cli/web).
`opencode serve` starts the API and web server. See [Web](/cli/web).
```bash
$ opencode2 serve
$ opencode serve
```
Bind to all interfaces on a fixed port.
```bash
$ opencode2 serve --hostname 0.0.0.0 --port 4096
$ opencode serve --hostname 0.0.0.0 --port 4096
```
Allow a browser client from another origin.
```bash
$ opencode2 serve --cors https://app.example.com
$ opencode serve --cors https://app.example.com
```
View all subcommands and flags.
```bash
$ opencode2 serve --help
$ opencode serve --help
```
## pair
`opencode2 pair` shows server pairing information, including URLs, credentials, and a QR code.
`opencode pair` shows server pairing information, including URLs, credentials, and a QR code.
```bash
$ opencode2 pair
$ opencode pair
```
Advertise an external URL in the QR code.
```bash
$ opencode2 pair --url https://dev.example.com
$ opencode pair --url https://dev.example.com
```
View all subcommands and flags.
```bash
$ opencode2 pair --help
$ opencode pair --help
```
## service
`opencode2 service` manages the background server. See [Web](/cli/web).
`opencode service` manages the background server. See [Web](/cli/web).
```bash
$ opencode2 service start
$ opencode service start
```
Restart it.
```bash
$ opencode2 service restart
$ opencode service restart
```
Show its status.
```bash
$ opencode2 service status
$ opencode service status
```
Stop it.
```bash
$ opencode2 service stop
$ opencode service stop
```
Read a setting.
```bash
$ opencode2 service get hostname
$ opencode service get hostname
```
Set a setting.
```bash
$ opencode2 service set hostname 0.0.0.0
$ opencode service set hostname 0.0.0.0
```
Allow an extra CORS origin.
```bash
$ opencode2 service set cors https://app.example.com
$ opencode service set cors https://app.example.com
```
Pass an environment variable to the server process.
```bash
$ opencode2 service set env OPENCODE_LOG_LEVEL DEBUG
$ opencode service set env OPENCODE_LOG_LEVEL DEBUG
```
Reset a setting to its default.
```bash
$ opencode2 service unset hostname
$ opencode service unset hostname
```
View all subcommands and flags.
```bash
$ opencode2 service --help
$ opencode service --help
```
## api
`opencode2 api` makes a request to the running server.
`opencode api` makes a request to the running server.
```bash
$ opencode2 api GET /api/session
$ opencode api GET /api/session
```
Call an operation ID with a query parameter.
```bash
$ opencode2 api v2.session.list --param limit=10
$ opencode api v2.session.list --param limit=10
```
Send a JSON body.
```bash
$ opencode2 api v2.session.create --data '{"title": "New session"}'
$ opencode api v2.session.create --data '{"title": "New session"}'
```
Add a request header.
```bash
$ opencode2 api GET /api/session -H "accept: application/json"
$ opencode api GET /api/session -H "accept: application/json"
```
View all subcommands and flags.
```bash
$ opencode2 api --help
$ opencode api --help
```
## acp
`opencode2 acp` starts an Agent Client Protocol server over stdin and stdout for editor integrations. It runs until the client closes the connection.
`opencode acp` starts an Agent Client Protocol server over stdin and stdout for editor integrations. It runs until the client closes the connection.
```bash
$ opencode2 acp
$ opencode acp
```
View all subcommands and flags.
```bash
$ opencode2 acp --help
$ opencode acp --help
```
## debug
`opencode2 debug` provides debugging and troubleshooting tools.
`opencode debug` provides debugging and troubleshooting tools.
```bash
$ opencode2 debug agents
$ opencode debug agents
```
List configuration sources.
```bash
$ opencode2 debug config
$ opencode debug config
```
Show global paths.
```bash
$ opencode2 debug paths
$ opencode debug paths
```
Print a single path.
```bash
$ opencode2 debug paths db
$ opencode debug paths db
```
View all subcommands and flags.
```bash
$ opencode2 debug --help
$ opencode debug --help
```
## upgrade
`opencode2 upgrade` upgrades OpenCode to the latest or a specific version. Alias: `update`.
`opencode upgrade` upgrades OpenCode to the latest or a specific version. Alias: `update`.
```bash
$ opencode2 upgrade
$ opencode upgrade
```
Upgrade to a specific version with a specific package manager.
```bash
$ opencode2 upgrade 1.18.15 --method bun
$ opencode upgrade 1.18.15 --method bun
```
View all subcommands and flags.
```bash
$ opencode2 upgrade --help
$ opencode upgrade --help
```
## uninstall
`opencode2 uninstall` removes OpenCode and all related files.
`opencode uninstall` removes OpenCode and all related files.
```bash
$ opencode2 uninstall
$ opencode uninstall
```
Preview what would be removed.
```bash
$ opencode2 uninstall --dry-run
$ opencode uninstall --dry-run
```
Keep configuration and session data.
```bash
$ opencode2 uninstall --keep-config --keep-data
$ opencode uninstall --keep-config --keep-data
```
View all subcommands and flags.
```bash
$ opencode2 uninstall --help
$ opencode uninstall --help
```
@@ -134,6 +134,22 @@ Configure notifications and sounds:
| `sound_pack` | string | Selects the active sound pack. |
| `sounds` | object | Overrides files for `default`, `question`, `permission`, `error`, `done`, or `subagent_done` events. |
## Session
Choose whether the TUI prompts before granting permission requests:
```json title="cli.json"
{
"session": {
"permissions": "prompt"
}
}
```
| Field | Values | Description |
| ------------- | ------------------------ | ----------------------------------------------------------------------- |
| `permissions` | `prompt` or `autoaccept` | Prompts for permission requests or accepts every request automatically. |
## Diffs
Configure diff presentation:
+15 -15
View File
@@ -5,13 +5,13 @@ title: "Intro"
Run the CLI in a project to open the full-screen terminal interface:
```bash
opencode2
opencode
```
Pass a directory to work in a different project:
```bash
opencode2 ~/code/my-project
opencode ~/code/my-project
```
Suggested terminals:
@@ -26,22 +26,22 @@ palette.
## Automation
Use `opencode2 run` to submit a prompt without opening the interactive interface. It is designed for scripts, CI jobs, and
Use `opencode run` to submit a prompt without opening the interactive interface. It is designed for scripts, CI jobs, and
other workflows that need model output directly in the terminal.
```bash
opencode2 run "Explain this repository"
opencode run "Explain this repository"
```
## Mini
Use `opencode2 mini` to start OpenCode's minimal interactive interface instead of the full-screen TUI.
Use `opencode mini` to start OpenCode's minimal interactive interface instead of the full-screen TUI.
```bash
opencode2 mini
opencode mini
```
Run `opencode2 mini --help` to see its session, model, agent, prompt, and replay options.
Run `opencode mini --help` to see its session, model, agent, prompt, and replay options.
## Background service
@@ -51,8 +51,8 @@ connects to that server, which owns sessions, configuration, integrations, permi
Use `--standalone` to run with a private server, or `--server` to connect to a specific server URL:
```bash
opencode2 --standalone
opencode2 --server http://localhost:4096
opencode --standalone
opencode --server http://localhost:4096
```
See [Troubleshooting](/troubleshooting) for shared service diagnostics and the [API reference](/api) for server endpoints.
@@ -62,8 +62,8 @@ See [Troubleshooting](/troubleshooting) for shared service diagnostics and the [
Print a specific local path for use with other tools:
```bash
opencode2 debug paths db
sqlite3 "$(opencode2 debug paths db)"
opencode debug paths db
sqlite3 "$(opencode debug paths db)"
```
The optional selector accepts `db`, `home`, `data`, `config`, `cache`, `state`, `tmp`, `bin`, `log`, or `repos` and prints
@@ -73,7 +73,7 @@ resolve under the data directory, and `:memory:` is printed as-is. This command
Omit the selector to show all paths with labels:
```bash
opencode2 debug paths
opencode debug paths
```
## Uninstall
@@ -81,16 +81,16 @@ opencode2 debug paths
Preview the files and installation that will be removed:
```bash
opencode2 uninstall --dry-run
opencode uninstall --dry-run
```
Run `opencode2 uninstall` to confirm removal. OpenCode stops registered background services and persistent terminals before
Run `opencode uninstall` to confirm removal. OpenCode stops registered background services and persistent terminals before
removing global data, cache, configuration, and state. These directories are shared by OpenCode versions and channels.
To retain configuration and session data:
```bash
opencode2 uninstall --keep-config --keep-data
opencode uninstall --keep-config --keep-data
```
- `--keep-config` (`-c`) retains configuration files.
@@ -101,7 +101,7 @@ Unknown command IDs are rejected.
## Mini
The `app.clear` command works only in [`opencode2 mini`](/cli). Press `ctrl+l` to clear the visible screen and draw the prompt again. The terminal keeps the scrollback.
The `app.clear` command works only in [`opencode mini`](/cli). Press `ctrl+l` to clear the visible screen and draw the prompt again. The terminal keeps the scrollback.
```json title="cli.json"
{
+13 -13
View File
@@ -32,20 +32,20 @@ Use `auth login` for the same provider methods without opening the TUI. With no
interactive provider picker.
```bash
opencode2 auth login
opencode auth login
```
Pass an integration ID or name to skip the first picker. Use `--method key` to select API-key entry explicitly.
```bash
opencode2 auth login anthropic --method key
opencode auth login anthropic --method key
```
Method IDs are provider-specific. Run the command without `--method` to see the available methods when a provider has
more than one.
```bash
opencode2 auth login openai
opencode auth login openai
```
API-key entry and provider forms require an interactive terminal. OAuth methods that ask you to paste an authorization
@@ -71,15 +71,15 @@ Set a provider's supported environment variable on the server process that runs
server, pass it when starting standalone mode.
```bash
ANTHROPIC_API_KEY=sk-ant-... opencode2 --standalone
ANTHROPIC_API_KEY=sk-ant-... opencode --standalone
```
For the shared background server, add the variable to its managed environment. This stops a running service; the next
OpenCode command starts it with the new value.
```bash
opencode2 service set env ANTHROPIC_API_KEY sk-ant-...
opencode2 auth list
opencode service set env ANTHROPIC_API_KEY sk-ant-...
opencode auth list
```
Environment connections appear in `auth list` with type `environment`. They are not accounts: `auth logout` cannot
@@ -87,8 +87,8 @@ remove them, so unset the variable to disconnect. A saved account takes preceden
the same integration.
```bash
opencode2 auth list
opencode2 service unset env ANTHROPIC_API_KEY
opencode auth list
opencode service unset env ANTHROPIC_API_KEY
```
Some cloud providers also use their native ambient credential chain instead of an API-key variable:
@@ -102,7 +102,7 @@ Some cloud providers also use their native ambient credential chain instead of a
```bash
gcloud auth application-default login
GOOGLE_CLOUD_PROJECT=my-project opencode2
GOOGLE_CLOUD_PROJECT=my-project opencode
```
See [Providers](/providers) for provider-specific and server-side setup.
@@ -113,14 +113,14 @@ Each successful API-key, OAuth, or command login creates a saved account. The ne
active account by its label or credential ID.
```bash
opencode2 auth list
opencode2 auth switch anthropic work
opencode auth list
opencode auth switch anthropic work
```
Remove a saved account with `auth logout`. Both commands open pickers when their arguments are omitted.
```bash
opencode2 auth logout anthropic work
opencode auth logout anthropic work
```
In the TUI, `/connect` provides the same add, activate, rename, and delete operations for saved accounts.
@@ -130,7 +130,7 @@ In the TUI, `/connect` provides the same add, activate, rename, and delete opera
Saved API keys and OAuth tokens live in the server's SQLite database. For the local server, print its database path with:
```bash
opencode2 debug paths db
opencode debug paths db
```
The usual release path is `~/.local/share/opencode/opencode.db`; `XDG_DATA_HOME`, the release channel, and `OPENCODE_DB`
+80
View File
@@ -0,0 +1,80 @@
---
title: "TUI"
description: "Start a session and use the everyday workflows in the terminal interface."
---
Run OpenCode from your project, type a request, and press **Enter**.
```bash
cd ~/code/my-project
opencode
```
```text
Explain how authentication works in this project
```
Press **Shift+Enter**, **Ctrl+Enter**, or **Ctrl+J** for a new line. While OpenCode is working, **Enter** steers the active session and **Alt+Enter** queues the prompt for later.
## Context
Type `@` to search for a file, then select it to attach that file to the prompt. Keep typing after `@` to narrow the results.
```text
Review @src/auth.ts for error handling problems
```
You can include a line or range after the path.
```text
Explain @src/auth.ts#20-45
```
## Shell
Type `!` at the start of an empty prompt to enter shell mode. Enter a command and press **Enter**; press **Esc** to leave shell mode without running one.
```text
!git status
```
Shell commands run in the session's working directory and their output stays in the session.
## Commands
Type `/` to list slash commands. Continue typing to filter the list, then press **Enter** to run the selected command.
```text
/models
```
Common commands include `/new`, `/sessions`, `/models`, `/agents`, `/undo`, `/redo`, and `/editor`. Press **Ctrl+P** to open the command palette for every action available in the current view.
## Models
Press **Ctrl+X**, then **M** to choose a model, or run `/models`. Press **F2** to cycle through recently used models.
Press **Ctrl+X**, then **A** to choose an agent, or run `/agents`. Press **Shift+Tab** to cycle agents.
## Sessions
Press **Ctrl+X**, then **N** to start a new session. Press **Ctrl+X**, then **L** or run `/sessions` to return to an existing session; **Ctrl+O** opens recent sessions and projects together.
Tabs are enabled by default. Use **Ctrl+Tab** and **Ctrl+Shift+Tab** to move between them, **Ctrl+X** then **W** to close one, and **Ctrl+Shift+T** to reopen the last closed tab.
## History
Press **Ctrl+X**, then **U**, or run `/undo`, to revert the latest user message and the work that followed it. The reverted prompt returns to the composer so you can edit it.
Press **Ctrl+X**, then **R**, or run `/redo`, to restore the reverted work. Sending a new prompt instead accepts the revert and continues from that point.
## Editor
Set `VISUAL` or `EDITOR`, then press **Ctrl+X**, then **E**, or run `/editor`, to edit the current prompt in that editor. Save and exit to return the text to the composer, then press **Enter** to send it.
```bash
export EDITOR="nvim"
opencode
```
The leader key is **Ctrl+X** by default. See [Keybinds](/cli/keybinds) to change these shortcuts.
+13 -13
View File
@@ -9,7 +9,7 @@ TUI. It's available by default and password protected.
## Access
```bash
$ opencode2 pair
$ opencode pair
URLs http://127.0.0.1:49374
Username opencode
@@ -17,42 +17,42 @@ $ opencode2 pair
```
By default the server runs on port 49374 and listens only on localhost. You can
change this config with the `opencode2 service` command.
change this config with the `opencode service` command.
## Configure
Set any option with `opencode2 service set`:
Set any option with `opencode service set`:
```bash
# Listen on every network interface
$ opencode2 service set hostname 0.0.0.0
$ opencode service set hostname 0.0.0.0
# Use a fixed port instead of the channel default
$ opencode2 service set port 49374
$ opencode service set port 49374
# Replace the generated password
$ opencode2 service set password "a-long-secret"
$ opencode service set password "a-long-secret"
# Allow a web client served from another origin
$ opencode2 service set cors https://app.example.com,https://other.example.com
$ opencode service set cors https://app.example.com,https://other.example.com
# Pass an environment variable to the server process
$ opencode2 service set env OPENCODE_LOG_LEVEL DEBUG
$ opencode service set env OPENCODE_LOG_LEVEL DEBUG
```
Changing a setting stops the background server. To apply the new config
```bash
$ opencode2 service start
$ opencode service start
```
## Standalone
`opencode2 serve` runs the same server in the foreground instead of through the
`opencode serve` runs the same server in the foreground instead of through the
shared background service.
```bash
$ opencode2 serve --hostname 0.0.0.0 --port 4096
$ opencode serve --hostname 0.0.0.0 --port 4096
server listening on http://0.0.0.0:4096
server password <password>
```
@@ -60,7 +60,7 @@ server password <password>
Use it when you want to:
- Run OpenCode on a shared, always-on, or remote host, then connect clients with
`opencode2 --server <url>`.
`opencode --server <url>`.
- Control the hostname, port, and CORS origins for a single process.
- Run under a supervisor like systemd, Docker, or another environment that expects
a foreground process.
@@ -70,5 +70,5 @@ Use it when you want to:
Connect a client to it with `--server`:
```bash
$ opencode2 --server http://127.0.0.1:4096
$ opencode --server http://127.0.0.1:4096
```
+5 -6
View File
@@ -2,10 +2,9 @@
title: "Intro"
---
These docs describe OpenCode 2 and its released APIs, configuration, and plugin system.
These docs describe OpenCode and its APIs, configuration, and plugin system.
OpenCode 2 installs and runs as `opencode2`. It does not replace OpenCode 1's `opencode` binary, so you can keep both
versions installed and run them side by side.
OpenCode installs and runs as `opencode`.
## Install
@@ -20,13 +19,13 @@ $ curl -fsSL https://opencode.ai/v2/install | bash`}
/>
</div>
The npm package uses a trusted postinstall script to select the native `opencode2` binary for your platform. The Bun and pnpm
The npm package uses a trusted postinstall script to select the native `opencode` binary for your platform. The Bun and pnpm
commands above explicitly allow that script to run.
On Arch Linux, install [`opencode-beta`](https://aur.archlinux.org/packages/opencode-beta) from the AUR with `paru`.
It provides the `opencode2` command; manage updates through your AUR helper.
It provides the `opencode` command; manage updates through your AUR helper.
Homebrew, Windows package managers, Docker, and standalone binaries are not supported in V2.
Homebrew, Windows package managers, Docker, and standalone binaries are not supported.
---
+11 -11
View File
@@ -9,14 +9,14 @@ OpenCode connects to [Model Context Protocol](https://modelcontextprotocol.io/)
Add a remote server from the project that should use it, then check its connection:
```sh
opencode2 mcp add context7 --url https://mcp.context7.com/mcp
opencode2 mcp list
opencode mcp add context7 --url https://mcp.context7.com/mcp
opencode mcp list
```
The command writes the server to the project [configuration](/config). Add `--global` to make it available in every project:
```sh
opencode2 mcp add context7 --global --url https://mcp.context7.com/mcp
opencode mcp add context7 --global --url https://mcp.context7.com/mcp
```
Remote servers use OAuth by default. If the list shows `needs authentication`, open OpenCode, run `/mcps`, select the server, and sign in. A connected server is ready for an agent to use:
@@ -79,7 +79,7 @@ A higher-precedence project config replaces the entire server object with the sa
A local server is a command that OpenCode starts over the MCP stdio transport. Add one with a command after `--`:
```sh
opencode2 mcp add everything -- npx -y @modelcontextprotocol/server-everything
opencode mcp add everything -- npx -y @modelcontextprotocol/server-everything
```
Use configuration for process options such as a working directory or environment variables:
@@ -128,7 +128,7 @@ Use `{env:NAME}` for environment substitution. Shell expressions such as `$NAME`
A remote server uses the MCP Streamable HTTP transport and requires an absolute URL:
```sh
opencode2 mcp add context7 --url https://mcp.context7.com/mcp
opencode mcp add context7 --url https://mcp.context7.com/mcp
```
Use configuration when the server needs headers or other options. Store secrets in environment variables rather than in the file:
@@ -195,7 +195,7 @@ For dynamic registration, configure only the server URL:
If the server needs authentication, run `/mcps`, select it, and complete authorization in the browser. The CLI can start the same flow:
```sh
opencode2 mcp auth sentry
opencode mcp auth sentry
```
When a provider gives you client credentials, use V2's snake_case OAuth fields:
@@ -232,7 +232,7 @@ When a provider gives you client credentials, use V2's snake_case OAuth fields:
Remove stored OAuth credentials when you need to sign in again or switch accounts:
```sh
opencode2 mcp logout sentry
opencode mcp logout sentry
```
## Timeouts
@@ -350,15 +350,15 @@ The ID is request metadata, not a tool argument, so it is absent from the model-
List servers and their current connection state from any project:
```sh
opencode2 mcp list
opencode mcp list
```
Use `/mcps` in OpenCode to view, connect, disconnect, or authenticate servers. Use the CLI to add servers and manage OAuth credentials:
```sh
opencode2 mcp add sentry --url https://mcp.sentry.dev/mcp
opencode2 mcp auth sentry
opencode2 mcp logout sentry
opencode mcp add sentry --url https://mcp.sentry.dev/mcp
opencode mcp auth sentry
opencode mcp logout sentry
```
To remove a server, delete its entry from the project or global configuration where it was added:
+6 -3
View File
@@ -2,8 +2,8 @@
title: "Migrate from V1"
---
OpenCode 1 and OpenCode 2 can be installed side by side. V1 runs as `opencode`, while V2 installs and runs separately as
`opencode2`.
OpenCode 1 and OpenCode 2 both use the `opencode` command and are no longer installed side by side by default. Remove a
package-managed V1 installation before installing V2; the V2 curl installer replaces the V1 binary.
## Breaking changes
@@ -399,7 +399,10 @@ See [Models](/models) for the complete native model shape.
### Supported fields without direct native equivalents
Most fields that keep the same shape, including `shell`, `model`, `default_agent`, `watcher`, `formatter`,
`lsp`, `instructions`, `enterprise`, and `tool_output`, require no migration.
`instructions`, `enterprise`, and `tool_output`, require no migration.
V2 accepts and preserves `lsp` configuration, but it does not run language servers, expose LSP tools, or produce LSP
diagnostics. Replace workflows that depend on those capabilities with the project's lint, typecheck, or compiler commands.
The V1 provider filters do not have one-to-one native V2 config fields, but their behavior remains supported:
+2 -2
View File
@@ -32,7 +32,7 @@ Availability is project-specific:
Use `--model` to choose a model for one command-line run without changing the configured default:
```bash
opencode2 run --model anthropic/claude-sonnet-4-5 "Refactor parseToken"
opencode run --model anthropic/claude-sonnet-4-5 "Refactor parseToken"
```
Agents and commands can also choose their own model. See [Agents](/agents) and [Commands](/commands).
@@ -63,7 +63,7 @@ Variants are named options for one model, often used for reasoning effort or tok
one for a run, session, agent, or command:
```bash
opencode2 run --model openai/gpt-5.2#high "Review this migration plan"
opencode run --model openai/gpt-5.2#high "Review this migration plan"
```
Variant names come from the selected model's current catalog metadata. Names such as `low`, `high`, and `max` are not
+104
View File
@@ -0,0 +1,104 @@
---
title: Network
description: Configure proxies and custom certificate authorities.
---
Set the standard proxy variables and exclude loopback addresses. OpenCode uses local HTTP connections between the CLI and
its background service, so the loopback exclusion is required when a proxy is configured in the CLI environment.
```bash
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1,::1
opencode
```
`HTTP_PROXY` handles HTTP destinations and `HTTPS_PROXY` handles HTTPS destinations. The proxy URL itself can use `http://`
for both variables. `NO_PROXY` is a comma-separated list of hosts and addresses that connect directly.
## Service
Shell exports affect a background service only when that service starts from the shell. Persist the variables in the managed
service configuration so later service starts use the same network settings.
```bash
opencode service set env HTTP_PROXY http://proxy.example.com:8080
opencode service set env HTTPS_PROXY http://proxy.example.com:8080
opencode service set env NO_PROXY localhost,127.0.0.1,::1
opencode service start
```
Changing a managed environment variable stops the running service. `service start` starts it again with the new environment.
Keep `NO_PROXY` in the CLI shell too when that shell sets a proxy, because managed service variables apply to the server
process, not the CLI process.
Remove a persisted variable with `service unset env`:
```bash
opencode service unset env HTTPS_PROXY
```
## Authentication
For a proxy that accepts URL credentials, include the username and password in each proxy URL that needs authentication.
Percent-encode characters such as `@`, `:`, and `/` inside either credential.
```bash
export HTTPS_PROXY='http://user:p%40ssword@proxy.example.com:8080'
```
For the background service, persist the complete URL. The value is stored in the private service configuration and is
shown by `opencode service get env`, so do not share that output.
```bash
opencode service set env HTTPS_PROXY 'http://user:p%40ssword@proxy.example.com:8080'
```
## Certificates
Set `NODE_EXTRA_CA_CERTS` to a PEM file when the proxy or destination uses a certificate signed by a private certificate
authority. The extra authorities are added to the runtime trust store when the process starts.
```bash
export NODE_EXTRA_CA_CERTS=/etc/company/ca.pem
opencode --standalone
```
Persist the file path for the background service, then restart it:
```bash
opencode service set env NODE_EXTRA_CA_CERTS /etc/company/ca.pem
opencode service start
```
## Foreground
`--standalone` and `serve` use the environment of their own process. `opencode service set env` does not configure these
processes.
```bash
HTTP_PROXY=http://proxy.example.com:8080 \
HTTPS_PROXY=http://proxy.example.com:8080 \
NO_PROXY=localhost,127.0.0.1,::1 \
opencode --standalone
```
Apply the same pattern to a foreground server:
```bash
HTTPS_PROXY=http://proxy.example.com:8080 \
NO_PROXY=localhost,127.0.0.1,::1 \
opencode serve
```
## Verify
Check that the CLI can reach the background service after applying the settings:
```bash
opencode api get /api/health
```
A successful health response verifies the local client-to-service connection. Run a normal provider request to verify the
service's outbound proxy and certificate path.
+12 -12
View File
@@ -81,13 +81,13 @@ use `.*` to match an ID prefix. A later ID re-enables a plugin.
Install, list, check, update, or remove global package plugins with the CLI.
```sh
opencode2 plugin add opencode-acme-plugin@1.2.0
opencode2 plugin list
opencode2 plugin list --builtin
opencode2 plugin check
opencode2 plugin update
opencode2 plugin update opencode-acme-plugin
opencode2 plugin remove opencode-acme-plugin@1.2.0
opencode plugin add opencode-acme-plugin@1.2.0
opencode plugin list
opencode plugin list --builtin
opencode plugin check
opencode plugin update
opencode plugin update opencode-acme-plugin
opencode plugin remove opencode-acme-plugin@1.2.0
```
`plugin check` checks server and TUI-only package plugins for updates. `plugin update` updates every outdated package;
@@ -98,10 +98,10 @@ Git repositories can use hosted shortcuts, HTTPS, or SSH, including private repo
Git credentials.
```sh
opencode2 plugin add @acme/opencode-plugin@latest
opencode2 plugin add github:acme/opencode-plugin
opencode2 plugin add git+ssh://git@github.com/acme/opencode-plugin.git#main
opencode2 plugin add 'github:acme/plugins#main::path:packages/opencode-plugin'
opencode plugin add @acme/opencode-plugin@latest
opencode plugin add github:acme/opencode-plugin
opencode plugin add git+ssh://git@github.com/acme/opencode-plugin.git#main
opencode plugin add 'github:acme/plugins#main::path:packages/opencode-plugin'
```
Branches, tags, complete commit hashes, and npm's `::path:` repository-subdirectory selectors are supported. Configure
@@ -116,7 +116,7 @@ still require restarting OpenCode.
```sh
touch .opencode/plugins/concise/index.ts
opencode2 service restart
opencode service restart
```
## Terminal
+267 -16
View File
@@ -238,18 +238,21 @@ See [Models](/models) for selection, defaults, capabilities, limits, costs, and
## Azure
Azure supports either an API key or the Microsoft Entra ID session from the Azure CLI. To use Entra ID, install the
[Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) and sign in before connecting.
Azure's standard catalog endpoint needs a resource name in addition to its credential. Set `settings.resourceName`
once, then connect an API key or use the Microsoft Entra ID session from the Azure CLI as described in
[Provider accounts](/cli/providers).
```bash
az login
```
For a resource in another tenant or subscription, select both explicitly.
```bash
az login --tenant TENANT_ID
az account set --subscription NAME_OR_ID
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"azure": {
"settings": {
"resourceName": "my-models",
},
},
},
}
```
Find the **Resource name** in the [Azure portal](https://portal.azure.com/) or
@@ -262,14 +265,23 @@ az cognitiveservices account list \
--output table
```
In OpenCode, select **Azure**, then **Microsoft Entra ID (Azure CLI)**. Enter the resource name when prompted;
`AZURE_RESOURCE_NAME` skips that prompt when already set.
To use Entra ID, install the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli), sign in, then
choose **Microsoft Entra ID (Azure CLI)** when connecting Azure.
```text
/connect
/models
```bash
az login
```
For a resource in another tenant or subscription, select both explicitly.
```bash
az login --tenant TENANT_ID
az account set --subscription NAME_OR_ID
```
Instead of configuration, `AZURE_RESOURCE_NAME` supplies the resource name to the OpenCode server. The legacy
`AZURE_COGNITIVE_SERVICES_RESOURCE_NAME` variable also works.
OpenCode does not query Azure management APIs or discover deployments. If a deployment does not match its catalog
model name, map an OpenCode model ID to the deployment with `modelID`.
@@ -299,6 +311,245 @@ If a request uses a token from the wrong tenant, sign in again with the required
az login --tenant TENANT_ID
```
## Bedrock
Amazon Bedrock uses the AWS default credential chain. A named AWS profile is the simplest durable setup; OpenCode also
recognizes access-key environments, web identity, and container credentials.
```bash
aws configure sso --profile work
aws sso login --profile work
```
Select the profile and region in provider settings. A configured `profile`, `AWS_PROFILE`, `AWS_ACCESS_KEY_ID`, web
identity token file, or container credential URI activates the provider; a region alone does not.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"amazon-bedrock": {
"settings": {
"profile": "work",
"region": "us-west-2",
},
},
},
}
```
Without an explicit region, OpenCode uses `AWS_REGION`, then `AWS_DEFAULT_REGION`, then `us-east-1`. For a private or
VPC endpoint, set `baseURL` while keeping the same profile and region.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"amazon-bedrock": {
"settings": {
"profile": "work",
"region": "us-west-2",
"baseURL": "https://bedrock-runtime.vpce.example.com",
},
},
},
}
```
Bedrock API keys use `AWS_BEARER_TOKEN_BEDROCK`. Other AWS variables feed SigV4 and are not stored as OpenCode API-key
accounts.
## Vertex
Google Vertex uses Application Default Credentials (ADC) and needs a project before its models become available.
Create local ADC, then set the project and location in provider settings.
```bash
gcloud auth application-default login
```
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"google-vertex": {
"settings": {
"project": "my-project",
"location": "us-central1",
},
},
},
}
```
OpenCode also resolves the project from `GOOGLE_VERTEX_PROJECT`, `GOOGLE_CLOUD_PROJECT`, `GCP_PROJECT`, or
`GCLOUD_PROJECT`, in that order. It resolves the location from `GOOGLE_VERTEX_LOCATION`, `GOOGLE_CLOUD_LOCATION`, or
`VERTEX_LOCATION`, and defaults to `us-central1`.
```bash
GOOGLE_CLOUD_PROJECT=my-project GOOGLE_VERTEX_LOCATION=europe-west4 opencode --standalone
```
Service accounts work through the same ADC path. Point `GOOGLE_APPLICATION_CREDENTIALS` at the service-account JSON
file and still provide a project through settings or one of the project variables above.
```bash
GOOGLE_APPLICATION_CREDENTIALS=/secure/vertex.json \
GOOGLE_CLOUD_PROJECT=my-project \
opencode --standalone
```
Use the single `google-vertex` provider ID for Gemini, Anthropic, and OpenAI-compatible Vertex catalog models. The old
`google-vertex-anthropic` provider ID is unavailable in V2.
## Copilot
GitHub Copilot supports device OAuth rather than manual API-key entry. Connect **GitHub Copilot**, choose GitHub.com or
GitHub Enterprise, finish the device flow, then open `/models`.
```text
/connect
# Select GitHub Copilot, then Login with GitHub Copilot.
/models
```
For GitHub Enterprise, enter the deployment URL or domain when prompted. OpenCode uses a Copilot API endpoint returned
by GitHub when available; otherwise it derives the endpoint from the enterprise domain.
```text
company.ghe.com
```
The connected account needs Copilot Chat access. If GitHub reports no entitlement, sign up for Copilot Free or ask the
organization to assign a Copilot seat, then connect again. OpenCode fetches the account's current model list after login
and whenever the active Copilot account changes.
## Ollama
Start Ollama and pull a model. OpenCode probes `http://127.0.0.1:11434`, discovers completion models, and adds them to
`/models` without an account connection.
```bash
ollama serve
ollama pull qwen3:8b
```
Point OpenCode at a remote or proxied Ollama server with `settings.baseURL`. Include `/v1`; OpenCode derives the native
`/api/tags` and `/api/show` discovery paths from it. `apiKey` is optional and is sent as a bearer token to discovery and
model requests.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"ollama": {
"settings": {
"baseURL": "https://ollama.example.com/v1",
"apiKey": "{env:OLLAMA_API_KEY}",
},
},
},
}
```
Discovery refreshes periodically and keeps the last successful inventory during a temporary outage. Embedding-only
models do not appear because OpenCode only adds models whose Ollama metadata includes the `completion` capability.
## Runtimes
LM Studio and vLLM also have built-in local discovery. Their default endpoints are
`http://127.0.0.1:1234/v1` and `http://127.0.0.1:8000/v1`.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"lmstudio": {
"settings": { "baseURL": "http://gpu-host:1234/v1" },
},
"vllm": {
"settings": { "baseURL": "http://gpu-host:8000/v1" },
},
},
}
```
LM Studio discovers language models from `/api/v1/models`. vLLM checks `/health` before reading `/v1/models`; its
discovery cannot infer tool support, so discovered vLLM models start with tools disabled. For another OpenAI-compatible
runtime, use the [custom provider](#custom) recipe and list its models explicitly.
## Gateways
OpenRouter uses its native runtime and catalog. Provide `OPENROUTER_API_KEY` to the OpenCode server or connect an
OpenRouter account, then refer to models with the full OpenRouter model ID after the provider prefix.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "openrouter/anthropic/claude-sonnet-4",
}
```
Keep the native OpenRouter package when overriding its endpoint or routing through an OpenRouter-compatible gateway.
This preserves OpenRouter request and reasoning behavior.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"openrouter": {
"settings": {
"baseURL": "https://openrouter-gateway.example.com/api/v1",
},
"headers": {
"X-Gateway-Tenant": "engineering",
},
},
},
}
```
For a gateway that exposes an OpenAI-compatible API but has its own model inventory, define a custom provider instead.
The configuration key becomes the provider prefix and each `models` key becomes a selectable model ID.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "company/coder",
"providers": {
"company": {
"env": ["COMPANY_GATEWAY_KEY"],
"package": "@opencode/ai/providers/openai-compatible",
"settings": {
"baseURL": "https://gateway.example.com/v1",
},
"models": {
"coder": { "modelID": "upstream/coder-v2" },
},
},
},
}
```
## Errors
Provider and model errors usually identify the failed stage. Check the server process environment and the exact model
ID before changing packages.
| Error or symptom | Check |
| --- | --- |
| `No model is available for session ...` | No enabled model is currently selectable. Finish the provider-specific setup, then choose a model in `/models`. |
| `Model unavailable: provider/model` | The provider is inactive, the model ID is absent or disabled, or dynamic discovery no longer returns it. For custom aliases, check the `models` map key rather than `modelID`. |
| `Cannot initialize provider/model: NAME is required to resolve the provider endpoint` | A `${NAME}` placeholder remains in `baseURL`. Set that variable on the OpenCode server or replace the template with a complete endpoint. |
| `Azure resource name is missing` | Set `providers.azure.settings.resourceName`, `AZURE_RESOURCE_NAME`, or a complete `settings.baseURL`. |
| Vertex does not appear | Set a resolvable project in provider settings or a supported project variable. ADC alone does not activate the provider. |
| Bedrock does not appear | Provide a profile or another supported AWS credential-chain input. `AWS_REGION` by itself only selects a region. |
| Ollama has no models | Confirm the server exposes `/api/tags` and `/api/show` at the path derived from `baseURL`, and that `/api/show` reports the `completion` capability. |
| Copilot has no models | Confirm the active OAuth account has Copilot Chat access. A failed model sync is logged as `failed to sync GitHub Copilot models`. |
V2 rejects the retired provider IDs with a direct replacement. Use `azure/<model>` instead of
`azure-cognitive-services/<model>`, and `google-vertex/<model>` instead of `google-vertex-anthropic/<model>`.
## WebSockets
OpenAI, xAI, and supported Azure Responses models can keep one WebSocket connection open per session. Consecutive steps
+265
View File
@@ -0,0 +1,265 @@
---
title: "Tools"
---
Ask for the outcome you want. OpenCode chooses the available tools, requests
approval when required, and returns the result.
```text
Find where request timeouts are configured, change the default to 30 seconds,
and run the relevant existing tests.
```
This task typically uses `grep` or `glob`, `read`, an editing tool, and `shell`.
You can also name a tool when you want a specific approach.
```text
Use grep to find every reference to DEFAULT_TIMEOUT, then read the matching files.
```
## Files
### Read
`read` returns a file or a non-recursive directory listing. Give it a `path`;
use `offset` and `limit` to page through large results.
```text
Read src/server.ts starting at line 120 and return at most 80 lines.
```
Text reads contain numbered lines and are limited to 2,000 lines and 50 KiB per
page. Individual lines are shortened after 2,000 characters. The tool also
passes PNG, JPEG, GIF, WebP, and PDF files to the model, up to 20 MiB of source
data. Other binary files fail. See [Attachments](/attachments) for supported
media and image processing.
Permission: `read` with the normalized path as its resource.
### Glob
`glob` finds file paths from a pattern such as `**/*.ts`. It searches the
current directory unless `path` selects another relative directory; `hidden`
includes hidden entries and `limit` caps the results.
```text
Use glob with **/*.test.ts under packages/core and return at most 50 files.
```
The default limit is 100 files and the search timeout is 30 seconds.
Permission: `glob` with the requested pattern as its resource.
### Grep
`grep` searches file contents and returns paths, line numbers, and previews.
Its `pattern` uses regular-expression syntax unless `literal` is true. Narrow a
search with `path`, `include`, `caseSensitive`, or `limit`.
```text
Use grep for the literal text "request timed out" in src, including only *.ts files.
```
The default limit is 100 matching lines and the search timeout is 30 seconds.
Permission: `grep` with the requested pattern, not the search path, as its
resource.
### Edit
`edit` replaces text in an existing file. Supply `path`, `oldString`, and a
different `newString`; the old text must match exactly once unless `replaceAll`
is true.
```text
In src/config.ts, replace the unique text "timeout: 60" with "timeout: 30".
```
Use `edit` for a focused change. OpenCode may format the file after writing it.
Permission: `edit` with the target path as its resource.
### Write
`write` creates or completely replaces a text file from `path` and `content`.
It creates missing parent directories and may run the configured formatter.
```text
Create docs/example.md with a heading and one setup command.
```
Use `edit` instead when only part of an existing file should change.
Permission: `edit` with the target path as its resource.
### Patch
`patch` applies one patch that can add, update, move, or delete several files.
Its `patchText` uses `*** Add File`, `*** Update File`, and `*** Delete File`
sections.
```text
Use patch to rename src/old.ts to src/new.ts and update its exported name.
```
OpenCode exposes `patch` for supported GPT models; other models receive `edit`
and `write` instead. Permission: `edit` with every affected path as a resource.
Paths outside the active Location or its project worktree also require
`external_directory` approval. See [Permissions](/permissions) for path
normalization, rule order, and saved approvals. To make outside material
available by name, configure [References](/references).
## Commands
### Shell
`shell` executes a command in the host user's shell. Set `workdir` instead of
putting `cd` in the command, and set `timeout` in milliseconds when the default
two-minute foreground timeout is not suitable.
```text
Run bun test from packages/core with a five-minute timeout.
```
Set `background` to true for a dev server or another long-running process.
Background calls return immediately and notify the session when they finish;
they have no timeout unless one is supplied. Large output is shortened and the
complete output is retained in a managed file.
Permission: `shell` with each scanner-produced command as a resource. The
scanner also checks an external working directory and directories it can infer
from the command. Shell has the host user's filesystem, process, and network
authority, so use narrow rules in [Permissions](/permissions).
## Web
### Webfetch
`webfetch` retrieves one HTTP or HTTPS URL as `markdown`, `text`, or `html`.
Markdown is the default; `timeout` accepts up to 120 seconds.
```text
Fetch https://example.com/docs as markdown.
```
It accepts textual responses such as HTML, plain text, JSON, and XML, but not
images or other binary downloads. Large output can be shortened while the full
text is retained in managed storage. Permission: `webfetch` with the requested
URL as its resource.
### Websearch
`websearch` searches the selected integration for current information. Its
input is a search `query`.
```text
Search the web for the latest Bun release notes.
```
Permission: `websearch` with the query as its resource. See [Websearch](/websearch)
for providers, selection, rate limits, and disabling the tool.
## Interaction
### Question
`question` pauses execution and presents one or more questions to the user.
Each question has a short header, prompt, and choices; `multiple` allows more
than one choice. A free-form answer is always available.
```text
Before changing the API, ask me to choose between a breaking change and a compatibility layer.
```
Permission: `question` with `*` as its resource. A client must support the
interactive form, and dismissing it cancels the question.
### Skill
`skill` loads the instructions and bundled resources for one available skill
ID into the conversation.
```text
Load the effect skill before changing this Effect code.
```
The ID must be in the advertised skill list or explicitly named by the user.
Permission: `skill` with the skill ID as its resource. See [Skills](/skills) for
creation, discovery, and loading rules.
## Automation
### Subagent
`subagent` starts a child session with a configured subagent. Supply the agent
ID, a short `description`, and a complete `prompt`.
```text
Ask the explore subagent to map the authentication flow and return the key files.
```
Foreground calls wait for the result. `background: true` returns immediately
and notifies the parent when the child finishes. Pass the returned `sessionID`
to continue that same child conversation. Only subagent-mode agents can be
used, and the default nesting depth is one.
Permission: `subagent` with the selected agent ID as its resource.
### Execute
`execute` runs JavaScript in Code Mode so the agent can call and combine tools
from the catalog. It is useful for parallel independent calls and for processing
results without adding every intermediate value to the model context.
```text
Read package.json and README.md in parallel, then return their relevant setup details.
```
The runtime has no direct filesystem access, imports, timers, or `fetch`; it can
only call tools in its catalog. Permission `execute` with resource `*` controls
whether Code Mode is available. Every nested tool still enforces its own
permission.
### Sessions
The `opencode` Code Mode namespace contains session utilities:
- `session_rename` changes the title of the current session, or another session
selected by `sessionID`.
- `session_move` moves the current session, or another selected session, to a
relative or absolute `directory`. The move takes effect at a safe boundary,
so destination-dependent work belongs in a later call.
```text
Rename this session to "Timeout cleanup", then move it to the new worktree.
```
These utilities do not request a built-in permission action.
## Browser
The `browser` Code Mode namespace controls the browser attached by the OpenCode
desktop app. Open a tab, keep its returned `tabID`, then pass that explicit ID to
every page operation.
```text
Open https://example.com, take a snapshot of that tab, and report the main heading.
```
The namespace includes tab and navigation commands, page snapshots and search,
clicking and form input, screenshots, file transfer, console and network
inspection, performance traces, heap inspection, and Lighthouse audits.
Screenshots require a focused visible tab. Upload paths are server-local;
captures return server-local paths, and each file transfer is limited to 5 MiB.
Page content, logs, headers, and response bodies are untrusted data, not agent
instructions. A `browser` deny rule with resource `*` removes the browser catalog;
browser operations do not issue individual permission prompts.
## Extensions
[MCP servers](/mcp-servers) add tools whose names and inputs come from each
connected server. Their permission action is `<server>_<tool>` with resource
`*`. They are not a fixed part of the built-in catalog.
[Skills](/skills) add task instructions rather than new executable tools.
[Attachments](/attachments) put user-selected content into a prompt, while
[References](/references) provide named outside directories. These features
complement tools without bypassing their permission checks.
@@ -15,26 +15,26 @@ state. Start by determining whether an issue is in a client, the shared server,
Show the current server status:
```bash
opencode2 service status
opencode service status
```
Verify that its API is healthy:
```bash
opencode2 api get /api/health
opencode api get /api/health
```
If the service is stuck or unhealthy, restart it:
```bash
opencode2 service restart
opencode service restart
```
You can also stop and start it explicitly:
```bash
opencode2 service stop
opencode2 service start
opencode service stop
opencode service start
```
<Callout type="note">
@@ -47,30 +47,30 @@ opencode2 service start
If a browser client on another origin cannot connect because of CORS, add the client's origin to the service configuration:
```bash
opencode2 service set cors http://192.168.1.10:3001
opencode2 service get cors
opencode service set cors http://192.168.1.10:3001
opencode service get cors
```
Use an exact HTTP or HTTPS origin, including the port when needed, without a path or trailing slash. To allow multiple
origins, pass a comma-separated list as one argument; whitespace around each origin is trimmed:
```bash
opencode2 service set cors "http://192.168.1.10:3001, https://app.example.com"
opencode service set cors "http://192.168.1.10:3001, https://app.example.com"
```
`service get cors` prints a JSON array. Remove the configured list with:
```bash
opencode2 service unset cors
opencode service unset cors
```
Setting or unsetting service configuration stops the background service. Its next start picks up the new configuration;
use `opencode2 service start` to start it explicitly.
use `opencode service start` to start it explicitly.
For a foreground server, repeat `--cors` for each additional allowed origin:
```bash
opencode2 serve --cors http://192.168.1.10:3001 --cors https://app.example.com
opencode serve --cors http://192.168.1.10:3001 --cors https://app.example.com
```
With `serve --service`, supplied `--cors` flags override the persisted list for that process. Without those flags, service
@@ -86,7 +86,7 @@ See the [API reference](/api) for all endpoints and operation IDs.
Pass a JSON request body with `--data` or `-d`, and add headers with `--header` or `-H`.
<Callout type="warning">
Running `opencode2 api` may start the background service when no compatible healthy service is available.
Running `opencode api` may start the background service when no compatible healthy service is available.
</Callout>
## Read logs
@@ -117,7 +117,7 @@ On macOS and Linux, you can signal a running OpenCode process to capture diagnos
from the health endpoint:
```bash
opencode2 api get /api/health
opencode api get /api/health
```
Use the `pid` from the response with one of these signals:
@@ -177,8 +177,8 @@ The database normally lives at:
Include the following when reporting a reproducible problem:
- Output from `opencode2 --version`
- Output from `opencode2 service status`
- Output from `opencode --version`
- Output from `opencode service status`
- The smallest sequence of steps that reproduces the issue
- Whether the issue affects the shared service, a specific client, or one project
- Relevant log lines, including their `run` and `role` fields
+1 -1
View File
@@ -24,7 +24,7 @@ OpenCode includes four search providers:
Connect an account from the TUI with `/connect`, or set the provider's environment variable before starting OpenCode.
```bash
$ TAVILY_API_KEY=your-key opencode2
$ TAVILY_API_KEY=your-key opencode
```
## Selection
+4
View File
@@ -38,11 +38,13 @@ export const docsSections: DocsSection[] = [
{ title: "Plugins", slug: "plugins" },
{ title: "Providers", slug: "providers" },
{ title: "Websearch", slug: "websearch" },
{ title: "Network", slug: "network" },
{ title: "Snapshots", slug: "snapshots" },
{ title: "Compaction", slug: "compaction" },
{ title: "Formatters", slug: "formatters" },
{ title: "References", slug: "references" },
{ title: "Attachments", slug: "attachments" },
{ title: "Tools", slug: "tools" },
{ title: "MCP servers", slug: "mcp-servers" },
{ title: "Permissions", slug: "permissions" },
{ title: "Instructions", slug: "instructions" },
@@ -66,10 +68,12 @@ export const docsSections: DocsSection[] = [
{
items: [
{ title: "Intro", slug: "cli" },
{ title: "TUI", slug: "cli/tui" },
{ title: "Config", slug: "cli/config" },
{ title: "Web", slug: "cli/web" },
{ title: "Providers", slug: "cli/providers" },
{ title: "Commands", slug: "cli/commands" },
{ title: "ACP", slug: "cli/acp" },
{ title: "Theme", slug: "cli/theme" },
{ title: "Plugins", slug: "cli/plugins" },
{ title: "Keybinds", slug: "cli/keybinds" },