Compare commits

..

17 Commits

Author SHA1 Message Date
Dax Raad e6db1cf29d ci: ignore release commits 2025-07-31 19:57:07 -04:00
Dax Raad f07f04d969 fix escape button not canceling if retry in progress 2025-07-31 19:55:57 -04:00
Dax Raad 33d613a470 docs: sync 2025-07-31 19:50:51 -04:00
Dax Raad 0bbd7ea17b docs: formatters 2025-07-31 19:50:31 -04:00
Dax Raad 87f3166437 ignore: config 2025-07-31 19:45:44 -04:00
opencode 7665bd9439 Release v0.3.105 2025-07-31 23:41:27 +00:00
Dax Raad 30e10127f2 formatter config 2025-07-31 19:36:07 -04:00
Jay V 5e66fc2318 docs: edit premissions doc 2025-07-31 19:10:54 -04:00
opencode c1c99c7e0f Release v0.3.104 2025-07-31 23:02:36 +00:00
Dax Raad 04e3e83db3 allow disabling formatter 2025-07-31 18:56:04 -04:00
Dax Raad 4273714a62 fix issue with some bash commands asking for permission 2025-07-31 18:35:51 -04:00
Dax Raad a21e237706 ignore: update opencode.json 2025-07-31 18:13:40 -04:00
Dax Raad aa9105649d docs: permissions 2025-07-31 18:11:34 -04:00
Dax Raad 53be288040 docs: permissions 2025-07-31 18:11:34 -04:00
Frank 13dbf912ca Remove hardcoded vscode extension theme 2025-07-31 17:53:18 -04:00
Jay V 69966c73f8 docs: add more providers 2025-07-31 17:47:24 -04:00
opencode a00de2df08 Release v0.3.102 2025-07-31 21:25:12 +00:00
21 changed files with 545 additions and 454 deletions
+11 -4
View File
@@ -48,6 +48,7 @@
"hono-openapi": "0.4.8",
"isomorphic-git": "1.32.1",
"jsonc-parser": "3.3.1",
"minimatch": "10.0.3",
"open": "10.1.2",
"remeda": "catalog:",
"tree-sitter": "0.22.4",
@@ -122,7 +123,7 @@
"catalog": {
"@tsconfig/node22": "22.0.2",
"@types/node": "22.13.9",
"ai": "5.0.0-beta.33",
"ai": "5.0.0-beta.34",
"hono": "4.7.10",
"remeda": "2.26.0",
"typescript": "5.8.2",
@@ -143,11 +144,11 @@
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-beta.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.9" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-1K5L7mY04ZwpngkDPLaiBiCivVj1h7gDiCZjAIgXtVp0S2zQ+1efnM/K/o2Pig6rUbt559rDLLalwZUgvn0vig=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-beta.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-felWPMuECZRGx8xnmvH5dW3jywKTkGnw/tXN8szphGzEDr/BfxywuXijfPBG2WBUS6frPXsvSLDRdCm5W38PXA=="],
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.2", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-vqhtZA7R24q1XnmfmIb1fZSmHMIaJH1BVQ+0kFnNJgqWsc+V8i+yfetZ37gUc4fXATFmBuS/6O7+RPoHsZ2Fqg=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.9", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-RJMeoqFA9mGo1XOE20bpVv4/ikVbZMHo00vmF4RweN7GHS+nEXU3SHFgtcp7NBG3j8W15b9MAitOBycRMYxecg=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA=="],
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
@@ -355,6 +356,10 @@
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
@@ -589,7 +594,7 @@
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
"ai": ["ai@5.0.0-beta.33", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.18", "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.9", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-TKDOYDRhS6kSmfbTj3lLFmS8kBx8OOHsIfhYKJBKnAPwlbkI3/byZRBty8tfKBrwsUAbSro3GB7rFeSthft37Q=="],
"ai": ["ai@5.0.0-beta.34", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.19", "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-AFJ4p35AxA+1KFtnoouePLaAUpoj0IxIAoq/xgIv88qzYajTg4Sac5KaV4CDHFRLoF0L2cwhlFXt/Ss/zyBKkA=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
@@ -1271,6 +1276,8 @@
"miniflare": ["miniflare@4.20250730.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250730.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-avGXBStHQSqcJr8ra1mJ3/OQvnLZ49B1uAILQapAha1DHNZZvXWLIgUVre/WGY6ZOlNGFPh5CJ+dXLm4yuV3Jw=="],
"minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"minimisted": ["minimisted@2.0.1", "", { "dependencies": { "minimist": "^1.2.5" } }, "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA=="],
-4
View File
@@ -28,9 +28,5 @@
"type": "local",
"command": ["opencode", "x", "@h1deya/mcp-server-weather"]
}
},
"permission": {
"edit": "ask",
"bash": "ask"
}
}
+1 -1
View File
@@ -18,7 +18,7 @@
"catalog": {
"@types/node": "22.13.9",
"@tsconfig/node22": "22.0.2",
"ai": "5.0.0-beta.33",
"ai": "5.0.0-beta.34",
"hono": "4.7.10",
"typescript": "5.8.2",
"zod": "3.25.49",
+2 -1
View File
@@ -46,11 +46,12 @@
"hono-openapi": "0.4.8",
"isomorphic-git": "1.32.1",
"jsonc-parser": "3.3.1",
"minimatch": "10.0.3",
"open": "10.1.2",
"remeda": "catalog:",
"turndown": "7.2.0",
"tree-sitter": "0.22.4",
"tree-sitter-bash": "0.23.3",
"turndown": "7.2.0",
"vscode-jsonrpc": "8.2.1",
"xdg-basedir": "5.1.0",
"yargs": "18.0.0",
+1
View File
@@ -104,6 +104,7 @@ if (!snapshot) {
.filter((x: string) => {
const lower = x.toLowerCase()
return (
!lower.includes("release:") &&
!lower.includes("ignore:") &&
!lower.includes("chore:") &&
!lower.includes("ci:") &&
+11
View File
@@ -278,6 +278,17 @@ export namespace Config {
.optional()
.describe("Custom provider configurations and model overrides"),
mcp: z.record(z.string(), Mcp).optional().describe("MCP (Model Context Protocol) server configurations"),
formatter: z
.record(
z.string(),
z.object({
disabled: z.boolean().optional(),
command: z.array(z.string()).optional(),
environment: z.record(z.string(), z.string()).optional(),
extensions: z.array(z.string()).optional(),
}),
)
.optional(),
instructions: z.array(z.string()).optional().describe("Additional instruction files or patterns to include"),
layout: Layout.optional().describe("@deprecated Always uses stretch layout."),
permission: z
+3 -1
View File
@@ -129,7 +129,9 @@ export const clang: Info = {
command: ["clang-format", "-i", "$FILE"],
extensions: [".c", ".cc", ".cpp", ".cxx", ".c++", ".h", ".hh", ".hpp", ".hxx", ".h++", ".ino", ".C", ".H"],
async enabled() {
return Bun.which("clang-format") !== null
const app = App.info()
const items = await Filesystem.findUp(".clang-format", app.path.cwd, app.path.root)
return items.length > 0
},
}
+25 -3
View File
@@ -5,20 +5,40 @@ import { Log } from "../util/log"
import path from "path"
import * as Formatter from "./formatter"
import { Config } from "../config/config"
import { mergeDeep } from "remeda"
export namespace Format {
const log = Log.create({ service: "format" })
const state = App.state("format", () => {
const state = App.state("format", async () => {
const enabled: Record<string, boolean> = {}
const cfg = await Config.get()
const formatters = { ...Formatter } as Record<string, Formatter.Info>
for (const [name, item] of Object.entries(cfg.formatter ?? {})) {
if (item.disabled) {
delete formatters[name]
continue
}
const result: Formatter.Info = mergeDeep(formatters[name] ?? {}, {
command: [],
extensions: [],
...item,
})
result.enabled = async () => true
result.name = name
formatters[name] = result
}
return {
enabled,
formatters,
}
})
async function isEnabled(item: Formatter.Info) {
const s = state()
const s = await state()
let status = s.enabled[item.name]
if (status === undefined) {
status = await item.enabled()
@@ -28,8 +48,10 @@ export namespace Format {
}
async function getFormatter(ext: string) {
const formatters = await state().then((x) => x.formatters)
const result = []
for (const item of Object.values(Formatter)) {
for (const item of Object.values(formatters)) {
log.info("checking", { name: item.name, ext })
if (!item.extensions.includes(ext)) continue
if (!(await isEnabled(item))) continue
result.push(item)
+2 -1
View File
@@ -7,6 +7,7 @@ import { Config } from "../config/config"
import { Filesystem } from "../util/filesystem"
import path from "path"
import { lazy } from "../util/lazy"
import { minimatch } from "minimatch"
const MAX_OUTPUT_LENGTH = 30000
const DEFAULT_TIMEOUT = 1 * 60 * 1000
@@ -84,7 +85,7 @@ export const BashTool = Tool.define("bash", {
if (!needsAsk && command[0] !== "cd") {
const ask = (() => {
for (const [pattern, value] of Object.entries(permissions)) {
if (new Bun.Glob(pattern).match(node.text)) {
if (minimatch(node.text, pattern)) {
return value
}
}
+1
View File
@@ -96,6 +96,7 @@ export const EditTool = Tool.define("edit", {
file: filePath,
})
contentNew = await file.text()
diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew))
})()
FileTime.read(ctx.sessionID, filePath)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "0.0.0-202507312003",
"version": "0.0.0",
"type": "module",
"exports": {
".": "./dist/index.js"
+21
View File
@@ -17,6 +17,8 @@ export type Event = ({
} & EventStorageWrite) | ({
type: 'permission.updated';
} & EventPermissionUpdated) | ({
type: 'permission.replied';
} & EventPermissionReplied) | ({
type: 'file.edited';
} & EventFileEdited) | ({
type: 'session.updated';
@@ -374,6 +376,15 @@ export type Permission = {
};
};
export type EventPermissionReplied = {
type: string;
properties: {
sessionID: string;
permissionID: string;
response: string;
};
};
export type EventFileEdited = {
type: string;
properties: {
@@ -579,6 +590,16 @@ export type Config = {
type: 'remote';
} & McpRemoteConfig);
};
formatter?: {
[key: string]: {
disabled?: boolean;
command?: Array<string>;
environment?: {
[key: string]: string;
};
extensions?: Array<string>;
};
};
/**
* Additional instruction files or patterns to include
*/
+5 -8
View File
@@ -69,12 +69,7 @@ export default defineConfig({
{
label: "Usage",
items: [
"docs/cli",
"docs/ide",
"docs/share",
"docs/github",
]
items: ["docs/cli", "docs/ide", "docs/share", "docs/github"],
},
{
@@ -86,9 +81,11 @@ export default defineConfig({
"docs/models",
"docs/themes",
"docs/keybinds",
"docs/permissions",
"docs/mcp-servers",
]
}
"docs/formatters",
],
},
],
components: {
Hero: "./src/components/Hero.astro",
@@ -230,6 +230,12 @@ You can also define agents using markdown files in `~/.config/opencode/agent/` o
You can disable providers that are loaded automatically through the `disabled_providers` option. This is useful when you want to prevent certain providers from being loaded even if their credentials are available.
---
### Formatters
You can configure code formatters through the `formatter` option. See [Formatters documentation](/docs/formatters) for more details.
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
@@ -0,0 +1,87 @@
---
title: Formatters
description: Code formatting with opencode
---
opencode automatically formats code files after they are written or edited using language-specific formatters. This ensures consistent code style across your project without manual intervention.
## Built-in Formatters
opencode comes with several built-in formatters for popular languages and frameworks:
| Formatter | Languages/Extensions | Requirements |
| -------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| gofmt | .go | `gofmt` command available |
| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available |
| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in package.json |
| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json` config file |
| zig | .zig, .zon | `zig` command available |
| clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file |
| ktlint | .kt, .kts | `ktlint` command available |
| ruff | .py, .pyi | `ruff` command available with config |
| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available |
| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available |
| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available |
Formatters are automatically enabled when their requirements are met in your project environment.
## Configuration
You can customize formatters through the `formatter` section in your `opencode.json` configuration file.
### Disabling Formatters
To disable a specific formatter, set its `disabled` property to `true`:
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"disabled": true
}
}
}
```
### Custom Formatters
You can override the default formatters or add new ones by specifying the command, environment variables, and file extensions:
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"custom-prettier": {
"command": ["npx", "prettier", "--write", "$FILE"],
"environment": {
"NODE_ENV": "development"
},
"extensions": [".js", ".ts", ".jsx", ".tsx"]
}
}
}
```
The `$FILE` placeholder in the command will be replaced with the path to the file being formatted.
### Configuration Options
Each formatter configuration supports these properties:
| Property | Type | Description |
| ------------- | -------- | ------------------------------------------------------- |
| `disabled` | boolean | Set to `true` to disable the formatter |
| `command` | string[] | The command to run for formatting |
| `environment` | object | Environment variables to set when running the formatter |
| `extensions` | string[] | File extensions this formatter should handle |
## How It Works
When opencode writes or edits a file, it:
1. Checks the file extension against all enabled formatters
2. Runs the appropriate formatter command on the file
3. Applies the formatting changes automatically
This process happens seamlessly in the background, ensuring your code maintains consistent formatting without requiring manual steps.
+16 -31
View File
@@ -18,6 +18,7 @@ Let's get started.
To use opencode, you'll need:
1. A modern terminal emulator like:
- [WezTerm](https://wezterm.org), cross-platform
- [Alacritty](https://alacritty.org), cross-platform
- [Ghostty](https://ghostty.org), Linux and macOS
@@ -40,26 +41,10 @@ You can also install it with the following:
- **Using Node.js**
<Tabs>
<TabItem label="npm">
```bash
npm install -g opencode-ai
```
</TabItem>
<TabItem label="Bun">
```bash
bun install -g opencode-ai
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm install -g opencode-ai
```
</TabItem>
<TabItem label="Yarn">
```bash
yarn global add opencode-ai
```
</TabItem>
<TabItem label="npm">```bash npm install -g opencode-ai ```</TabItem>
<TabItem label="Bun">```bash bun install -g opencode-ai ```</TabItem>
<TabItem label="pnpm">```bash pnpm install -g opencode-ai ```</TabItem>
<TabItem label="Yarn">```bash yarn global add opencode-ai ```</TabItem>
</Tabs>
- **Using Homebrew on macOS**
@@ -138,7 +123,7 @@ You should commit your project's `AGENTS.md` file to Git.
:::
This helps opencode understand the project structure and the coding patterns
used.
used.
---
@@ -178,22 +163,22 @@ You can ask opencode to add new features to your project. Though we first recomm
instead suggest _how_ it'll implement the feature.
Switch to it using the **Tab** key. You'll see an indicator for this in the lower right corner.
```bash frame="none" title="Switch to Plan mode"
<TAB>
```
Now let's describe what we want it to do.
```txt frame="none"
When a user deletes a note, we'd like to flag it as deleted in the database.
Then create a screen that shows all the recently deleted notes.
From this screen, the user can undelete a note or permanently delete it.
```
You want to give opencode enough details to understand what you want. It helps
to talk to it like you are talking to a junior developer on your team.
:::tip
Give opencode plenty of context and examples to help it understand what you
want.
@@ -202,7 +187,7 @@ You can ask opencode to add new features to your project. Though we first recomm
2. **Iterate on the plan**
Once it gives you a plan, you can give it feedback or add more details.
```txt frame="none"
We'd like to design this new screen using a design I've used before.
[Image #1] Take a look at this image and use it as a reference.
@@ -216,16 +201,16 @@ You can ask opencode to add new features to your project. Though we first recomm
do this by dragging and dropping an image into the terminal.
3. **Build the feature**
Once you feel comfortable with the plan, switch back to _Build mode_ by
hitting the **Tab** key again.
```bash frame="none"
<TAB>
```
And asking it to make the changes.
```bash frame="none"
Sounds good! Go ahead and make the changes.
```
@@ -271,4 +256,4 @@ Here's an [example conversation](https://opencode.ai/s/4XP1fce5) with opencode.
And that's it! You are now a pro at using opencode.
To make it your own, we recommend [picking a theme](/docs/themes), [customizing the keybinds](/docs/keybinds), or playing around with the [opencode config](/docs/config).
To make it your own, we recommend [picking a theme](/docs/themes), [customizing the keybinds](/docs/keybinds), [configuring code formatters](/docs/formatters), or playing around with the [opencode config](/docs/config).
@@ -46,6 +46,8 @@ This mode is useful when you want the AI to analyze code, suggest changes, or cr
You can switch between modes during a session using the _Tab_ key. Or your configured `switch_mode` keybind.
See also: [Formatters](/docs/formatters) for information about code formatting configuration.
---
## Configure
@@ -99,6 +101,7 @@ tools:
---
You are in code review mode. Focus on:
- Code quality and best practices
- Potential bugs and edge cases
- Performance implications
@@ -281,6 +284,7 @@ tools:
You are in debug mode. Your primary goal is to help investigate and diagnose issues.
Focus on:
- Understanding the problem through careful analysis
- Using bash commands to inspect system state
- Reading relevant files and logs
@@ -304,6 +308,7 @@ tools:
You are in refactoring mode. Focus on improving code quality without changing functionality.
Priorities:
- Improve code readability and maintainability
- Apply consistent naming conventions
- Reduce code duplication
@@ -1,144 +1,74 @@
---
title: Permissions
description: Control what AI agents can do in your codebase.
description: Control what agents can do in your codebase.
---
The opencode permissions system provides granular control over what actions AI agents can perform in your codebase. It allows you to configure explicit approval requirements for sensitive operations like file editing, bash commands, and more.
By default, opencode **allows all operations** without requiring explicit approval.
## How it works
The permissions system provides granular control to restrict what actions AI agents can perform in your codebase, allowing you to configure explicit approval requirements for sensitive operations like file editing, bash commands, and more.
The permissions system works by intercepting tool calls and checking if user approval is required before executing potentially sensitive operations. When a tool requests permission, it creates a permission request that must be approved by the user.
---
```typescript
// Example of how a tool requests permission
await Permission.ask({
type: "edit",
sessionID: ctx.sessionID,
messageID: ctx.messageID,
callID: ctx.callID,
title: "Edit this file: " + filePath,
metadata: {
filePath,
diff,
},
})
```
## Configure
When a permission is requested, the system checks the configuration to determine if approval is needed. If approval is required, the user is prompted to allow or deny the action.
Permissions are configured in your `opencode.json` file under the `permission` key. Here are the available options.
## Configuration
---
Permissions are configured in your `opencode.json` file under the `permission` key. Here are the available options:
### edit
### permission.edit
Use the `permission.edit` key to control whether file editing operations require user approval.
Controls whether file editing operations require user approval.
- `"ask"` - Prompt for approval before editing files
- `"allow"` - Allow all file editing operations without approval
```json title="opencode.json"
```json title="opencode.json" {4}
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask"
}
}
```
- `"ask"` - Prompt user for approval before editing files
- `"allow"` - Allow all file editing operations without approval
---
### permission.bash
### bash
Controls whether bash commands require user approval. This can be configured globally or with specific patterns.
Controls whether bash commands require user approval.
```json title="opencode.json"
{
"permission": {
"bash": "ask"
}
}
```
:::tip
You can specify which commands you want to have run without approval.
:::
Or with specific patterns:
This can be configured globally or with specific patterns. Setting this to `"ask"` is the strictest mode, requiring approval for all bash commands.
```json title="opencode.json"
{
"permission": {
"bash": {
"git *": "allow",
"npm install": "ask",
"*": "ask"
For example.
- **Ask for approval for all commands**
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": "ask"
}
}
}
```
```
## Configuration examples
- **Approve specific commands**
### Basic permission configuration
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": "ask"
}
}
```
### Advanced bash permission configuration
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": {
"git status": "allow",
"git diff": "allow",
"git add *": "ask",
"git commit*": "ask",
"npm install": "ask",
"npm run build": "allow",
"ls": "allow",
"pwd": "allow",
"*": "ask"
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": {
"git status": "allow",
"git diff": "allow",
"npm run build": "allow",
"ls": "allow",
"pwd": "allow"
}
}
}
}
```
### Permissive configuration (development only)
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "allow",
"bash": "allow"
}
}
```
### Strict configuration
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": {
"*": "ask"
}
}
}
```
## Best practices
1. **Start with "ask"**: Begin with asking for permissions and adjust based on your workflow
2. **Use patterns wisely**: Create specific patterns for commands you trust
3. **Review regularly**: Periodically review your permission settings
4. **Be specific**: Use specific patterns rather than broad wildcards when possible
5. **Document exceptions**: Comment your configuration to explain why certain permissions are set
This permissions system ensures that you maintain control over what AI agents can do in your codebase while providing flexibility for trusted operations.
```
+302 -284
View File
@@ -45,6 +45,126 @@ You can customize the base URL for any provider by setting the `baseURL` option.
---
## Custom provider
To add any **OpenAI-compatible** provider that's not listed in `opencode auth login`:
:::tip
You can use any OpenAI-compatible provider with opencode. Most modern AI providers offer OpenAI-compatible APIs.
:::
1. Run `opencode auth login` and scroll down to **Other**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ...
│ ● Other
```
2. Enter a unique ID for the provider.
```bash
$ opencode auth login
┌ Add credential
◇ Enter provider id
│ myprovider
```
:::note
Choose a memorable ID, you'll use this in your config file.
:::
3. Enter your API key for the provider.
```bash
$ opencode auth login
┌ Add credential
▲ This only stores a credential for myprovider - you will need configure it in opencode.json, check the docs for examples.
◇ Enter your API key
│ sk-...
```
4. Create or update your `opencode.json` file in your project directory:
```json title="opencode.json" ""myprovider"" {5-15}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1"
},
"models": {
"my-model-name": {
"name": "My Model Display Name"
}
}
}
}
}
```
Here are the configuration options:
- **npm**: AI SDK package to use, `@ai-sdk/openai-compatible` for OpenAI-compatible providers
- **name**: Display name in UI.
- **models**: Available models.
- **options.baseURL**: API endpoint URL.
- **options.apiKey**: Optionally set the API key, if not using auth.
- **options.headers**: Optionally set custom headers.
More on the advanced options in the example below.
5. Run the `/models` command and your custom provider and models will appear in the selection list.
---
##### Example
Here's an example setting the `apiKey` and `headers` options.
```json title="opencode.json" {9,11}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1",
"apiKey": "{env:ANTHROPIC_API_KEY}",
"headers": {
"Authorization": "Bearer custom-token"
}
},
"models": {
"my-model-name": {
"name": "My Model Display Name"
}
}
}
}
}
```
We are setting the `apiKey` using the `env` variable syntax, [learn more](/docs/config#env-vars).
---
## Directory
Let's look at some of the providers in detail. If you'd like to add a provider to the
@@ -117,8 +237,30 @@ $ opencode auth login
```
This will ask you login with your Anthropic account in your browser. Now all the
the Anthropic models should be available when you use the `/models` command.
Here you can select the **Claude Pro/Max** option and it'll open your browser
and ask you to authenticate.
```bash
$ opencode auth login
┌ Add credential
◇ Select provider
│ Anthropic
◆ Login method
│ ● Claude Pro/Max
│ ○ Create API Key
│ ○ Manually enter API Key
```
Now all the the Anthropic models should be available when you use the `/models` command.
##### Using API keys
You can also select **Create API Key** if you don't have a Pro/Max subscription. It'll also open your browser and ask you to login to Anthropic and give you a code you can paste in your terminal.
Or if you already have an API key, you can select **Manually enter API Key** and paste it in your terminal.
---
@@ -185,6 +327,78 @@ the Anthropic models should be available when you use the `/models` command.
---
### DeepSeek
1. Head over to the [DeepSeek console](https://platform.deepseek.com/), create an account, and click **Create new API key**.
2. Run `opencode auth login` and select **DeepSeek**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ● DeepSeek
│ ...
```
3. Enter your DeepSeek API key.
```bash
$ opencode auth login
┌ Add credential
◇ Select provider
│ DeepSeek
◇ Enter your API key
│ _
```
4. Run the `/models` command to select a DeepSeek model like _DeepSeek Reasoner_.
---
### Fireworks AI
1. Head over to the [Fireworks AI console](https://app.fireworks.ai/), create an account, and click **Create API Key**.
2. Run `opencode auth login` and select **Fireworks AI**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ● Fireworks AI
│ ...
```
3. Enter your Fireworks AI API key.
```bash
$ opencode auth login
┌ Add credential
◇ Select provider
│ Fireworks AI
◇ Enter your API key
│ _
```
4. Run the `/models` command to select a model like _Kimi K2 Instruct_.
---
### GitHub Copilot
To use your GitHub Copilot subscription with opencode:
@@ -290,6 +504,78 @@ In this example:
---
### Moonshot AI
To use Kimi K2 from Moonshot AI:
1. Head over to the [Moonshot AI console](https://platform.moonshot.ai/console), create an account, and click **Create API key**.
2. Run `opencode auth login` and select **Other**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ...
│ ● Other
```
3. Enter `moonshot` as the provider ID.
```bash
$ opencode auth login
┌ Add credential
◇ Select provider
│ Other
◇ Enter provider id
│ moonshot
```
4. Enter your Moonshot API key.
```bash
$ opencode auth login
┌ Add credential
◇ Enter your API key
│ sk-...
```
5. Configure Moonshot in your opencode config.
```json title="opencode.json" "\"moonshot\"" {5-15}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"moonshot": {
"npm": "@ai-sdk/openai-compatible",
"name": "Moonshot AI",
"options": {
"baseURL": "https://api.moonshot.ai/v1"
},
"models": {
"kimi-k2-0711-preview": {
"name": "Kimi K2"
}
}
}
}
}
```
6. Run the `/models` command to select _Kimi K2_.
---
### Ollama
You can configure opencode to use local models through Ollama.
@@ -435,8 +721,12 @@ https://platform.openai.com/api-keys
}
```
{ /*
---
TODO: Test a model that actually works, currently getting errors form the API
for Qwen non-thinking models.
### Cerebras
Cerebras offers fast inference with generous free tiers and competitive pricing.
@@ -510,15 +800,15 @@ Cerebras offers fast inference with generous free tiers and competitive pricing.
6. Run the `/models` command to select a Cerebras model.
*/ }
---
### DeepSeek
### Together AI
DeepSeek offers powerful reasoning models at competitive prices.
1. Head over to the [Together AI console](https://api.together.ai), create an account, and click **Add Key**.
1. Head over to the [DeepSeek console](https://platform.deepseek.com/), create an account, and generate an API key.
2. Run `opencode auth login` and select **Other**.
2. Run `opencode auth login` and select **Together AI**.
```bash
$ opencode auth login
@@ -526,12 +816,12 @@ DeepSeek offers powerful reasoning models at competitive prices.
┌ Add credential
◆ Select provider
│ ● Together AI
│ ...
│ ● Other
```
3. Enter `deepseek` as the provider ID.
3. Enter your Together AI API key.
```bash
$ opencode auth login
@@ -539,286 +829,14 @@ DeepSeek offers powerful reasoning models at competitive prices.
┌ Add credential
◇ Select provider
Other
◇ Enter provider id
│ deepseek
```
4. Enter your DeepSeek API key.
```bash
$ opencode auth login
┌ Add credential
Together AI
◇ Enter your API key
sk-...
_
```
5. Configure DeepSeek in your opencode config.
```json title="opencode.json" "deepseek" {5-17}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"deepseek": {
"npm": "@ai-sdk/openai-compatible",
"name": "DeepSeek",
"options": {
"baseURL": "https://api.deepseek.com/v1"
},
"models": {
"deepseek-reasoner": {
"name": "DeepSeek Reasoner"
}
}
}
}
}
```
6. Run the `/models` command to select a DeepSeek model.
---
### Moonshot AI (Kimi)
Moonshot AI offers the Kimi models with long context capabilities.
1. Head over to the [Moonshot AI console](https://platform.moonshot.cn/), create an account, and generate an API key.
2. Run `opencode auth login` and select **Other**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ...
│ ● Other
```
3. Enter `moonshot` as the provider ID.
```bash
$ opencode auth login
┌ Add credential
◇ Select provider
│ Other
◇ Enter provider id
│ moonshot
```
4. Enter your Moonshot API key.
```bash
$ opencode auth login
┌ Add credential
◇ Enter your API key
│ sk-...
```
5. Configure Moonshot in your opencode config.
```json title="opencode.json" "moonshot" {5-17}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"moonshot": {
"npm": "@ai-sdk/openai-compatible",
"name": "Moonshot AI",
"options": {
"baseURL": "https://api.moonshot.cn/v1"
},
"models": {
"kimi-k2-0711-preview": {
"name": "Kimi K2"
}
}
}
}
}
```
6. Run the `/models` command to select a Kimi model.
---
### Custom
To add any **OpenAI-compatible** provider that's not listed in `opencode auth login`:
:::tip
You can use any OpenAI-compatible provider with opencode. Most modern AI providers offer OpenAI-compatible APIs.
:::
1. Run `opencode auth login` and scroll down to **Other**.
```bash
$ opencode auth login
┌ Add credential
◆ Select provider
│ ...
│ ● Other
```
2. Enter a unique ID for the provider.
```bash
$ opencode auth login
┌ Add credential
◇ Enter provider id
│ myprovider
```
:::note
Choose a memorable ID, you'll use this in your config file.
:::
3. Enter your API key for the provider.
```bash
$ opencode auth login
┌ Add credential
▲ This only stores a credential for myprovider - you will need configure it in opencode.json, check the docs for examples.
◇ Enter your API key
│ sk-...
```
4. Create or update your `opencode.json` file in your project directory:
```json title="opencode.json" ""myprovider"" {5-15}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1"
},
"models": {
"my-model-name": {
"name": "My Model Display Name"
}
}
}
}
}
```
Here are the configuration options:
- **npm**: AI SDK package to use, `@ai-sdk/openai-compatible` for OpenAI-compatible providers
- **name**: Display name in UI.
- **models**: Available models.
- **options.baseURL**: API endpoint URL.
- **options.apiKey**: Optionally set the API key, if not using auth.
- **options.headers**: Optionally set custom headers.
More on the advanced options in the example below.
5. Run the `/models` command and your custom provider and models will appear in the selection list.
---
##### Example
Here's an example setting the `apiKey` and `headers` options.
```json title="opencode.json" {9,11}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1",
"apiKey": "{env:ANTHROPIC_API_KEY}",
"headers": {
"Authorization": "Bearer custom-token"
}
},
"models": {
"my-model-name": {
"name": "My Model Display Name"
}
}
}
}
}
```
We are setting the `apiKey` using the `env` variable syntax, [learn more](/docs/config#env-vars).
#### Common Examples
**Together AI:**
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"together": {
"npm": "@ai-sdk/openai-compatible",
"name": "Together AI",
"options": {
"baseURL": "https://api.together.xyz/v1"
},
"models": {
"meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo": {
"name": "Llama 3.2 11B Vision"
}
}
}
}
}
```
**Fireworks AI:**
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"fireworks": {
"npm": "@ai-sdk/openai-compatible",
"name": "Fireworks AI",
"options": {
"baseURL": "https://api.fireworks.ai/inference/v1"
},
"models": {
"accounts/fireworks/models/llama-v3p1-70b-instruct": {
"name": "Llama 3.1 70B"
}
}
}
}
}
```
4. Run the `/models` command to select a model like _Kimi K2 Instruct_.
---
+1 -1
View File
@@ -16,7 +16,7 @@ await import(`../packages/sdk/js/script/publish.ts`)
// await import(`../packages/sdk/stainless/generate.ts`)
if (!snapshot) {
await $`git commit -am "Release v${version}"`
await $`git commit -am "release: v${version}"`
await $`git tag v${version}`
await $`git push origin HEAD --tags`
}
+1 -1
View File
@@ -57,7 +57,7 @@ export function activate(context: vscode.ExtensionContext) {
})
terminal.show()
terminal.sendText(`OPENCODE_THEME=system OPENCODE_CALLER=vscode opencode --port ${port}`)
terminal.sendText(`OPENCODE_CALLER=vscode opencode --port ${port}`)
const fileRef = getActiveFile()
if (!fileRef) return