summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/deploy.yml2
-rw-r--r--.github/workflows/format.yml2
-rw-r--r--.github/workflows/publish-vscode.yml2
-rw-r--r--.github/workflows/publish.yml6
-rw-r--r--.github/workflows/snapshot.yml6
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--.github/workflows/typecheck.yml2
-rw-r--r--.opencode/command/spellcheck.md5
-rw-r--r--STATS.md2
-rw-r--r--bun.lock108
-rw-r--r--package.json8
-rw-r--r--packages/console/app/package.json3
-rw-r--r--packages/console/core/package.json9
-rw-r--r--packages/console/core/src/context.ts2
-rw-r--r--packages/console/function/package.json7
-rw-r--r--packages/console/mail/package.json11
-rw-r--r--packages/console/resource/package.json3
-rw-r--r--packages/console/scripts/package.json2
-rw-r--r--packages/desktop/package.json4
-rw-r--r--packages/desktop/tsconfig.json13
-rw-r--r--packages/function/package.json2
-rw-r--r--packages/opencode/package.json4
-rwxr-xr-xpackages/opencode/script/build.ts7
-rw-r--r--packages/opencode/src/cli/cmd/export.ts3
-rw-r--r--packages/opencode/src/cli/error.ts2
-rw-r--r--packages/opencode/src/config/config.ts58
-rw-r--r--packages/opencode/src/file/ignore.ts69
-rw-r--r--packages/opencode/src/file/watcher.ts61
-rw-r--r--packages/opencode/src/flag/flag.ts1
-rw-r--r--packages/opencode/src/lsp/client.ts4
-rw-r--r--packages/opencode/src/lsp/language.ts3
-rw-r--r--packages/opencode/src/lsp/server.ts50
-rw-r--r--packages/opencode/src/project/bootstrap.ts2
-rw-r--r--packages/opencode/src/provider/models.ts6
-rw-r--r--packages/opencode/src/provider/provider.ts5
-rw-r--r--packages/opencode/src/session/message-v2.ts174
-rw-r--r--packages/opencode/src/session/prompt.ts5
-rw-r--r--packages/opencode/src/tool/read.ts45
-rw-r--r--packages/opencode/src/tool/read.txt4
-rw-r--r--packages/opencode/src/tool/tool.ts3
-rw-r--r--packages/opencode/src/util/context.ts2
-rw-r--r--packages/opencode/test/file/ignore.test.ts10
-rw-r--r--packages/opencode/test/snapshot/snapshot.test.ts2
-rw-r--r--packages/opencode/tsconfig.json1
-rw-r--r--packages/plugin/package.json3
-rw-r--r--packages/sdk/js/package.json13
-rw-r--r--packages/sdk/js/src/gen/types.gen.ts9
-rw-r--r--packages/web/astro.config.mjs1
-rw-r--r--packages/web/package.json4
-rw-r--r--packages/web/src/content/docs/agents.mdx42
-rw-r--r--packages/web/src/content/docs/config.mdx18
-rw-r--r--packages/web/src/content/docs/custom-tools.mdx2
-rw-r--r--packages/web/src/content/docs/mcp-servers.mdx91
-rw-r--r--packages/web/src/content/docs/plugins.mdx2
-rw-r--r--packages/web/src/content/docs/tools.mdx316
-rw-r--r--packages/web/src/content/docs/zen.mdx1
-rwxr-xr-xscript/publish.ts4
-rw-r--r--sdks/vscode/package.json2
58 files changed, 910 insertions, 320 deletions
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 90d440641..d94f7f48d 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -17,7 +17,7 @@ jobs:
- uses: oven-sh/setup-bun@v1
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- run: bun install
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index 11ecc93fd..2927dd1b1 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -22,7 +22,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- name: run
run: |
diff --git a/.github/workflows/publish-vscode.yml b/.github/workflows/publish-vscode.yml
index f40c93798..bff8c192b 100644
--- a/.github/workflows/publish-vscode.yml
+++ b/.github/workflows/publish-vscode.yml
@@ -21,7 +21,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- run: git fetch --force --tags
- run: bun install -g @vscode/vsce
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 310ce162c..c10957a1b 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -37,16 +37,16 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v3
with:
path: ~/.bun
- key: ${{ runner.os }}-bun-1-2-21-${{ hashFiles('bun.lock') }}
+ key: ${{ runner.os }}-bun-1-3-0-${{ hashFiles('bun.lock') }}
restore-keys: |
- ${{ runner.os }}-bun-1-2-21-
+ ${{ runner.os }}-bun-1-3-0-
- name: Install makepkg
run: |
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 0cc6faf7f..ddb095354 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -26,16 +26,16 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v3
with:
path: ~/.bun
- key: ${{ runner.os }}-bun-1-2-21-${{ hashFiles('bun.lock') }}
+ key: ${{ runner.os }}-bun-1-3-0-${{ hashFiles('bun.lock') }}
restore-keys: |
- ${{ runner.os }}-bun-1-2-21-
+ ${{ runner.os }}-bun-1-3-0-
- name: Install dependencies
run: bun install
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 73516f2ec..441f9585f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -20,7 +20,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- name: run
run: |
diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml
index e80896566..3854644b6 100644
--- a/.github/workflows/typecheck.yml
+++ b/.github/workflows/typecheck.yml
@@ -15,7 +15,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
- bun-version: 1.2.21
+ bun-version: 1.3.0
- name: Install dependencies
run: bun install
diff --git a/.opencode/command/spellcheck.md b/.opencode/command/spellcheck.md
new file mode 100644
index 000000000..afa1970b7
--- /dev/null
+++ b/.opencode/command/spellcheck.md
@@ -0,0 +1,5 @@
+---
+description: Spellcheck all markdown file changes
+---
+
+Look at all the unstaged changes to markdown (.md, .mdx) files, pull out the lines that have changed, and check for spelling and grammar errors.
diff --git a/STATS.md b/STATS.md
index b1c90d402..a2184ef22 100644
--- a/STATS.md
+++ b/STATS.md
@@ -103,3 +103,5 @@
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
| 2025-10-08 | 474,643 (+7,307) | 394,139 (+8,701) | 868,782 (+16,008) |
+| 2025-10-09 | 479,203 (+4,560) | 400,526 (+6,387) | 879,729 (+10,947) |
+| 2025-10-10 | 484,374 (+5,171) | 406,015 (+5,489) | 890,389 (+10,660) |
diff --git a/bun.lock b/bun.lock
index db0a6d423..a82c40452 100644
--- a/bun.lock
+++ b/bun.lock
@@ -4,6 +4,7 @@
"": {
"name": "opencode",
"devDependencies": {
+ "@tsconfig/bun": "1.0.9",
"husky": "9.1.7",
"prettier": "3.6.2",
"sst": "3.17.13",
@@ -21,14 +22,13 @@
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0",
- "solid-js": "catalog:",
"vinxi": "^0.5.7",
"zod": "catalog:",
},
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -40,27 +40,35 @@
"postgres": "3.4.7",
"stripe": "18.0.0",
"ulid": "3.0.0",
+ "zod": "catalog:",
},
"devDependencies": {
+ "@cloudflare/workers-types": "4.20251008.0",
+ "@tsconfig/node22": "22.0.2",
+ "@types/bun": "1.3.0",
+ "@types/node": "24.7.1",
"drizzle-kit": "0.30.5",
"mysql2": "3.14.4",
},
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
"@ai-sdk/openai-compatible": "1.0.1",
"@hono/zod-validator": "catalog:",
"@openauthjs/openauth": "0.0.0-20250322224806",
+ "@opencode-ai/console-core": "workspace:*",
+ "@opencode-ai/console-resource": "workspace:*",
"ai": "catalog:",
"hono": "catalog:",
"zod": "catalog:",
},
"devDependencies": {
- "@cloudflare/workers-types": "4.20250522.0",
+ "@cloudflare/workers-types": "4.20251008.0",
+ "@tsconfig/node22": "22.0.2",
"@types/node": "catalog:",
"openai": "5.11.0",
"typescript": "catalog:",
@@ -68,10 +76,11 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+ "@tsconfig/bun": "1.0.9",
"@types/react": "18.0.25",
"react": "18.2.0",
},
@@ -81,17 +90,20 @@
"dependencies": {
"@cloudflare/workers-types": "^4.20250830.0",
},
+ "devDependencies": {
+ "@tsconfig/node22": "22.0.2",
+ },
},
"packages/console/scripts": {
"name": "@opencode-ai/console-scripts",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@opencode-ai/console-core": "workspace:*",
},
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -108,13 +120,13 @@
"marked-shiki": "1.2.1",
"remeda": "catalog:",
"shiki": "3.9.2",
- "solid-js": "catalog:",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3",
},
"devDependencies": {
"@tailwindcss/vite": "4.1.11",
+ "@tsconfig/bun": "1.0.9",
"@types/luxon": "3.7.1",
"@types/node": "catalog:",
"typescript": "catalog:",
@@ -125,7 +137,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0",
@@ -140,7 +152,7 @@
},
"packages/opencode": {
"name": "opencode",
- "version": "0.14.6",
+ "version": "0.14.7",
"bin": {
"opencode": "./bin/opencode",
},
@@ -152,6 +164,7 @@
"@openauthjs/openauth": "0.4.3",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
+ "@parcel/watcher": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
@@ -181,6 +194,7 @@
"@ai-sdk/amazon-bedrock": "2.2.10",
"@ai-sdk/google-vertex": "3.0.16",
"@octokit/webhooks-types": "7.6.1",
+ "@parcel/watcher-win32-x64": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@tsconfig/bun": "1.0.7",
"@types/bun": "catalog:",
@@ -193,31 +207,30 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
},
"devDependencies": {
"@tsconfig/node22": "catalog:",
+ "@types/node": "24.7.1",
"typescript": "catalog:",
},
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
- "version": "0.14.6",
- "dependencies": {
- "@hey-api/openapi-ts": "0.81.0",
- },
+ "version": "0.14.7",
"devDependencies": {
- "@hey-api/openapi-ts": "0.80.1",
+ "@hey-api/openapi-ts": "0.81.0",
"@tsconfig/node22": "catalog:",
+ "@types/node": "24.7.1",
"typescript": "catalog:",
},
},
"packages/web": {
"name": "@opencode-ai/web",
- "version": "0.14.6",
+ "version": "0.14.7",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -239,7 +252,7 @@
"sharp": "0.32.5",
"shiki": "3.4.2",
"solid-js": "catalog:",
- "toolbeam-docs-theme": "0.4.7",
+ "toolbeam-docs-theme": "0.4.8",
},
"devDependencies": {
"@types/node": "catalog:",
@@ -255,11 +268,14 @@
"web-tree-sitter",
"tree-sitter-bash",
],
+ "overrides": {
+ "@types/bun": "catalog:",
+ },
"catalog": {
"@hono/zod-validator": "0.4.2",
"@kobalte/core": "0.13.11",
"@tsconfig/node22": "22.0.2",
- "@types/bun": "1.2.21",
+ "@types/bun": "1.3.0",
"@types/node": "22.13.9",
"ai": "5.0.8",
"fuzzysort": "3.1.0",
@@ -453,7 +469,7 @@
"@cloudflare/workerd-windows-64": ["@cloudflare/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-+m124IiM149QvvzAOrO766uTdILqXJZqzZjqTaMTaWXegjjsJwGSL6v9d71TSFntEwxeXnpJPBkVWyKZFjqrvg=="],
- "@cloudflare/workers-types": ["@cloudflare/[email protected]", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="],
+ "@cloudflare/workers-types": ["@cloudflare/[email protected]", "", {}, "sha512-dZLkO4PbCL0qcCSKzuW7KE4GYe49lI12LCfQ5y9XeSwgYBoAUbwH4gmJ6A0qUIURiTJTkGkRkhVPqpq2XNgYRA=="],
"@corvu/utils": ["@corvu/[email protected]", "", { "dependencies": { "@floating-ui/dom": "^1.6.11" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA=="],
@@ -553,7 +569,7 @@
"@hey-api/json-schema-ref-parser": ["@hey-api/[email protected]", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } }, "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w=="],
- "@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-AC478kg36vmmrseLZNFonZ/cmXXmDzW5yWz4PVg1S8ebJsRtVRJ/QU+mtnXfzf9avN2P0pz/AO4WAe4jyFY2gA=="],
+ "@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-PoJukNBkUfHOoMDpN33bBETX49TUhy7Hu8Sa0jslOvFndvZ5VjQr4Nl/Dzjb9LG1Lp5HjybyTJMA6a1zYk/q6A=="],
"@hono/standard-validator": ["@hono/[email protected]", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
@@ -1141,7 +1157,7 @@
"@thisbeyond/solid-dnd": ["@thisbeyond/[email protected]", "", { "peerDependencies": { "solid-js": "^1.5" } }, "sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw=="],
- "@tsconfig/bun": ["@tsconfig/[email protected]", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
+ "@tsconfig/bun": ["@tsconfig/[email protected]", "", {}, "sha512-4M0/Ivfwcpz325z6CwSifOBZYji3DFOEpY6zEUt0+Xi2qRhzwvmqQN9XAHJh3OVvRJuAqVTLU2abdCplvp6mwQ=="],
"@tsconfig/node22": ["@tsconfig/[email protected]", "", {}, "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA=="],
@@ -1155,7 +1171,7 @@
"@types/braces": ["@types/[email protected]", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
- "@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
+ "@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
"@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
@@ -1183,7 +1199,7 @@
"@types/nlcst": ["@types/[email protected]", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
- "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+ "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="],
"@types/prop-types": ["@types/[email protected]", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
@@ -1369,7 +1385,7 @@
"buffer-from": ["[email protected]", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
- "bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
+ "bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
"bundle-name": ["[email protected]", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
@@ -2797,7 +2813,7 @@
"toidentifier": ["[email protected]", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
- "toolbeam-docs-theme": ["[email protected]", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-oVA/V4M4s4vtLljfnZrOSuCNomek5h9jIYkr92l4QgAQvB3ht+D7xAJIy27IGVJzYA5scUE1OK84ZZqeajoeWw=="],
+ "toolbeam-docs-theme": ["[email protected]", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-b+5ynEFp4Woe5a22hzNQm42lD23t13ZMihVxHbzjA50zdcM9aOSJTIjdJ0PDSd4/50HbBXcpHiQsz6rM4N88ww=="],
"tr46": ["[email protected]", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
@@ -2857,7 +2873,7 @@
"undici": ["[email protected]", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
- "undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+ "undici-types": ["[email protected]", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
"unenv": ["[email protected]", "", { "dependencies": { "consola": "^3.2.3", "defu": "^6.1.4", "mime": "^3.0.0", "node-fetch-native": "^1.6.4", "pathe": "^1.1.2" } }, "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ=="],
@@ -3125,14 +3141,20 @@
"@openauthjs/openauth/jose": ["[email protected]", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
- "@opencode-ai/console-resource/@cloudflare/workers-types": ["@cloudflare/[email protected]", "", {}, "sha512-MXseDjmqL1hIdQCqwHDMG8SE60W4FdwqLsofZjo/KtLH9zFcoQfZkCYyQrdfEJINiSoNJjrup7WR6KsqiFUSsg=="],
+ "@opencode-ai/console-function/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
+ "@opencode-ai/desktop/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
+ "@opencode-ai/function/@cloudflare/workers-types": ["@cloudflare/[email protected]", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="],
- "@opencode-ai/sdk/@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-PoJukNBkUfHOoMDpN33bBETX49TUhy7Hu8Sa0jslOvFndvZ5VjQr4Nl/Dzjb9LG1Lp5HjybyTJMA6a1zYk/q6A=="],
+ "@opencode-ai/function/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="],
"@opencode-ai/web/@types/luxon": ["@types/[email protected]", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
+ "@opencode-ai/web/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
"@opencode-ai/web/marked": ["[email protected]", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
"@opencode-ai/web/shiki": ["[email protected]", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/engine-javascript": "3.4.2", "@shikijs/engine-oniguruma": "3.4.2", "@shikijs/langs": "3.4.2", "@shikijs/themes": "3.4.2", "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ=="],
@@ -3169,6 +3191,10 @@
"@tanstack/router-utils/pathe": ["[email protected]", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+ "@types/fontkit/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
+ "@types/sax/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
"@vercel/nft/estree-walker": ["[email protected]", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vercel/nft/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
@@ -3205,6 +3231,8 @@
"boxen/chalk": ["[email protected]", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
+ "bun-types/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
"c12/confbox": ["[email protected]", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
"c12/ohash": ["[email protected]", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
@@ -3309,10 +3337,14 @@
"opencode/@openauthjs/openauth": ["@openauthjs/[email protected]", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw=="],
+ "opencode/@tsconfig/bun": ["@tsconfig/[email protected]", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
+
"opencode/ulid": ["[email protected]", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q=="],
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/[email protected]", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
+ "opencontrol/@tsconfig/bun": ["@tsconfig/[email protected]", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
+
"opencontrol/hono": ["[email protected]", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
"opencontrol/zod": ["[email protected]", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
@@ -3383,6 +3415,8 @@
"strip-literal/js-tokens": ["[email protected]", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
+ "stripe/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+
"sucrase/commander": ["[email protected]", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
"sucrase/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
@@ -3589,10 +3623,18 @@
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
+ "@opencode-ai/console-function/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
+ "@opencode-ai/desktop/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
+ "@opencode-ai/function/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
"@opencode-ai/web/@shikijs/transformers/@shikijs/core": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="],
"@opencode-ai/web/@shikijs/transformers/@shikijs/types": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="],
+ "@opencode-ai/web/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
"@opencode-ai/web/shiki/@shikijs/core": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="],
"@opencode-ai/web/shiki/@shikijs/engine-javascript": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ=="],
@@ -3619,6 +3661,10 @@
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+ "@types/fontkit/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
+ "@types/sax/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
"@vercel/nft/glob/jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"@vercel/nft/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
@@ -3637,6 +3683,8 @@
"bl/buffer/ieee754": ["[email protected]", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
+ "bun-types/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
"c12/pkg-types/pathe": ["[email protected]", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@@ -3757,6 +3805,8 @@
"string-width-cjs/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+ "stripe/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+
"sucrase/glob/jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"sucrase/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
diff --git a/package.json b/package.json
index e1116503a..abbc89009 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"name": "opencode",
"private": true,
"type": "module",
- "packageManager": "[email protected]",
+ "packageManager": "[email protected]",
"scripts": {
"dev": "bun run packages/opencode/src/index.ts",
"typecheck": "bun turbo typecheck",
@@ -16,7 +16,7 @@
"packages/sdk/js"
],
"catalog": {
- "@types/bun": "1.2.21",
+ "@types/bun": "1.3.0",
"@hono/zod-validator": "0.4.2",
"@kobalte/core": "0.13.11",
"@types/node": "22.13.9",
@@ -32,6 +32,7 @@
}
},
"devDependencies": {
+ "@tsconfig/bun": "1.0.9",
"husky": "9.1.7",
"prettier": "3.6.2",
"sst": "3.17.13",
@@ -56,5 +57,8 @@
],
"patchedDependencies": {
"@solidjs/[email protected]": "patches/@solidjs%[email protected]"
+ },
+ "overrides": {
+ "@types/bun": "catalog:"
}
}
diff --git a/packages/console/app/package.json b/packages/console/app/package.json
index 59eb56792..a8f264e53 100644
--- a/packages/console/app/package.json
+++ b/packages/console/app/package.json
@@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
- "version": "0.14.6"
+ "version": "0.14.7"
},
"dependencies": {
"@ibm/plex": "6.4.1",
@@ -18,7 +18,6 @@
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0",
- "solid-js": "catalog:",
"vinxi": "^0.5.7",
"zod": "catalog:"
},
diff --git a/packages/console/core/package.json b/packages/console/core/package.json
index 13c6991b2..a701dae37 100644
--- a/packages/console/core/package.json
+++ b/packages/console/core/package.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
- "version": "0.14.6",
+ "version": "0.14.7",
"private": true,
"type": "module",
"dependencies": {
@@ -14,7 +14,8 @@
"drizzle-orm": "0.41.0",
"postgres": "3.4.7",
"stripe": "18.0.0",
- "ulid": "3.0.0"
+ "ulid": "3.0.0",
+ "zod": "catalog:"
},
"exports": {
"./*": "./src/*"
@@ -29,6 +30,10 @@
"typecheck": "tsc --noEmit"
},
"devDependencies": {
+ "@cloudflare/workers-types": "4.20251008.0",
+ "@tsconfig/node22": "22.0.2",
+ "@types/bun": "1.3.0",
+ "@types/node": "24.7.1",
"drizzle-kit": "0.30.5",
"mysql2": "3.14.4"
}
diff --git a/packages/console/core/src/context.ts b/packages/console/core/src/context.ts
index c2ca6a313..59594cb4a 100644
--- a/packages/console/core/src/context.ts
+++ b/packages/console/core/src/context.ts
@@ -14,7 +14,7 @@ export namespace Context {
return result
},
provide<R>(value: T, fn: () => R) {
- return storage.run<R>(value, fn)
+ return storage.run(value, fn)
},
}
}
diff --git a/packages/console/function/package.json b/packages/console/function/package.json
index 40f12b1c2..97e5b8a87 100644
--- a/packages/console/function/package.json
+++ b/packages/console/function/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
- "version": "0.14.6",
+ "version": "0.14.7",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
@@ -8,7 +8,8 @@
"typecheck": "tsc --noEmit"
},
"devDependencies": {
- "@cloudflare/workers-types": "4.20250522.0",
+ "@cloudflare/workers-types": "4.20251008.0",
+ "@tsconfig/node22": "22.0.2",
"@types/node": "catalog:",
"openai": "5.11.0",
"typescript": "catalog:"
@@ -18,6 +19,8 @@
"@ai-sdk/openai": "2.0.2",
"@ai-sdk/openai-compatible": "1.0.1",
"@hono/zod-validator": "catalog:",
+ "@opencode-ai/console-core": "workspace:*",
+ "@opencode-ai/console-resource": "workspace:*",
"@openauthjs/openauth": "0.0.0-20250322224806",
"ai": "catalog:",
"hono": "catalog:",
diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json
index 5cdc1c4a8..7b83a7dd7 100644
--- a/packages/console/mail/package.json
+++ b/packages/console/mail/package.json
@@ -1,19 +1,20 @@
{
- "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-mail",
- "version": "0.14.6",
- "private": true,
- "type": "module",
+ "version": "0.14.7",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+ "@tsconfig/bun": "1.0.9",
"@types/react": "18.0.25",
"react": "18.2.0"
},
"exports": {
"./*": "./emails/templates/*"
},
+ "$schema": "https://json.schemastore.org/package.json",
+ "private": true,
"scripts": {
"dev": "email preview emails/templates"
- }
+ },
+ "type": "module"
}
diff --git a/packages/console/resource/package.json b/packages/console/resource/package.json
index 3f5f54ab6..a59aee750 100644
--- a/packages/console/resource/package.json
+++ b/packages/console/resource/package.json
@@ -11,5 +11,8 @@
},
"import": "./resource.node.ts"
}
+ },
+ "devDependencies": {
+ "@tsconfig/node22": "22.0.2"
}
}
diff --git a/packages/console/scripts/package.json b/packages/console/scripts/package.json
index 9259562f5..7c47c433c 100644
--- a/packages/console/scripts/package.json
+++ b/packages/console/scripts/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-scripts",
- "version": "0.14.6",
+ "version": "0.14.7",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
diff --git a/packages/desktop/package.json b/packages/desktop/package.json
index 54173ae0f..5885ad159 100644
--- a/packages/desktop/package.json
+++ b/packages/desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/desktop",
- "version": "0.14.6",
+ "version": "0.14.7",
"description": "",
"type": "module",
"scripts": {
@@ -13,6 +13,7 @@
"license": "MIT",
"devDependencies": {
"@tailwindcss/vite": "4.1.11",
+ "@tsconfig/bun": "1.0.9",
"@types/luxon": "3.7.1",
"@types/node": "catalog:",
"typescript": "catalog:",
@@ -36,7 +37,6 @@
"marked-shiki": "1.2.1",
"remeda": "catalog:",
"shiki": "3.9.2",
- "solid-js": "catalog:",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3"
diff --git a/packages/desktop/tsconfig.json b/packages/desktop/tsconfig.json
index d86b8ca46..82541a6d3 100644
--- a/packages/desktop/tsconfig.json
+++ b/packages/desktop/tsconfig.json
@@ -1,11 +1,18 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
- "extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
- "types": ["vite/client"],
- "lib": ["DOM", "DOM.Iterable"],
+ "allowJs": true,
+ "strict": true,
+ "noEmit": true,
+ "isolatedModules": true,
"paths": {
"@/*": ["./src/*"]
}
diff --git a/packages/function/package.json b/packages/function/package.json
index 44ee9be2c..fedecd97b 100644
--- a/packages/function/package.json
+++ b/packages/function/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
- "version": "0.14.6",
+ "version": "0.14.7",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index ea83d4e22..c0aa6c8d6 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
- "version": "0.14.6",
+ "version": "0.14.7",
"name": "opencode",
"type": "module",
"private": true,
@@ -20,6 +20,7 @@
"@ai-sdk/amazon-bedrock": "2.2.10",
"@ai-sdk/google-vertex": "3.0.16",
"@octokit/webhooks-types": "7.6.1",
+ "@parcel/watcher-win32-x64": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@tsconfig/bun": "1.0.7",
"@types/bun": "catalog:",
@@ -37,6 +38,7 @@
"@openauthjs/openauth": "0.4.3",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
+ "@parcel/watcher": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts
index 02090c69f..4c4bc7825 100755
--- a/packages/opencode/script/build.ts
+++ b/packages/opencode/script/build.ts
@@ -1,4 +1,5 @@
#!/usr/bin/env bun
+import path from "path"
const dir = new URL("..", import.meta.url).pathname
process.chdir(dir)
import { $ } from "bun"
@@ -32,6 +33,12 @@ for (const [os, arch] of targets) {
await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`
.cwd("../tui")
.quiet()
+
+ const watcher = `@parcel/watcher-${os === "windows" ? "win32" : os}-${arch.replace("-baseline", "")}${os === "linux" ? "-glibc" : ""}`
+ await $`mkdir -p ../../node_modules/${watcher}`
+ await $`npm pack npm pack ${watcher}`.cwd(path.join(dir, "../../node_modules")).quiet()
+ await $`tar -xf ../../node_modules/${watcher.replace("@parcel/", "parcel-")}-*.tgz -C ../../node_modules/${watcher} --strip-components=1`
+
await Bun.build({
compile: {
target: `bun-${os}-${arch}` as any,
diff --git a/packages/opencode/src/cli/cmd/export.ts b/packages/opencode/src/cli/cmd/export.ts
index 996522b5a..c8825c83d 100644
--- a/packages/opencode/src/cli/cmd/export.ts
+++ b/packages/opencode/src/cli/cmd/export.ts
@@ -66,7 +66,8 @@ export const ExportCommand = cmd({
})),
}
- console.log(JSON.stringify(exportData, null, 2))
+ process.stdout.write(JSON.stringify(exportData, null, 2))
+ process.stdout.write("\n")
} catch (error) {
UI.error(`Session not found: ${sessionID!}`)
process.exit(1)
diff --git a/packages/opencode/src/cli/error.ts b/packages/opencode/src/cli/error.ts
index fa77ca773..8288de1eb 100644
--- a/packages/opencode/src/cli/error.ts
+++ b/packages/opencode/src/cli/error.ts
@@ -10,6 +10,8 @@ export function FormatError(input: unknown) {
`Config file at ${input.data.path} is not valid JSON(C)` + (input.data.message ? `: ${input.data.message}` : "")
)
}
+ if (Config.DirectoryError.isInstance(input))
+ return `Directory "${input.data.dir}" in ${input.data.path} is not valid. Did you mean "${input.data.suggestion}"?`
if (Config.InvalidError.isInstance(input))
return [
`Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""),
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 40e4d90a4..aa785235f 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -15,6 +15,8 @@ import { Auth } from "../auth"
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
import { Instance } from "../project/instance"
import { LSPServer } from "../lsp/server"
+import { BunProc } from "@/bun"
+import { Installation } from "@/installation"
export namespace Config {
const log = Log.create({ service: "config" })
@@ -60,7 +62,8 @@ export namespace Config {
]
for (const dir of directories) {
- await assertValid(dir).catch(() => {})
+ await assertValid(dir)
+ installDependencies(dir)
result.command = mergeDeep(result.command ?? {}, await loadCommand(dir))
result.agent = mergeDeep(result.agent, await loadAgent(dir))
result.agent = mergeDeep(result.agent, await loadMode(dir))
@@ -117,26 +120,34 @@ export namespace Config {
}
})
+ const INVALID_DIRS = new Bun.Glob(`{${["agents", "commands", "plugins", "tools"].join(",")}}/`)
async function assertValid(dir: string) {
- const ALLOWED_DIRS = new Set(["agent", "command", "mode", "plugin", "tool", "themes"])
- const UNEXPECTED_DIR_GLOB = new Bun.Glob("*/")
- for await (const item of UNEXPECTED_DIR_GLOB.scan({
- absolute: true,
- followSymlinks: true,
- dot: true,
- cwd: dir,
- onlyFiles: false,
- })) {
- const dirname = path.basename(item)
- if (!ALLOWED_DIRS.has(dirname)) {
- throw new InvalidError({
- path: dir,
- message: `Unexpected directory "${dirname}" found in "${dir}". Only ${ALLOWED_DIRS.values().toArray().join(", ")} directories are allowed.`,
- })
- }
+ const invalid = await Array.fromAsync(
+ INVALID_DIRS.scan({
+ onlyFiles: false,
+ cwd: dir,
+ }),
+ )
+ for (const item of invalid) {
+ throw new DirectoryError({
+ path: dir,
+ dir: item,
+ suggestion: item.substring(0, item.length - 1),
+ })
}
}
+ async function installDependencies(dir: string) {
+ await Bun.write(path.join(dir, "package.json"), "{}")
+ await Bun.write(path.join(dir, ".gitignore"), ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))
+ await BunProc.run(
+ ["add", "@opencode-ai/plugin@" + (Installation.isDev() ? "latest" : Installation.VERSION), "--exact"],
+ {
+ cwd: dir,
+ },
+ )
+ }
+
const COMMAND_GLOB = new Bun.Glob("command/**/*.md")
async function loadCommand(dir: string) {
const result: Record<string, Command> = {}
@@ -680,10 +691,10 @@ export namespace Config {
}
const data = parsed.data
if (data.plugin) {
- for (let i = 0; i < data.plugin?.length; i++) {
+ for (let i = 0; i < data.plugin.length; i++) {
const plugin = data.plugin[i]
try {
- data.plugin[i] = import.meta.resolve(plugin, configFilepath)
+ data.plugin[i] = import.meta.resolve!(plugin, configFilepath)
} catch (err) {}
}
}
@@ -700,6 +711,15 @@ export namespace Config {
}),
)
+ export const DirectoryError = NamedError.create(
+ "ConfigDirectoryError",
+ z.object({
+ path: z.string(),
+ dir: z.string(),
+ suggestion: z.string(),
+ }),
+ )
+
export const InvalidError = NamedError.create(
"ConfigInvalidError",
z.object({
diff --git a/packages/opencode/src/file/ignore.ts b/packages/opencode/src/file/ignore.ts
index 912f2159e..2e1d1428f 100644
--- a/packages/opencode/src/file/ignore.ts
+++ b/packages/opencode/src/file/ignore.ts
@@ -1,28 +1,30 @@
+import { sep } from "node:path"
+
export namespace FileIgnore {
- const DEFAULT_PATTERNS = [
- // Dependencies
- "**/node_modules/**",
- "**/bower_components/**",
- "**/.pnpm-store/**",
- "**/vendor/**",
-
- // Build outputs
- "**/dist/**",
- "**/build/**",
- "**/out/**",
- "**/.next/**",
- "**/target/**", // Rust
- "**/bin/**",
- "**/obj/**", // .NET
-
- // Version control
- "**/.git/**",
- "**/.svn/**",
- "**/.hg/**",
-
- // IDE/Editor
- "**/.vscode/**",
- "**/.idea/**",
+ const FOLDERS = new Set([
+ "node_modules",
+ "bower_components",
+ ".pnpm-store",
+ "vendor",
+ "dist",
+ "build",
+ "out",
+ ".next",
+ "target",
+ "bin",
+ "obj",
+ ".git",
+ ".svn",
+ ".hg",
+ ".vscode",
+ ".idea",
+ ".turbo",
+ ".output",
+ "desktop",
+ ".sst",
+ ])
+
+ const FILES = [
"**/*.swp",
"**/*.swo",
@@ -41,22 +43,31 @@ export namespace FileIgnore {
"**/.nyc_output/**",
]
- const GLOBS = DEFAULT_PATTERNS.map((p) => new Bun.Glob(p))
+ const FILE_GLOBS = FILES.map((p) => new Bun.Glob(p))
+
+ export const PATTERNS = [...FILES, ...FOLDERS]
export function match(
filepath: string,
- opts: {
+ opts?: {
extra?: Bun.Glob[]
whitelist?: Bun.Glob[]
},
) {
- for (const glob of opts.whitelist || []) {
+ for (const glob of opts?.whitelist || []) {
if (glob.match(filepath)) return false
}
- const extra = opts.extra || []
- for (const glob of [...GLOBS, ...extra]) {
+
+ const parts = filepath.split(sep)
+ for (let i = 0; i < parts.length; i++) {
+ if (FOLDERS.has(parts[i])) return true
+ }
+
+ const extra = opts?.extra || []
+ for (const glob of [...FILE_GLOBS, ...extra]) {
if (glob.match(filepath)) return true
}
+
return false
}
}
diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts
index 652cb7853..1bae71cfb 100644
--- a/packages/opencode/src/file/watcher.ts
+++ b/packages/opencode/src/file/watcher.ts
@@ -1,11 +1,13 @@
import z from "zod/v4"
import { Bus } from "../bus"
-import chokidar from "chokidar"
import { Flag } from "../flag/flag"
import { Instance } from "../project/instance"
import { Log } from "../util/log"
import { FileIgnore } from "./ignore"
import { Config } from "../config/config"
+// @ts-ignore
+import { createWrapper } from "@parcel/watcher/wrapper"
+import { lazy } from "@/util/lazy"
export namespace FileWatcher {
const log = Log.create({ service: "file.watcher" })
@@ -20,37 +22,48 @@ export namespace FileWatcher {
),
}
+ const watcher = lazy(() => {
+ const binding = require(
+ `@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? "-glibc" : ""}`,
+ )
+ return createWrapper(binding) as typeof import("@parcel/watcher")
+ })
+
const state = Instance.state(
async () => {
if (Instance.project.vcs !== "git") return {}
log.info("init")
const cfg = await Config.get()
- const ignore = (cfg.watcher?.ignore ?? []).map((v) => new Bun.Glob(v))
- const watcher = chokidar.watch(Instance.directory, {
- ignoreInitial: true,
- ignored: (filepath) => {
- return FileIgnore.match(filepath, {
- whitelist: [new Bun.Glob("**/.git/{index,logs/HEAD}")],
- extra: ignore,
- })
+ const backend = (() => {
+ if (process.platform === "win32") return "windows"
+ if (process.platform === "darwin") return "fs-events"
+ if (process.platform === "linux") return "inotify"
+ })()
+ if (!backend) {
+ log.error("watcher backend not supported", { platform: process.platform })
+ return {}
+ }
+ log.info("watcher backend", { platform: process.platform, backend })
+ const sub = await watcher().subscribe(
+ Instance.directory,
+ (err, evts) => {
+ if (err) return
+ for (const evt of evts) {
+ log.info("event", evt)
+ if (evt.type === "create") Bus.publish(Event.Updated, { file: evt.path, event: "add" })
+ if (evt.type === "update") Bus.publish(Event.Updated, { file: evt.path, event: "change" })
+ if (evt.type === "delete") Bus.publish(Event.Updated, { file: evt.path, event: "unlink" })
+ }
+ },
+ {
+ ignore: [...FileIgnore.PATTERNS, ...(cfg.watcher?.ignore ?? [])],
+ backend,
},
- })
- watcher.on("change", (file) => {
- Bus.publish(Event.Updated, { file, event: "change" })
- })
- watcher.on("add", (file) => {
- Bus.publish(Event.Updated, { file, event: "add" })
- })
- watcher.on("unlink", (file) => {
- Bus.publish(Event.Updated, { file, event: "unlink" })
- })
- watcher.on("ready", () => {
- log.info("ready")
- })
- return { watcher }
+ )
+ return { sub }
},
async (state) => {
- state.watcher?.close()
+ state.sub?.unsubscribe()
},
)
diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts
index 501279dd4..0437c4c69 100644
--- a/packages/opencode/src/flag/flag.ts
+++ b/packages/opencode/src/flag/flag.ts
@@ -12,6 +12,7 @@ export namespace Flag {
// Experimental
export const OPENCODE_EXPERIMENTAL_WATCHER = truthy("OPENCODE_EXPERIMENTAL_WATCHER")
+ export const OPENCODE_EXPERIMENTAL_NO_BOOTSTRAP = truthy("OPENCODE_EXPERIMENTAL_NO_BOOTSTRAP")
function truthy(key: string) {
const value = process.env[key]?.toLowerCase()
diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts
index 66cc8eaa8..8a6589680 100644
--- a/packages/opencode/src/lsp/client.ts
+++ b/packages/opencode/src/lsp/client.ts
@@ -39,8 +39,8 @@ export namespace LSPClient {
l.info("starting client")
const connection = createMessageConnection(
- new StreamMessageReader(input.server.process.stdout),
- new StreamMessageWriter(input.server.process.stdin),
+ new StreamMessageReader(input.server.process.stdout as any),
+ new StreamMessageWriter(input.server.process.stdin as any),
)
const diagnostics = new Map<string, Diagnostic[]>()
diff --git a/packages/opencode/src/lsp/language.ts b/packages/opencode/src/lsp/language.ts
index 2b6e0476c..8ab7f48dc 100644
--- a/packages/opencode/src/lsp/language.ts
+++ b/packages/opencode/src/lsp/language.ts
@@ -52,7 +52,8 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
".m": "objective-c",
".mm": "objective-cpp",
".pl": "perl",
- ".pm": "perl6",
+ ".pm": "perl",
+ ".pm6": "perl6",
".php": "php",
".ps1": "powershell",
".psm1": "powershell",
diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts
index c680c2896..17211da11 100644
--- a/packages/opencode/src/lsp/server.ts
+++ b/packages/opencode/src/lsp/server.ts
@@ -25,11 +25,11 @@ export namespace LSPServer {
const files = Filesystem.up({
targets: patterns,
start: path.dirname(file),
- stop: Instance.worktree,
+ stop: Instance.directory,
})
const first = await files.next()
await files.return()
- if (!first.value) return Instance.worktree
+ if (!first.value) return Instance.directory
return path.dirname(first.value)
}
}
@@ -44,7 +44,7 @@ export namespace LSPServer {
export const Typescript: Info = {
id: "typescript",
- root: NearestRoot(["tsconfig.json", "package.json", "jsconfig.json"]),
+ root: NearestRoot(["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"]),
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
async spawn(root) {
const tsserver = await Bun.resolve("typescript/lib/tsserver.js", Instance.directory).catch(() => {})
@@ -70,20 +70,7 @@ export namespace LSPServer {
export const Vue: Info = {
id: "vue",
extensions: [".vue"],
- root: NearestRoot([
- "tsconfig.json",
- "jsconfig.json",
- "package.json",
- "pnpm-lock.yaml",
- "yarn.lock",
- "bun.lockb",
- "bun.lock",
- "vite.config.ts",
- "vite.config.js",
- "nuxt.config.ts",
- "nuxt.config.js",
- "vue.config.js",
- ]),
+ root: NearestRoot(["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"]),
async spawn(root) {
let binary = Bun.which("vue-language-server")
const args: string[] = []
@@ -131,20 +118,7 @@ export namespace LSPServer {
export const ESLint: Info = {
id: "eslint",
- root: NearestRoot([
- "eslint.config.js",
- "eslint.config.mjs",
- "eslint.config.cjs",
- "eslint.config.ts",
- "eslint.config.mts",
- "eslint.config.cts",
- ".eslintrc.js",
- ".eslintrc.cjs",
- ".eslintrc.yaml",
- ".eslintrc.yml",
- ".eslintrc.json",
- "package.json",
- ]),
+ root: NearestRoot(["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"]),
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue"],
async spawn(root) {
const eslint = await Bun.resolve("eslint", Instance.directory).catch(() => {})
@@ -659,19 +633,7 @@ export namespace LSPServer {
export const Svelte: Info = {
id: "svelte",
extensions: [".svelte"],
- root: NearestRoot([
- "tsconfig.json",
- "jsconfig.json",
- "package.json",
- "pnpm-lock.yaml",
- "yarn.lock",
- "bun.lockb",
- "bun.lock",
- "vite.config.ts",
- "vite.config.js",
- "svelte.config.ts",
- "svelte.config.js",
- ]),
+ root: NearestRoot(["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"]),
async spawn(root) {
let binary = Bun.which("svelteserver")
const args: string[] = []
diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts
index a3037528f..b636cb3ea 100644
--- a/packages/opencode/src/project/bootstrap.ts
+++ b/packages/opencode/src/project/bootstrap.ts
@@ -5,8 +5,10 @@ import { LSP } from "../lsp"
import { Snapshot } from "../snapshot"
import { FileWatcher } from "../file/watcher"
import { File } from "../file"
+import { Flag } from "../flag/flag"
export async function InstanceBootstrap() {
+ if (Flag.OPENCODE_EXPERIMENTAL_NO_BOOTSTRAP) return
await Plugin.init()
Share.init()
Format.init()
diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts
index 514203e91..97310dd19 100644
--- a/packages/opencode/src/provider/models.ts
+++ b/packages/opencode/src/provider/models.ts
@@ -28,6 +28,12 @@ export namespace ModelsDev {
context: z.number(),
output: z.number(),
}),
+ modalities: z
+ .object({
+ input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
+ output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
+ })
+ .optional(),
experimental: z.boolean().optional(),
options: z.record(z.string(), z.any()),
provider: z.object({ npm: z.string() }).optional(),
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index c18bc4898..e0fe4be23 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -279,6 +279,11 @@ export namespace Provider {
context: 0,
output: 0,
},
+ modalities: model.modalities ??
+ existing?.modalities ?? {
+ input: ["text"],
+ output: ["text"],
+ },
provider: model.provider ?? existing?.provider,
}
parsed.models[modelID] = parsedModel
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 7704a6155..8dc059ca1 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -17,71 +17,6 @@ export namespace MessageV2 {
}),
)
- export const ToolStatePending = z
- .object({
- status: z.literal("pending"),
- })
- .meta({
- ref: "ToolStatePending",
- })
-
- export type ToolStatePending = z.infer<typeof ToolStatePending>
-
- export const ToolStateRunning = z
- .object({
- status: z.literal("running"),
- input: z.any(),
- title: z.string().optional(),
- metadata: z.record(z.string(), z.any()).optional(),
- time: z.object({
- start: z.number(),
- }),
- })
- .meta({
- ref: "ToolStateRunning",
- })
- export type ToolStateRunning = z.infer<typeof ToolStateRunning>
-
- export const ToolStateCompleted = z
- .object({
- status: z.literal("completed"),
- input: z.record(z.string(), z.any()),
- output: z.string(),
- title: z.string(),
- metadata: z.record(z.string(), z.any()),
- time: z.object({
- start: z.number(),
- end: z.number(),
- compacted: z.number().optional(),
- }),
- })
- .meta({
- ref: "ToolStateCompleted",
- })
- export type ToolStateCompleted = z.infer<typeof ToolStateCompleted>
-
- export const ToolStateError = z
- .object({
- status: z.literal("error"),
- input: z.record(z.string(), z.any()),
- error: z.string(),
- metadata: z.record(z.string(), z.any()).optional(),
- time: z.object({
- start: z.number(),
- end: z.number(),
- }),
- })
- .meta({
- ref: "ToolStateError",
- })
- export type ToolStateError = z.infer<typeof ToolStateError>
-
- export const ToolState = z
- .discriminatedUnion("status", [ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError])
- .meta({
- ref: "ToolState",
- })
-
const PartBase = z.object({
id: z.string(),
sessionID: z.string(),
@@ -134,17 +69,6 @@ export namespace MessageV2 {
})
export type ReasoningPart = z.infer<typeof ReasoningPart>
- export const ToolPart = PartBase.extend({
- type: z.literal("tool"),
- callID: z.string(),
- tool: z.string(),
- state: ToolState,
- metadata: z.record(z.string(), z.any()).optional(),
- }).meta({
- ref: "ToolPart",
- })
- export type ToolPart = z.infer<typeof ToolPart>
-
const FilePartSourceBase = z.object({
text: z
.object({
@@ -228,6 +152,83 @@ export namespace MessageV2 {
})
export type StepFinishPart = z.infer<typeof StepFinishPart>
+ export const ToolStatePending = z
+ .object({
+ status: z.literal("pending"),
+ })
+ .meta({
+ ref: "ToolStatePending",
+ })
+
+ export type ToolStatePending = z.infer<typeof ToolStatePending>
+
+ export const ToolStateRunning = z
+ .object({
+ status: z.literal("running"),
+ input: z.any(),
+ title: z.string().optional(),
+ metadata: z.record(z.string(), z.any()).optional(),
+ time: z.object({
+ start: z.number(),
+ }),
+ })
+ .meta({
+ ref: "ToolStateRunning",
+ })
+ export type ToolStateRunning = z.infer<typeof ToolStateRunning>
+
+ export const ToolStateCompleted = z
+ .object({
+ status: z.literal("completed"),
+ input: z.record(z.string(), z.any()),
+ output: z.string(),
+ title: z.string(),
+ metadata: z.record(z.string(), z.any()),
+ time: z.object({
+ start: z.number(),
+ end: z.number(),
+ compacted: z.number().optional(),
+ }),
+ attachments: FilePart.array().optional(),
+ })
+ .meta({
+ ref: "ToolStateCompleted",
+ })
+ export type ToolStateCompleted = z.infer<typeof ToolStateCompleted>
+
+ export const ToolStateError = z
+ .object({
+ status: z.literal("error"),
+ input: z.record(z.string(), z.any()),
+ error: z.string(),
+ metadata: z.record(z.string(), z.any()).optional(),
+ time: z.object({
+ start: z.number(),
+ end: z.number(),
+ }),
+ })
+ .meta({
+ ref: "ToolStateError",
+ })
+ export type ToolStateError = z.infer<typeof ToolStateError>
+
+ export const ToolState = z
+ .discriminatedUnion("status", [ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError])
+ .meta({
+ ref: "ToolState",
+ })
+
+ export const ToolPart = PartBase.extend({
+ type: z.literal("tool"),
+ callID: z.string(),
+ tool: z.string(),
+ state: ToolState,
+ metadata: z.record(z.string(), z.any()).optional(),
+ }).meta({
+ ref: "ToolPart",
+ })
+ export type ToolPart = z.infer<typeof ToolPart>
+
const Base = z.object({
id: z.string(),
sessionID: z.string(),
@@ -531,7 +532,25 @@ export namespace MessageV2 {
},
]
if (part.type === "tool") {
- if (part.state.status === "completed")
+ if (part.state.status === "completed") {
+ if (part.state.attachments?.length) {
+ result.push({
+ id: Identifier.ascending("message"),
+ role: "user",
+ parts: [
+ {
+ type: "text",
+ text: `Tool ${part.tool} returned an attachment:`,
+ },
+ ...part.state.attachments.map((attachment) => ({
+ type: "file" as const,
+ url: attachment.url,
+ mediaType: attachment.mime,
+ filename: attachment.filename,
+ })),
+ ],
+ })
+ }
return [
{
type: ("tool-" + part.tool) as `tool-${string}`,
@@ -542,6 +561,7 @@ export namespace MessageV2 {
callProviderMetadata: part.metadata,
},
]
+ }
if (part.state.status === "error")
return [
{
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index b65309d9e..0ccb208c6 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -457,6 +457,10 @@ export namespace SessionPrompt {
abort: options.abortSignal!,
messageID: input.processor.message.id,
callID: options.toolCallId,
+ extra: {
+ modelID: input.modelID,
+ providerID: input.providerID,
+ },
agent: input.agent.name,
metadata: async (val) => {
const match = input.processor.partFromToolCall(options.toolCallId)
@@ -989,6 +993,7 @@ export namespace SessionPrompt {
start: match.state.time.start,
end: Date.now(),
},
+ attachments: value.output.attachments,
},
})
delete toolcalls[value.toolCallId]
diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts
index 2ed3accbd..5e8cecaf2 100644
--- a/packages/opencode/src/tool/read.ts
+++ b/packages/opencode/src/tool/read.ts
@@ -7,6 +7,8 @@ import { FileTime } from "../file/time"
import DESCRIPTION from "./read.txt"
import { Filesystem } from "../util/filesystem"
import { Instance } from "../project/instance"
+import { Provider } from "../provider/provider"
+import { Identifier } from "../id/id"
const DEFAULT_READ_LIMIT = 2000
const MAX_LINE_LENGTH = 2000
@@ -23,6 +25,8 @@ export const ReadTool = Tool.define("read", {
if (!path.isAbsolute(filepath)) {
filepath = path.join(process.cwd(), filepath)
}
+ const title = path.relative(Instance.worktree, filepath)
+
if (!ctx.extra?.["bypassCwdCheck"] && !Filesystem.contains(Instance.directory, filepath)) {
throw new Error(`File ${filepath} is not in the current working directory`)
}
@@ -48,12 +52,45 @@ export const ReadTool = Tool.define("read", {
throw new Error(`File not found: ${filepath}`)
}
- const limit = params.limit ?? DEFAULT_READ_LIMIT
- const offset = params.offset || 0
const isImage = isImageFile(filepath)
- if (isImage) throw new Error(`This is an image file of type: ${isImage}\nUse a different tool to process images`)
+ const supportsImages = await (async () => {
+ if (!ctx.extra?.["providerID"] || !ctx.extra?.["modelID"]) return false
+ const providerID = ctx.extra["providerID"] as string
+ const modelID = ctx.extra["modelID"] as string
+ const model = await Provider.getModel(providerID, modelID).catch(() => undefined)
+ if (!model) return false
+ return model.info.modalities?.input?.includes("image") ?? false
+ })()
+ if (isImage) {
+ if (!supportsImages) {
+ throw new Error(`Failed to read image: ${filepath}, model may not be able to read images`)
+ }
+ const mime = file.type
+ const msg = "Image read successfully"
+ return {
+ title,
+ output: msg,
+ metadata: {
+ preview: msg,
+ },
+ attachments: [
+ {
+ id: Identifier.ascending("part"),
+ sessionID: ctx.sessionID,
+ messageID: ctx.messageID,
+ type: "file",
+ mime,
+ url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`,
+ },
+ ],
+ }
+ }
+
const isBinary = await isBinaryFile(filepath, file)
if (isBinary) throw new Error(`Cannot read binary file: ${filepath}`)
+
+ const limit = params.limit ?? DEFAULT_READ_LIMIT
+ const offset = params.offset || 0
const lines = await file.text().then((text) => text.split("\n"))
const raw = lines.slice(offset, offset + limit).map((line) => {
return line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) + "..." : line
@@ -76,7 +113,7 @@ export const ReadTool = Tool.define("read", {
FileTime.read(ctx.sessionID, filepath)
return {
- title: path.relative(Instance.worktree, filepath),
+ title,
output,
metadata: {
preview,
diff --git a/packages/opencode/src/tool/read.txt b/packages/opencode/src/tool/read.txt
index 3904c0939..b5bffee26 100644
--- a/packages/opencode/src/tool/read.txt
+++ b/packages/opencode/src/tool/read.txt
@@ -7,6 +7,6 @@ Usage:
- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
- Any lines longer than 2000 characters will be truncated
- Results are returned using cat -n format, with line numbers starting at 1
-- This tool cannot read binary files, including images
-- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
+- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.
+- You can read image files using this tool.
diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts
index a372a69d7..2fc72274d 100644
--- a/packages/opencode/src/tool/tool.ts
+++ b/packages/opencode/src/tool/tool.ts
@@ -1,9 +1,11 @@
import z from "zod/v4"
+import type { MessageV2 } from "../session/message-v2"
export namespace Tool {
interface Metadata {
[key: string]: any
}
+
export type Context<M extends Metadata = Metadata> = {
sessionID: string
messageID: string
@@ -25,6 +27,7 @@ export namespace Tool {
title: string
metadata: M
output: string
+ attachments?: MessageV2.FilePart[]
}>
}>
}
diff --git a/packages/opencode/src/util/context.ts b/packages/opencode/src/util/context.ts
index 1e5db36a7..b56329d84 100644
--- a/packages/opencode/src/util/context.ts
+++ b/packages/opencode/src/util/context.ts
@@ -18,7 +18,7 @@ export namespace Context {
return result
},
provide<R>(value: T, fn: () => R) {
- return storage.run<R>(value, fn)
+ return storage.run(value, fn)
},
}
}
diff --git a/packages/opencode/test/file/ignore.test.ts b/packages/opencode/test/file/ignore.test.ts
new file mode 100644
index 000000000..6387ff63e
--- /dev/null
+++ b/packages/opencode/test/file/ignore.test.ts
@@ -0,0 +1,10 @@
+import { test, expect } from "bun:test"
+import { FileIgnore } from "../../src/file/ignore"
+
+test("match nested and non-nested", () => {
+ expect(FileIgnore.match("node_modules/index.js")).toBe(true)
+ expect(FileIgnore.match("node_modules")).toBe(true)
+ expect(FileIgnore.match("node_modules/")).toBe(true)
+ expect(FileIgnore.match("node_modules/bar")).toBe(true)
+ expect(FileIgnore.match("node_modules/bar/")).toBe(true)
+})
diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts
index 1398162e3..0ffed351f 100644
--- a/packages/opencode/test/snapshot/snapshot.test.ts
+++ b/packages/opencode/test/snapshot/snapshot.test.ts
@@ -123,7 +123,7 @@ test("binary file handling", async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
- await Bun.write(`${tmp.path}/image.png`, Buffer.from([0x89, 0x50, 0x4e, 0x47]))
+ await Bun.write(`${tmp.path}/image.png`, new Uint8Array([0x89, 0x50, 0x4e, 0x47]))
const patch = await Snapshot.patch(before!)
expect(patch.files).toContain(`${tmp.path}/image.png`)
diff --git a/packages/opencode/tsconfig.json b/packages/opencode/tsconfig.json
index 167d7936c..478672ce9 100644
--- a/packages/opencode/tsconfig.json
+++ b/packages/opencode/tsconfig.json
@@ -5,6 +5,7 @@
"jsx": "preserve",
"jsxImportSource": "@opentui/solid",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "types": [],
"customConditions": ["browser"],
"baseUrl": ".",
"paths": {
diff --git a/packages/plugin/package.json b/packages/plugin/package.json
index 36deca0cf..2b49cd010 100644
--- a/packages/plugin/package.json
+++ b/packages/plugin/package.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
- "version": "0.14.6",
+ "version": "0.14.7",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
@@ -20,6 +20,7 @@
},
"devDependencies": {
"@tsconfig/node22": "catalog:",
+ "@types/node": "24.7.1",
"typescript": "catalog:"
}
} \ No newline at end of file
diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json
index 56390dd57..3f6073e34 100644
--- a/packages/sdk/js/package.json
+++ b/packages/sdk/js/package.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
- "version": "0.14.6",
+ "version": "0.14.7",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
@@ -16,13 +16,12 @@
"dist"
],
"devDependencies": {
- "typescript": "catalog:",
- "@hey-api/openapi-ts": "0.80.1",
- "@tsconfig/node22": "catalog:"
- },
- "dependencies": {
- "@hey-api/openapi-ts": "0.81.0"
+ "@hey-api/openapi-ts": "0.81.0",
+ "@tsconfig/node22": "catalog:",
+ "@types/node": "24.7.1",
+ "typescript": "catalog:"
},
+ "dependencies": {},
"publishConfig": {
"directory": "dist"
}
diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts
index cc94c1f1f..706fff125 100644
--- a/packages/sdk/js/src/gen/types.gen.ts
+++ b/packages/sdk/js/src/gen/types.gen.ts
@@ -413,6 +413,10 @@ export type Config = {
context: number
output: number
}
+ modalities?: {
+ input: Array<"text" | "audio" | "image" | "video" | "pdf">
+ output: Array<"text" | "audio" | "image" | "video" | "pdf">
+ }
experimental?: boolean
options?: {
[key: string]: unknown
@@ -746,6 +750,7 @@ export type ToolStateCompleted = {
end: number
compacted?: number
}
+ attachments?: Array<FilePart>
}
export type ToolStateError = {
@@ -904,6 +909,10 @@ export type Model = {
context: number
output: number
}
+ modalities?: {
+ input: Array<"text" | "audio" | "image" | "video" | "pdf">
+ output: Array<"text" | "audio" | "image" | "video" | "pdf">
+ }
experimental?: boolean
options: {
[key: string]: unknown
diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs
index fb7bd75b5..3bcd98cb7 100644
--- a/packages/web/astro.config.mjs
+++ b/packages/web/astro.config.mjs
@@ -74,6 +74,7 @@ export default defineConfig({
{
label: "Configure",
items: [
+ "tools",
"rules",
"agents",
"models",
diff --git a/packages/web/package.json b/packages/web/package.json
index b01a51e2a..b3390c169 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/web",
"type": "module",
- "version": "0.14.6",
+ "version": "0.14.7",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
@@ -31,7 +31,7 @@
"sharp": "0.32.5",
"shiki": "3.4.2",
"solid-js": "catalog:",
- "toolbeam-docs-theme": "0.4.7"
+ "toolbeam-docs-theme": "0.4.8"
},
"devDependencies": {
"opencode": "workspace:*",
diff --git a/packages/web/src/content/docs/agents.mdx b/packages/web/src/content/docs/agents.mdx
index 7016d6576..e15ba6cb1 100644
--- a/packages/web/src/content/docs/agents.mdx
+++ b/packages/web/src/content/docs/agents.mdx
@@ -303,27 +303,33 @@ Use the `model` config to override the default model for this agent. Useful for
Control which tools are available in this agent with the `tools` config. You can enable or disable specific tools by setting them to `true` or `false`.
-```json title="opencode.json"
+```json title="opencode.json" {3-6,9-12}
{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "write": true,
+ "bash": true
+ },
"agent": {
- "readonly": {
+ "plan": {
"tools": {
"write": false,
- "edit": false,
- "bash": false,
- "read": true,
- "grep": true,
- "glob": true
+ "bash": false
}
}
}
}
```
+:::note
+The agent-specific config overrides the global config.
+:::
+
You can also use wildcards to control multiple tools at once. For example, to disable all tools from an MCP server:
```json title="opencode.json"
{
+ "$schema": "https://opencode.ai/config.json",
"agent": {
"readonly": {
"tools": {
@@ -336,27 +342,7 @@ You can also use wildcards to control multiple tools at once. For example, to di
}
```
-If no tools are specified, all tools are enabled by default.
-
----
-
-#### Available tools
-
-Here are all the tools can be controlled through the agent config.
-
-| Tool | Description |
-| ----------- | ----------------------- |
-| `bash` | Execute shell commands |
-| `edit` | Modify existing files |
-| `write` | Create new files |
-| `read` | Read file contents |
-| `grep` | Search file contents |
-| `glob` | Find files by pattern |
-| `list` | List directory contents |
-| `patch` | Apply patches to files |
-| `todowrite` | Manage todo lists |
-| `todoread` | Read todo lists |
-| `webfetch` | Fetch web content |
+[Learn more about tools](/docs/tools).
---
diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx
index d996cb94b..f3b2a05a0 100644
--- a/packages/web/src/content/docs/config.mdx
+++ b/packages/web/src/content/docs/config.mdx
@@ -86,6 +86,24 @@ You can configure TUI-specific settings through the `tui` option.
---
+### Tools
+
+You can manage the tools an LLM can use through the `tools` option.
+
+```json title="opencode.json"
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "write": false,
+ "bash": false
+ }
+}
+```
+
+[Learn more about tools here](/docs/tools).
+
+---
+
### Models
You can configure the providers and models you want to use in your OpenCode config through the `provider`, `model` and `small_model` options.
diff --git a/packages/web/src/content/docs/custom-tools.mdx b/packages/web/src/content/docs/custom-tools.mdx
index d982475ef..3f4cf06bc 100644
--- a/packages/web/src/content/docs/custom-tools.mdx
+++ b/packages/web/src/content/docs/custom-tools.mdx
@@ -3,7 +3,7 @@ title: Custom Tools
description: Create tools the LLM can call in opencode.
---
-Custom tools are functions you create that the LLM can call during conversations. They work alongside opencode's built-in tools like `read`, `write`, and `bash`.
+Custom tools are functions you create that the LLM can call during conversations. They work alongside opencode's [built-in tools](/docs/tools) like `read`, `write`, and `bash`.
---
diff --git a/packages/web/src/content/docs/mcp-servers.mdx b/packages/web/src/content/docs/mcp-servers.mdx
index 0ceeb47a3..535e22839 100644
--- a/packages/web/src/content/docs/mcp-servers.mdx
+++ b/packages/web/src/content/docs/mcp-servers.mdx
@@ -3,10 +3,10 @@ title: MCP servers
description: Add local and remote MCP tools.
---
-You can add external tools to opencode using the _Model Context Protocol_, or MCP. opencode supports both:
+You can add external tools to OpenCode using the _Model Context Protocol_, or MCP. OpenCode supports both:
- Local servers
-- And remote servers
+- Remote servers
Once added, MCP tools are automatically available to the LLM alongside built-in tools.
@@ -14,15 +14,22 @@ Once added, MCP tools are automatically available to the LLM alongside built-in
## Configure
-You can define MCP servers in your opencode config under `mcp`.
+You can define MCP servers in your OpenCode config under `mcp`.
---
### Local
-Add local MCP servers using `"type": "local"` within the MCP object. Multiple MCP servers can be added. The key string for each server can be any arbitrary name.
+Add local MCP servers using `"type": "local"` within the MCP object. Multiple MCP servers can be added.
-```json title="opencode.json"
+:::tip
+MCP servers add to your context, so you want to be careful with which
+ones you enable.
+:::
+
+The key string for each server can be any arbitrary name.
+
+```json title="opencode.json" {15}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
@@ -95,16 +102,70 @@ Local and remote servers can be used together within the same `mcp` config objec
---
-## Per agent
+## Manage
+
+Your MCPs are available as tools in OpenCode, alongside built-in tools. So you
+can manage them through the OpenCode config like any other tool.
+
+---
+
+### Global
+
+This means that you can enable or disable them globally.
+
+```json title="opencode.json" {14}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "mcp": {
+ "my-mcp-foo": {
+ "type": "local",
+ "command": ["bun", "x", "my-mcp-command-foo"]
+ },
+ "my-mcp-bar": {
+ "type": "local",
+ "command": ["bun", "x", "my-mcp-command-bar"]
+ }
+ },
+ "tools": {
+ "my-mcp-foo": false
+ }
+}
+```
+
+We can also use a glob pattern to disable all matching MCPs.
+
+```json title="opencode.json" {14}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "mcp": {
+ "my-mcp-foo": {
+ "type": "local",
+ "command": ["bun", "x", "my-mcp-command-foo"]
+ },
+ "my-mcp-bar": {
+ "type": "local",
+ "command": ["bun", "x", "my-mcp-command-bar"]
+ }
+ },
+ "tools": {
+ "my-mcp*": false
+ }
+}
+```
+
+Here we are using the glob pattern `my-mcp*` to disable all MCPs.
+
+---
+
+### Per agent
If you have a large number of MCP servers you may want to only enable them per
agent and disable them globally. To do this:
-1. Configure the MCP server.
-2. Disable it as a tool globally.
-3. In your [agent config](/docs/agents#tools) enable the MCP server as a tool.
+1. Disable it as a tool globally.
+2. In your [agent config](/docs/agents#tools) enable the MCP server as a tool.
-```json title="opencode.json" {11, 14-17}
+```json title="opencode.json" {11, 14-18}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
@@ -126,3 +187,13 @@ agent and disable them globally. To do this:
}
}
```
+
+---
+
+#### Glob patterns
+
+The glob pattern uses simple regex globbing patterns.
+
+- `*` matches zero or more of any character
+- `?` matches exactly one character
+- All other characters match literally
diff --git a/packages/web/src/content/docs/plugins.mdx b/packages/web/src/content/docs/plugins.mdx
index bfcf1ed87..fe1ae9bfd 100644
--- a/packages/web/src/content/docs/plugins.mdx
+++ b/packages/web/src/content/docs/plugins.mdx
@@ -111,7 +111,7 @@ export const EnvProtection = async ({ project, client, $, directory, worktree })
Plugins can also add custom tools to opencode:
```ts title=".opencode/plugin/custom-tools.ts"
-import type { Plugin, tool } from "@opencode-ai/plugin"
+import { type Plugin, tool } from "@opencode-ai/plugin"
export const CustomToolsPlugin: Plugin = async (ctx) => {
return {
diff --git a/packages/web/src/content/docs/tools.mdx b/packages/web/src/content/docs/tools.mdx
new file mode 100644
index 000000000..11761d4c8
--- /dev/null
+++ b/packages/web/src/content/docs/tools.mdx
@@ -0,0 +1,316 @@
+---
+title: Tools
+description: Manage the tools an LLM can use.
+---
+
+Tools allow the LLM to perform actions in your codebase. OpenCode comes with a set of built-in tools, but you can extend it with [custom tools](/docs/custom-tools) or [MCP servers](/docs/mcp-servers).
+
+By default, all tools are **enabled** and don't need permission to run. But you can configure this and control the [permissions](/docs/permissions) through your config.
+
+---
+
+## Configure
+
+You can configure tools globally or per agent. Agent-specific configs override global settings.
+
+By default, all tools are set to `true`. To disable a tool, set it to `false`.
+
+---
+
+### Global
+
+Disable or enable tools globally using the `tools` option.
+
+```json title="opencode.json"
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "write": false,
+ "bash": false,
+ "webfetch": true
+ }
+}
+```
+
+You can also use wildcards to control multiple tools at once. For example, to disable all tools from an MCP server:
+
+```json title="opencode.json"
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "mymcp_*": false
+ }
+}
+```
+
+---
+
+### Per agent
+
+Override global tool settings for specific agents using the `tools` config in the agent definition.
+
+```json title="opencode.json" {3-6,9-12}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "write": true,
+ "bash": true
+ },
+ "agent": {
+ "plan": {
+ "tools": {
+ "write": false,
+ "bash": false
+ }
+ }
+ }
+}
+```
+
+For example, here the `plan` agent overrides the global config to disable `write` and `bash` tools.
+
+You can also configure tools for agents in Markdown.
+
+```markdown title="~/.config/opencode/agent/readonly.md"
+---
+description: Read-only analysis agent
+mode: subagent
+tools:
+ write: false
+ edit: false
+ bash: false
+---
+
+Analyze code without making any modifications.
+```
+
+[Learn more](/docs/agents#tools) about configuring tools per agent.
+
+---
+
+## Built-in
+
+Here are all the built-in tools available in OpenCode.
+
+---
+
+### bash
+
+Execute shell commands in your project environment.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "bash": true
+ }
+}
+```
+
+This tool allows the LLM to run terminal commands like `npm install`, `git status`, or any other shell command.
+
+---
+
+### edit
+
+Modify existing files using exact string replacements.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "edit": true
+ }
+}
+```
+
+This tool performs precise edits to files by replacing exact text matches. It's the primary way the LLM modifies code.
+
+---
+
+### write
+
+Create new files or overwrite existing ones.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "write": true
+ }
+}
+```
+
+Use this to allow the LLM to create new files. It will overwrite existing files if they already exist.
+
+---
+
+### read
+
+Read file contents from your codebase.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "read": true
+ }
+}
+```
+
+This tool reads files and returns their contents. It supports reading specific line ranges for large files.
+
+---
+
+### grep
+
+Search file contents using regular expressions.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "grep": true
+ }
+}
+```
+
+Fast content search across your codebase. Supports full regex syntax and file pattern filtering.
+
+---
+
+### glob
+
+Find files by pattern matching.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "glob": true
+ }
+}
+```
+
+Search for files using glob patterns like `**/*.js` or `src/**/*.ts`. Returns matching file paths sorted by modification time.
+
+---
+
+### list
+
+List files and directories in a given path.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "list": true
+ }
+}
+```
+
+This tool lists directory contents. It accepts glob patterns to filter results.
+
+---
+
+### patch
+
+Apply patches to files.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "patch": true
+ }
+}
+```
+
+This tool applies patch files to your codebase. Useful for applying diffs and patches from various sources.
+
+---
+
+### todowrite
+
+Manage todo lists during coding sessions.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "todowrite": true
+ }
+}
+```
+
+Creates and updates task lists to track progress during complex operations. The LLM uses this to organize multi-step tasks.
+
+---
+
+### todoread
+
+Read existing todo lists.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "todoread": true
+ }
+}
+```
+
+Reads the current todo list state. Used by the LLM to track what tasks are pending or completed.
+
+---
+
+### webfetch
+
+Fetch web content.
+
+```json title="opencode.json" {4}
+{
+ "$schema": "https://opencode.ai/config.json",
+ "tools": {
+ "webfetch": true
+ }
+}
+```
+
+Allows the LLM to fetch and read web pages. Useful for looking up documentation or researching online resources.
+
+---
+
+## Custom tools
+
+Custom tools let you define your own functions that the LLM can call. These are defined in your config file and can execute arbitrary code.
+
+[Learn more](/docs/custom-tools) about creating custom tools.
+
+---
+
+## MCP servers
+
+MCP (Model Context Protocol) servers allow you to integrate external tools and services. This includes database access, API integrations, and third-party services.
+
+[Learn more](/docs/mcp-servers) about configuring MCP servers.
+
+---
+
+## Internals
+
+Internally, tools like `grep`, `glob`, and `list` use [ripgrep](https://github.com/BurntSushi/ripgrep) under the hood. By default, ripgrep respects `.gitignore` patterns, which means files and directories listed in your `.gitignore` will be excluded from searches and listings.
+
+---
+
+### Ignore patterns
+
+To include files that would normally be ignored, create a `.ignore` file in your project root. This file can explicitly allow certain paths.
+
+```text title=".ignore"
+!node_modules/
+!dist/
+!build/
+```
+
+For example, this `.ignore` file allows ripgrep to search within `node_modules/`, `dist/`, and `build/` directories even if they're listed in `.gitignore`.
diff --git a/packages/web/src/content/docs/zen.mdx b/packages/web/src/content/docs/zen.mdx
index 6bbe34a84..f1c2b3338 100644
--- a/packages/web/src/content/docs/zen.mdx
+++ b/packages/web/src/content/docs/zen.mdx
@@ -66,6 +66,7 @@ You can also access our models through the following API endpoints.
| ---------------- | ---------------- | --------------------------------------------- | --------------------------- |
| GPT 5 | gpt-5 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5 Codex | gpt-5-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
+| Claude Sonnet 4.5 | claude-sonnet-4-5 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Sonnet 4 | claude-sonnet-4 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
diff --git a/script/publish.ts b/script/publish.ts
index a4f2ba663..05d6df222 100755
--- a/script/publish.ts
+++ b/script/publish.ts
@@ -2,8 +2,8 @@
import { $ } from "bun"
import { createOpencode } from "@opencode-ai/sdk"
-if (process.versions.bun !== "1.2.21") {
- throw new Error("This script requires [email protected]")
+if (process.versions.bun !== "1.3.0") {
+ throw new Error("This script requires [email protected]")
}
const notes = [] as string[]
diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json
index 0f7e8d238..e9e71e24d 100644
--- a/sdks/vscode/package.json
+++ b/sdks/vscode/package.json
@@ -2,7 +2,7 @@
"name": "opencode",
"displayName": "opencode",
"description": "opencode for VS Code",
- "version": "0.14.6",
+ "version": "0.14.7",
"publisher": "sst-dev",
"repository": {
"type": "git",