فهرست منبع

Merge branch 'dev' into truncate-to-file

Aiden Cline 3 ماه پیش
والد
کامیت
ff2c1c4267
47فایلهای تغییر یافته به همراه639 افزوده شده و 105 حذف شده
  1. 1 2
      .github/workflows/publish.yml
  2. 2 1
      README.md
  3. 2 1
      README.zh-CN.md
  4. 2 1
      README.zh-TW.md
  5. 1 0
      STATS.md
  6. 31 25
      bun.lock
  7. 1 1
      nix/hashes.json
  8. 1 1
      packages/app/package.json
  9. 6 7
      packages/app/src/components/prompt-input.tsx
  10. 1 1
      packages/console/app/package.json
  11. 1 1
      packages/console/core/package.json
  12. 1 1
      packages/console/function/package.json
  13. 1 1
      packages/console/mail/package.json
  14. 1 1
      packages/desktop/package.json
  15. 14 0
      packages/desktop/src-tauri/entitlements.plist
  16. 1 1
      packages/desktop/src-tauri/src/lib.rs
  17. 7 4
      packages/desktop/src/index.tsx
  18. 1 1
      packages/enterprise/package.json
  19. 6 6
      packages/extensions/zed/extension.toml
  20. 1 1
      packages/function/package.json
  21. 2 2
      packages/opencode/package.json
  22. 1 0
      packages/opencode/src/agent/agent.ts
  23. 11 4
      packages/opencode/src/agent/prompt/title.txt
  24. 4 2
      packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
  25. 10 9
      packages/opencode/src/cli/cmd/tui/context/theme.tsx
  26. 4 0
      packages/opencode/src/cli/cmd/tui/context/theme/lucent-orng.json
  27. 4 0
      packages/opencode/src/cli/cmd/tui/context/theme/orng.json
  28. 29 10
      packages/opencode/src/config/config.ts
  29. 2 2
      packages/opencode/src/installation/index.ts
  30. 6 1
      packages/opencode/src/server/server.ts
  31. 233 1
      packages/opencode/test/config/config.test.ts
  32. 1 1
      packages/plugin/package.json
  33. 1 1
      packages/sdk/js/package.json
  34. 3 1
      packages/sdk/js/src/v2/client.ts
  35. 1 1
      packages/slack/package.json
  36. 1 1
      packages/ui/package.json
  37. 7 2
      packages/ui/src/hooks/use-filtered-list.tsx
  38. 14 0
      packages/ui/src/styles/base.css
  39. 3 0
      packages/ui/src/theme/default-themes.ts
  40. 1 0
      packages/ui/src/theme/index.ts
  41. 131 0
      packages/ui/src/theme/themes/nightowl.json
  42. 1 1
      packages/util/package.json
  43. 1 1
      packages/web/package.json
  44. 59 6
      packages/web/src/content/docs/config.mdx
  45. 3 1
      packages/web/src/content/docs/index.mdx
  46. 23 0
      packages/web/src/content/docs/mcp-servers.mdx
  47. 1 1
      sdks/vscode/package.json

+ 1 - 2
.github/workflows/publish.yml

@@ -172,8 +172,7 @@ jobs:
       - name: Install tauri-cli from portable appimage branch
         if: contains(matrix.settings.host, 'ubuntu')
         run: |
-          # cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
-          cargo install tauri-cli --git https://github.com/brendonovich/tauri --branch appimage-sidecar-binaries --force
+          cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
           echo "Installed tauri-cli version:"
           cargo tauri --version
 

+ 2 - 1
README.md

@@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
 npm i -g opencode-ai@latest        # or bun/pnpm/yarn
 scoop bucket add extras; scoop install extras/opencode  # Windows
 choco install opencode             # Windows
-brew install opencode              # macOS and Linux
+brew install anomalyco/tap/opencode # macOS and Linux (recommended, always up to date)
+brew install opencode              # macOS and Linux (official brew formula, updated less frequently)
 paru -S opencode-bin               # Arch Linux
 mise use -g opencode               # Any OS
 nix run nixpkgs#opencode           # or github:anomalyco/opencode for latest dev branch

+ 2 - 1
README.zh-CN.md

@@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
 npm i -g opencode-ai@latest        # 也可使用 bun/pnpm/yarn
 scoop bucket add extras; scoop install extras/opencode  # Windows
 choco install opencode             # Windows
-brew install opencode              # macOS 和 Linux
+brew install anomalyco/tap/opencode # macOS 和 Linux(推荐,始终保持最新)
+brew install opencode              # macOS 和 Linux(官方 brew formula,更新频率较低)
 paru -S opencode-bin               # Arch Linux
 mise use -g opencode               # 任意系统
 nix run nixpkgs#opencode           # 或用 github:anomalyco/opencode 获取最新 dev 分支

+ 2 - 1
README.zh-TW.md

@@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
 npm i -g opencode-ai@latest        # 也可使用 bun/pnpm/yarn
 scoop bucket add extras; scoop install extras/opencode  # Windows
 choco install opencode             # Windows
-brew install opencode              # macOS 與 Linux
+brew install anomalyco/tap/opencode # macOS 與 Linux(推薦,始終保持最新)
+brew install opencode              # macOS 與 Linux(官方 brew formula,更新頻率較低)
 paru -S opencode-bin               # Arch Linux
 mise use -g github:anomalyco/opencode    # 任何作業系統
 nix run nixpkgs#opencode           # 或使用 github:anomalyco/opencode 以取得最新開發分支

+ 1 - 0
STATS.md

@@ -193,3 +193,4 @@
 | 2026-01-04 | 1,672,656 (+39,702)  | 1,339,883 (+7,969)  | 3,012,539 (+62,560)  |
 | 2026-01-05 | 1,738,171 (+65,515)  | 1,353,043 (+13,160) | 3,091,214 (+78,675)  |
 | 2026-01-06 | 1,960,988 (+222,817) | 1,377,377 (+24,334) | 3,338,365 (+247,151) |
+| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |

+ 31 - 25
bun.lock

@@ -22,7 +22,7 @@
     },
     "packages/app": {
       "name": "@opencode-ai/app",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@opencode-ai/sdk": "workspace:*",
@@ -70,7 +70,7 @@
     },
     "packages/console/app": {
       "name": "@opencode-ai/console-app",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@cloudflare/vite-plugin": "1.15.2",
         "@ibm/plex": "6.4.1",
@@ -98,7 +98,7 @@
     },
     "packages/console/core": {
       "name": "@opencode-ai/console-core",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@aws-sdk/client-sts": "3.782.0",
         "@jsx-email/render": "1.1.1",
@@ -125,7 +125,7 @@
     },
     "packages/console/function": {
       "name": "@opencode-ai/console-function",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@ai-sdk/anthropic": "2.0.0",
         "@ai-sdk/openai": "2.0.2",
@@ -149,7 +149,7 @@
     },
     "packages/console/mail": {
       "name": "@opencode-ai/console-mail",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@jsx-email/all": "2.2.3",
         "@jsx-email/cli": "1.4.3",
@@ -173,7 +173,7 @@
     },
     "packages/desktop": {
       "name": "@opencode-ai/desktop",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@opencode-ai/app": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
@@ -202,7 +202,7 @@
     },
     "packages/enterprise": {
       "name": "@opencode-ai/enterprise",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/util": "workspace:*",
@@ -231,7 +231,7 @@
     },
     "packages/function": {
       "name": "@opencode-ai/function",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@octokit/auth-app": "8.0.1",
         "@octokit/rest": "catalog:",
@@ -247,7 +247,7 @@
     },
     "packages/opencode": {
       "name": "opencode",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "bin": {
         "opencode": "./bin/opencode",
       },
@@ -277,7 +277,7 @@
         "@clack/prompts": "1.0.0-alpha.1",
         "@hono/standard-validator": "0.1.5",
         "@hono/zod-validator": "catalog:",
-        "@modelcontextprotocol/sdk": "1.15.1",
+        "@modelcontextprotocol/sdk": "1.25.2",
         "@octokit/graphql": "9.0.2",
         "@octokit/rest": "catalog:",
         "@openauthjs/openauth": "catalog:",
@@ -350,7 +350,7 @@
     },
     "packages/plugin": {
       "name": "@opencode-ai/plugin",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "zod": "catalog:",
@@ -370,7 +370,7 @@
     },
     "packages/sdk/js": {
       "name": "@opencode-ai/sdk",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "devDependencies": {
         "@hey-api/openapi-ts": "0.88.1",
         "@tsconfig/node22": "catalog:",
@@ -381,7 +381,7 @@
     },
     "packages/slack": {
       "name": "@opencode-ai/slack",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "@slack/bolt": "^3.17.1",
@@ -394,7 +394,7 @@
     },
     "packages/ui": {
       "name": "@opencode-ai/ui",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@opencode-ai/sdk": "workspace:*",
@@ -433,7 +433,7 @@
     },
     "packages/util": {
       "name": "@opencode-ai/util",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "zod": "catalog:",
       },
@@ -444,7 +444,7 @@
     },
     "packages/web": {
       "name": "@opencode-ai/web",
-      "version": "1.1.4",
+      "version": "1.1.6",
       "dependencies": {
         "@astrojs/cloudflare": "12.6.3",
         "@astrojs/markdown-remark": "6.3.1",
@@ -911,6 +911,8 @@
 
     "@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/codegen-core": "^0.3.3", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-x/nDTupOnV9VuSeNIiJpgIpc915GHduhyseJeMTnI0JMsXaObmpa0rgPr3ASVEYMLgpvqozIEG1RTOOnal6zLQ=="],
 
+    "@hono/node-server": ["@hono/[email protected]", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="],
+
     "@hono/standard-validator": ["@hono/[email protected]", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
 
     "@hono/zod-validator": ["@hono/[email protected]", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g=="],
@@ -1095,7 +1097,7 @@
 
     "@mixmark-io/domino": ["@mixmark-io/[email protected]", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
 
-    "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.15.1", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w=="],
+    "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="],
 
     "@motionone/animation": ["@motionone/[email protected]", "", { "dependencies": { "@motionone/easing": "^10.18.0", "@motionone/types": "^10.17.1", "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw=="],
 
@@ -1903,7 +1905,9 @@
 
     "ai": ["[email protected]", "", { "dependencies": { "@ai-sdk/gateway": "2.0.12", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ=="],
 
-    "ajv": ["[email protected]", "", { "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=="],
+    "ajv": ["[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
+
+    "ajv-formats": ["[email protected]", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
 
     "ansi-align": ["[email protected]", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
 
@@ -2407,7 +2411,7 @@
 
     "fast-glob": ["[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
 
-    "fast-json-stable-stringify": ["[email protected]", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+    "fast-uri": ["[email protected]", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
 
     "fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="],
 
@@ -2783,7 +2787,9 @@
 
     "json-schema": ["[email protected]", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
 
-    "json-schema-traverse": ["[email protected]", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+    "json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+
+    "json-schema-typed": ["[email protected]", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
 
     "json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
 
@@ -3385,6 +3391,8 @@
 
     "remeda": ["[email protected]", "", { "dependencies": { "type-fest": "^4.41.0" } }, "sha512-lmNNwtaC6Co4m0WTTNoZ/JlpjEqAjPZO0+czC9YVRQUpkbS4x8Hmh+Mn9HPfJfiXqUQ5IXXgSXSOB2pBKAytdA=="],
 
+    "require-from-string": ["[email protected]", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
+
     "reselect": ["[email protected]", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
 
     "resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
@@ -3759,8 +3767,6 @@
 
     "update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
 
-    "uri-js": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
-
     "url": ["[email protected]", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
 
     "use-callback-ref": ["[email protected]", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
@@ -4059,9 +4065,11 @@
 
     "@modelcontextprotocol/sdk/express": ["[email protected]", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
 
+    "@modelcontextprotocol/sdk/jose": ["[email protected]", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
+
     "@modelcontextprotocol/sdk/raw-body": ["[email protected]", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
 
-    "@modelcontextprotocol/sdk/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
+    "@modelcontextprotocol/sdk/zod-to-json-schema": ["[email protected]", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
 
     "@octokit/auth-app/@octokit/request": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
 
@@ -4405,8 +4413,6 @@
 
     "unifont/ofetch": ["[email protected]", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
 
-    "uri-js/punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
     "utif2/pako": ["[email protected]", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
 
     "vitest/tinyexec": ["[email protected]", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],

+ 1 - 1
nix/hashes.json

@@ -1,3 +1,3 @@
 {
-  "nodeModules": "sha256-WHqX159BYPSHBFmxxkTrWPytBzTSTcWkoEywAxP58kI="
+  "nodeModules": "sha256-rNGq0yjL5ZHYVg+zyV4nFPug4gqhKhyOnfebaufyd34="
 }

+ 1 - 1
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/app",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "description": "",
   "type": "module",
   "exports": {

+ 6 - 7
packages/app/src/components/prompt-input.tsx

@@ -248,6 +248,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
     }
   }
 
+  const isFocused = createFocusSignal(() => editorRef)
+
   createEffect(() => {
     params.id
     editorRef.focus()
@@ -258,7 +260,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
     onCleanup(() => clearInterval(interval))
   })
 
-  const isFocused = createFocusSignal(() => editorRef)
   const [composing, setComposing] = createSignal(false)
   const isImeComposing = (event: KeyboardEvent) => event.isComposing || composing() || event.keyCode === 229
 
@@ -292,12 +293,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
     const clipboardData = event.clipboardData
     if (!clipboardData) return
 
+    event.preventDefault()
+    event.stopPropagation()
+
     const items = Array.from(clipboardData.items)
     const imageItems = items.filter((item) => ACCEPTED_FILE_TYPES.includes(item.type))
 
     if (imageItems.length > 0) {
-      event.preventDefault()
-      event.stopPropagation()
       for (const item of imageItems) {
         const file = item.getAsFile()
         if (file) await addImageAttachment(file)
@@ -305,8 +307,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
       return
     }
 
-    event.preventDefault()
-    event.stopPropagation()
     const plainText = clipboardData.getData("text/plain") ?? ""
     addPart({ type: "text", content: plainText, start: 0, end: 0 })
   }
@@ -347,13 +347,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
   }
 
   onMount(() => {
-    editorRef.addEventListener("paste", handlePaste)
     document.addEventListener("dragover", handleGlobalDragOver)
     document.addEventListener("dragleave", handleGlobalDragLeave)
     document.addEventListener("drop", handleGlobalDrop)
   })
   onCleanup(() => {
-    editorRef.removeEventListener("paste", handlePaste)
     document.removeEventListener("dragover", handleGlobalDragOver)
     document.removeEventListener("dragleave", handleGlobalDragLeave)
     document.removeEventListener("drop", handleGlobalDrop)
@@ -1508,6 +1506,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
             }}
             contenteditable="true"
             onInput={handleInput}
+            onPaste={handlePaste}
             onCompositionStart={() => setComposing(true)}
             onCompositionEnd={() => setComposing(false)}
             onKeyDown={handleKeyDown}

+ 1 - 1
packages/console/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-app",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/console/core/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/console-core",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/console/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-function",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 1 - 1
packages/console/mail/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-mail",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "dependencies": {
     "@jsx-email/all": "2.2.3",
     "@jsx-email/cli": "1.4.3",

+ 1 - 1
packages/desktop/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@opencode-ai/desktop",
   "private": true,
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 14 - 0
packages/desktop/src-tauri/entitlements.plist

@@ -12,5 +12,19 @@
     <true/>
     <key>com.apple.security.cs.disable-library-validation</key>
     <true/>
+    <key>com.apple.security.automation.apple-events</key>
+    <true/>
+    <key>com.apple.security.device.audio-input</key>
+    <true/>
+    <key>com.apple.security.device.camera</key>
+    <true/>
+    <key>com.apple.security.personal-information.addressbook</key>
+    <true/>
+    <key>com.apple.security.personal-information.calendars</key>
+    <true/>
+    <key>com.apple.security.personal-information.location</key>
+    <true/>
+    <key>com.apple.security.personal-information.photos-library</key>
+    <true/>
 </dict>
 </plist>

+ 1 - 1
packages/desktop/src-tauri/src/lib.rs

@@ -139,7 +139,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild {
             .args([
                 "-il",
                 "-c",
-                &format!("{} serve --port={}", sidecar.display(), port),
+                &format!("\"{}\" serve --port={}", sidecar.display(), port),
             ])
             .spawn()
             .expect("Failed to spawn opencode")

+ 7 - 4
packages/desktop/src/index.tsx

@@ -18,6 +18,7 @@ import { Suspense, createResource, ParentProps } from "solid-js"
 import { UPDATER_ENABLED } from "./updater"
 import { createMenu } from "./menu"
 import pkg from "../package.json"
+import { Show } from "solid-js"
 
 const root = document.getElementById("root")
 if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
@@ -288,7 +289,9 @@ function ServerGate(props: ParentProps) {
   })
 
   return (
-    <Suspense
+    // Not using suspense as not all components are compatible with it (undefined refs)
+    <Show
+      when={status.state !== "pending"}
       fallback={
         <div class="h-screen w-screen flex flex-col items-center justify-center bg-background-base">
           <Logo class="w-xl opacity-12 animate-pulse" />
@@ -296,9 +299,9 @@ function ServerGate(props: ParentProps) {
         </div>
       }
     >
-      {/* Triggers suspense/error boundaries without rendering the returned value */}
+      {/* Trigger error boundary without rendering the returned value */}
       {(status(), null)}
-      <Suspense>{props.children}</Suspense>
-    </Suspense>
+      {props.children}
+    </Show>
   )
 }

+ 1 - 1
packages/enterprise/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/enterprise",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 6 - 6
packages/extensions/zed/extension.toml

@@ -1,7 +1,7 @@
 id = "opencode"
 name = "OpenCode"
 description = "The open source coding agent."
-version = "1.1.4"
+version = "1.1.6"
 schema_version = 1
 authors = ["Anomaly"]
 repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
 icon = "./icons/opencode.svg"
 
 [agent_servers.opencode.targets.darwin-aarch64]
-archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-darwin-arm64.zip"
+archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-darwin-arm64.zip"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.darwin-x86_64]
-archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-darwin-x64.zip"
+archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-darwin-x64.zip"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.linux-aarch64]
-archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-linux-arm64.tar.gz"
+archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-linux-arm64.tar.gz"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.linux-x86_64]
-archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-linux-x64.tar.gz"
+archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-linux-x64.tar.gz"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.windows-x86_64]
-archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-windows-x64.zip"
+archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-windows-x64.zip"
 cmd = "./opencode.exe"
 args = ["acp"]

+ 1 - 1
packages/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/function",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 2 - 2
packages/opencode/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "name": "opencode",
   "type": "module",
   "license": "MIT",
@@ -72,7 +72,7 @@
     "@clack/prompts": "1.0.0-alpha.1",
     "@hono/standard-validator": "0.1.5",
     "@hono/zod-validator": "catalog:",
-    "@modelcontextprotocol/sdk": "1.15.1",
+    "@modelcontextprotocol/sdk": "1.25.2",
     "@octokit/graphql": "9.0.2",
     "@octokit/rest": "catalog:",
     "@openauthjs/openauth": "catalog:",

+ 1 - 0
packages/opencode/src/agent/agent.ts

@@ -144,6 +144,7 @@ export namespace Agent {
         options: {},
         native: true,
         hidden: true,
+        temperature: 0.5,
         permission: PermissionNext.merge(
           defaults,
           PermissionNext.fromConfig({

+ 11 - 4
packages/opencode/src/agent/prompt/title.txt

@@ -12,8 +12,11 @@ Your output must be:
 </task>
 
 <rules>
+- Title must be grammatically correct and read naturally - no word salad
+- Never include tool names in the title (e.g. "read tool", "bash tool", "edit tool")
 - Focus on the main topic or question the user needs to retrieve
-- Use -ing verbs for actions (Debugging, Implementing, Analyzing)
+- Vary your phrasing - avoid repetitive patterns like always starting with "Analyzing"
+- When a file is mentioned, focus on WHAT the user wants to do WITH the file, not just that they shared it
 - Keep exact: technical terms, numbers, filenames, HTTP codes
 - Remove: the, this, my, a, an
 - Never assume tech stack
@@ -29,8 +32,12 @@ Your output must be:
 <examples>
 "debug 500 errors in production" → Debugging production 500 errors
 "refactor user service" → Refactoring user service
-"why is app.js failing" → Analyzing app.js failure
-"implement rate limiting" → Implementing rate limiting
-"how do I connect postgres to my API" → Connecting Postgres to API
+"why is app.js failing" → app.js failure investigation
+"implement rate limiting" → Rate limiting implementation
+"how do I connect postgres to my API" → Postgres API connection
 "best practices for React hooks" → React hooks best practices
+"@src/auth.ts can you add refresh token support" → Auth refresh token support
+"@utils/parser.ts this is broken" → Parser bug fix
+"look at @config.json" → Config review
+"@App.tsx add dark mode toggle" → Dark mode toggle in App
 </examples>

+ 4 - 2
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

@@ -653,8 +653,10 @@ export function Autocomplete(props: {
   })
 
   const height = createMemo(() => {
-    if (options().length) return Math.min(10, options().length)
-    return 1
+    const count = options().length || 1
+    if (!store.visible) return Math.min(10, count)
+    positionTick()
+    return Math.min(10, count, Math.max(1, props.anchor().y))
   })
 
   let scroll: ScrollBoxRenderable

+ 10 - 9
packages/opencode/src/cli/cmd/tui/context/theme.tsx

@@ -288,11 +288,11 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
 
     createEffect(() => {
       const theme = sync.data.config.theme
-      console.log("theme", theme)
       if (theme) setStore("active", theme)
     })
 
-    createEffect(() => {
+    function init() {
+      resolveSystemTheme()
       getCustomThemes()
         .then((custom) => {
           setStore(
@@ -309,15 +309,18 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
             setStore("ready", true)
           }
         })
-    })
+    }
+
+    onMount(init)
 
     function resolveSystemTheme() {
-      console.log("resolved system theme")
+      console.log("resolveSystemTheme")
       renderer
         .getPalette({
           size: 16,
         })
         .then((colors) => {
+          console.log(colors.palette)
           if (!colors.palette[0]) {
             if (store.active === "system") {
               setStore(
@@ -341,11 +344,9 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
     }
 
     const renderer = useRenderer()
-    resolveSystemTheme()
-
-    const sdk = useSDK()
-    sdk.event.on("server.instance.disposed", () => {
-      resolveSystemTheme()
+    process.on("SIGUSR2", async () => {
+      renderer.clearPaletteCache()
+      init()
     })
 
     const values = createMemo(() => {

+ 4 - 0
packages/opencode/src/cli/cmd/tui/context/theme/lucent-orng.json

@@ -61,6 +61,10 @@
       "dark": "darkStep11",
       "light": "lightStep11"
     },
+    "selectedListItemText": {
+      "dark": "#0a0a0a",
+      "light": "#ffffff"
+    },
     "background": {
       "dark": "transparent",
       "light": "transparent"

+ 4 - 0
packages/opencode/src/cli/cmd/tui/context/theme/orng.json

@@ -77,6 +77,10 @@
       "dark": "darkStep11",
       "light": "lightStep11"
     },
+    "selectedListItemText": {
+      "dark": "#0a0a0a",
+      "light": "#ffffff"
+    },
     "background": {
       "dark": "darkStep1",
       "light": "lightStep1"

+ 29 - 10
packages/opencode/src/config/config.ts

@@ -37,14 +37,40 @@ export namespace Config {
 
   export const state = Instance.state(async () => {
     const auth = await Auth.all()
-    let result = await global()
 
-    // Override with custom config if provided
+    // Load remote/well-known config first as the base layer (lowest precedence)
+    // This allows organizations to provide default configs that users can override
+    let result: Info = {}
+    for (const [key, value] of Object.entries(auth)) {
+      if (value.type === "wellknown") {
+        process.env[value.key] = value.token
+        log.debug("fetching remote config", { url: `${key}/.well-known/opencode` })
+        const response = await fetch(`${key}/.well-known/opencode`)
+        if (!response.ok) {
+          throw new Error(`failed to fetch remote config from ${key}: ${response.status}`)
+        }
+        const wellknown = (await response.json()) as any
+        const remoteConfig = wellknown.config ?? {}
+        // Add $schema to prevent load() from trying to write back to a non-existent file
+        if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json"
+        result = mergeConfigConcatArrays(
+          result,
+          await load(JSON.stringify(remoteConfig), `${key}/.well-known/opencode`),
+        )
+        log.debug("loaded remote config from well-known", { url: key })
+      }
+    }
+
+    // Global user config overrides remote config
+    result = mergeConfigConcatArrays(result, await global())
+
+    // Custom config path overrides global
     if (Flag.OPENCODE_CONFIG) {
       result = mergeConfigConcatArrays(result, await loadFile(Flag.OPENCODE_CONFIG))
       log.debug("loaded custom config", { path: Flag.OPENCODE_CONFIG })
     }
 
+    // Project config has highest precedence (overrides global and remote)
     for (const file of ["opencode.jsonc", "opencode.json"]) {
       const found = await Filesystem.findUp(file, Instance.directory, Instance.worktree)
       for (const resolved of found.toReversed()) {
@@ -52,19 +78,12 @@ export namespace Config {
       }
     }
 
+    // Inline config content has highest precedence
     if (Flag.OPENCODE_CONFIG_CONTENT) {
       result = mergeConfigConcatArrays(result, JSON.parse(Flag.OPENCODE_CONFIG_CONTENT))
       log.debug("loaded custom config from OPENCODE_CONFIG_CONTENT")
     }
 
-    for (const [key, value] of Object.entries(auth)) {
-      if (value.type === "wellknown") {
-        process.env[value.key] = value.token
-        const wellknown = (await fetch(`${key}/.well-known/opencode`).then((x) => x.json())) as any
-        result = mergeConfigConcatArrays(result, await load(JSON.stringify(wellknown.config ?? {}), process.cwd()))
-      }
-    }
-
     result.agent = result.agent || {}
     result.mode = result.mode || {}
     result.plugin = result.plugin || []

+ 2 - 2
packages/opencode/src/installation/index.ts

@@ -111,8 +111,8 @@ export namespace Installation {
   )
 
   async function getBrewFormula() {
-    const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).quiet().text()
-    if (tapFormula.includes("opencode")) return "sst/tap/opencode"
+    const tapFormula = await $`brew list --formula anomalyco/tap/opencode`.throws(false).quiet().text()
+    if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode"
     const coreFormula = await $`brew list --formula opencode`.throws(false).quiet().text()
     if (coreFormula.includes("opencode")) return "opencode"
     return "opencode"

+ 6 - 1
packages/opencode/src/server/server.ts

@@ -246,7 +246,12 @@ export namespace Server {
         },
       )
       .use(async (c, next) => {
-        const directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
+        let directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
+        try {
+          directory = decodeURIComponent(directory)
+        } catch {
+          // fallback to original value
+        }
         return Instance.provide({
           directory,
           init: InstanceBootstrap,

+ 233 - 1
packages/opencode/test/config/config.test.ts

@@ -1,6 +1,7 @@
-import { test, expect } from "bun:test"
+import { test, expect, mock, afterEach } from "bun:test"
 import { Config } from "../../src/config/config"
 import { Instance } from "../../src/project/instance"
+import { Auth } from "../../src/auth"
 import { tmpdir } from "../fixture/fixture"
 import path from "path"
 import fs from "fs/promises"
@@ -913,3 +914,234 @@ test("permission config preserves key order", async () => {
     },
   })
 })
+
+// MCP config merging tests
+
+test("project config can override MCP server enabled status", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      // Simulates a base config (like from remote .well-known) with disabled MCP
+      await Bun.write(
+        path.join(dir, "opencode.jsonc"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            jira: {
+              type: "remote",
+              url: "https://jira.example.com/mcp",
+              enabled: false,
+            },
+            wiki: {
+              type: "remote",
+              url: "https://wiki.example.com/mcp",
+              enabled: false,
+            },
+          },
+        }),
+      )
+      // Project config enables just jira
+      await Bun.write(
+        path.join(dir, "opencode.json"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            jira: {
+              type: "remote",
+              url: "https://jira.example.com/mcp",
+              enabled: true,
+            },
+          },
+        }),
+      )
+    },
+  })
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await Config.get()
+      // jira should be enabled (overridden by project config)
+      expect(config.mcp?.jira).toEqual({
+        type: "remote",
+        url: "https://jira.example.com/mcp",
+        enabled: true,
+      })
+      // wiki should still be disabled (not overridden)
+      expect(config.mcp?.wiki).toEqual({
+        type: "remote",
+        url: "https://wiki.example.com/mcp",
+        enabled: false,
+      })
+    },
+  })
+})
+
+test("MCP config deep merges preserving base config properties", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      // Base config with full MCP definition
+      await Bun.write(
+        path.join(dir, "opencode.jsonc"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            myserver: {
+              type: "remote",
+              url: "https://myserver.example.com/mcp",
+              enabled: false,
+              headers: {
+                "X-Custom-Header": "value",
+              },
+            },
+          },
+        }),
+      )
+      // Override just enables it, should preserve other properties
+      await Bun.write(
+        path.join(dir, "opencode.json"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            myserver: {
+              type: "remote",
+              url: "https://myserver.example.com/mcp",
+              enabled: true,
+            },
+          },
+        }),
+      )
+    },
+  })
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await Config.get()
+      expect(config.mcp?.myserver).toEqual({
+        type: "remote",
+        url: "https://myserver.example.com/mcp",
+        enabled: true,
+        headers: {
+          "X-Custom-Header": "value",
+        },
+      })
+    },
+  })
+})
+
+test("local .opencode config can override MCP from project config", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      // Project config with disabled MCP
+      await Bun.write(
+        path.join(dir, "opencode.json"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            docs: {
+              type: "remote",
+              url: "https://docs.example.com/mcp",
+              enabled: false,
+            },
+          },
+        }),
+      )
+      // Local .opencode directory config enables it
+      const opencodeDir = path.join(dir, ".opencode")
+      await fs.mkdir(opencodeDir, { recursive: true })
+      await Bun.write(
+        path.join(opencodeDir, "opencode.json"),
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            docs: {
+              type: "remote",
+              url: "https://docs.example.com/mcp",
+              enabled: true,
+            },
+          },
+        }),
+      )
+    },
+  })
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await Config.get()
+      expect(config.mcp?.docs?.enabled).toBe(true)
+    },
+  })
+})
+
+test("project config overrides remote well-known config", async () => {
+  const originalFetch = globalThis.fetch
+  let fetchedUrl: string | undefined
+  const mockFetch = mock((url: string | URL | Request) => {
+    const urlStr = url.toString()
+    if (urlStr.includes(".well-known/opencode")) {
+      fetchedUrl = urlStr
+      return Promise.resolve(
+        new Response(
+          JSON.stringify({
+            config: {
+              mcp: {
+                jira: {
+                  type: "remote",
+                  url: "https://jira.example.com/mcp",
+                  enabled: false,
+                },
+              },
+            },
+          }),
+          { status: 200 },
+        ),
+      )
+    }
+    return originalFetch(url)
+  })
+  globalThis.fetch = mockFetch as unknown as typeof fetch
+
+  const originalAuthAll = Auth.all
+  Auth.all = mock(() =>
+    Promise.resolve({
+      "https://example.com": {
+        type: "wellknown" as const,
+        key: "TEST_TOKEN",
+        token: "test-token",
+      },
+    }),
+  )
+
+  try {
+    await using tmp = await tmpdir({
+      git: true,
+      init: async (dir) => {
+        // Project config enables jira (overriding remote default)
+        await Bun.write(
+          path.join(dir, "opencode.json"),
+          JSON.stringify({
+            $schema: "https://opencode.ai/config.json",
+            mcp: {
+              jira: {
+                type: "remote",
+                url: "https://jira.example.com/mcp",
+                enabled: true,
+              },
+            },
+          }),
+        )
+      },
+    })
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const config = await Config.get()
+        // Verify fetch was called for wellknown config
+        expect(fetchedUrl).toBe("https://example.com/.well-known/opencode")
+        // Project config (enabled: true) should override remote (enabled: false)
+        expect(config.mcp?.jira?.enabled).toBe(true)
+      },
+    })
+  } finally {
+    globalThis.fetch = originalFetch
+    Auth.all = originalAuthAll
+  }
+})

+ 1 - 1
packages/plugin/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/plugin",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/sdk/js/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/sdk",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 3 - 1
packages/sdk/js/src/v2/client.ts

@@ -19,9 +19,11 @@ export function createOpencodeClient(config?: Config & { directory?: string }) {
   }
 
   if (config?.directory) {
+    const isNonASCII = /[^\x00-\x7F]/.test(config.directory)
+    const encodedDirectory = isNonASCII ? encodeURIComponent(config.directory) : config.directory
     config.headers = {
       ...config.headers,
-      "x-opencode-directory": config.directory,
+      "x-opencode-directory": encodedDirectory,
     }
   }
 

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/slack",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "scripts": {

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/ui",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "type": "module",
   "license": "MIT",
   "exports": {

+ 7 - 2
packages/ui/src/hooks/use-filtered-list.tsx

@@ -1,6 +1,6 @@
 import fuzzysort from "fuzzysort"
 import { entries, flatMap, groupBy, map, pipe } from "remeda"
-import { createMemo, createResource } from "solid-js"
+import { createEffect, createMemo, createResource, on } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createList } from "solid-list"
 
@@ -86,9 +86,14 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
     }
   }
 
+  createEffect(
+    on(grouped, () => {
+      reset()
+    }),
+  )
+
   const onInput = (value: string) => {
     setStore("filter", value)
-    reset()
   }
 
   return {

+ 14 - 0
packages/ui/src/styles/base.css

@@ -372,3 +372,17 @@ input:where([type="button"], [type="reset"], [type="submit"]),
 [hidden]:where(:not([hidden="until-found"])) {
   display: none !important;
 }
+
+/*
+  Prevent iOS Safari from auto-zooming on input focus.
+  iOS WebKit zooms on any input with font-size < 16px as an accessibility feature.
+*/
+
+@media (hover: none) and (pointer: coarse) {
+  input,
+  select,
+  textarea,
+  [contenteditable="true"] {
+    font-size: 16px !important;
+  }
+}

+ 3 - 0
packages/ui/src/theme/default-themes.ts

@@ -9,6 +9,7 @@ import catppuccinThemeJson from "./themes/catppuccin.json"
 import ayuThemeJson from "./themes/ayu.json"
 import oneDarkProThemeJson from "./themes/onedarkpro.json"
 import shadesOfPurpleThemeJson from "./themes/shadesofpurple.json"
+import nightowlThemeJson from "./themes/nightowl.json"
 
 export const oc1Theme = oc1ThemeJson as DesktopTheme
 export const tokyonightTheme = tokyoThemeJson as DesktopTheme
@@ -20,6 +21,7 @@ export const catppuccinTheme = catppuccinThemeJson as DesktopTheme
 export const ayuTheme = ayuThemeJson as DesktopTheme
 export const oneDarkProTheme = oneDarkProThemeJson as DesktopTheme
 export const shadesOfPurpleTheme = shadesOfPurpleThemeJson as DesktopTheme
+export const nightowlTheme = nightowlThemeJson as DesktopTheme
 
 export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
   "oc-1": oc1Theme,
@@ -32,4 +34,5 @@ export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
   ayu: ayuTheme,
   onedarkpro: oneDarkProTheme,
   shadesofpurple: shadesOfPurpleTheme,
+  nightowl: nightowlTheme,
 }

+ 1 - 0
packages/ui/src/theme/index.ts

@@ -41,4 +41,5 @@ export {
   ayuTheme,
   oneDarkProTheme,
   shadesOfPurpleTheme,
+  nightowlTheme,
 } from "./default-themes"

+ 131 - 0
packages/ui/src/theme/themes/nightowl.json

@@ -0,0 +1,131 @@
+{
+  "$schema": "https://opencode.ai/desktop-theme.json",
+  "name": "Night Owl",
+  "id": "nightowl",
+  "light": {
+    "seeds": {
+      "neutral": "#f0f0f0",
+      "primary": "#4876d6",
+      "success": "#2aa298",
+      "warning": "#c96765",
+      "error": "#de3d3b",
+      "info": "#4876d6",
+      "interactive": "#4876d6",
+      "diffAdd": "#2aa298",
+      "diffDelete": "#de3d3b"
+    },
+    "overrides": {
+      "background-base": "#fbfbfb",
+      "background-weak": "#f0f0f0",
+      "background-strong": "#ffffff",
+      "background-stronger": "#ffffff",
+      "border-weak-base": "#d9d9d9",
+      "border-weak-hover": "#cccccc",
+      "border-weak-active": "#bfbfbf",
+      "border-weak-selected": "#4876d6",
+      "border-weak-disabled": "#e6e6e6",
+      "border-weak-focus": "#4876d6",
+      "border-base": "#c0c0c0",
+      "border-hover": "#b3b3b3",
+      "border-active": "#a6a6a6",
+      "border-selected": "#4876d6",
+      "border-disabled": "#d9d9d9",
+      "border-focus": "#4876d6",
+      "border-strong-base": "#90a7b2",
+      "border-strong-hover": "#7d9aa6",
+      "border-strong-active": "#6a8d9a",
+      "border-strong-selected": "#4876d6",
+      "border-strong-disabled": "#c0c0c0",
+      "border-strong-focus": "#4876d6",
+      "surface-diff-add-base": "#eaf8f6",
+      "surface-diff-delete-base": "#fbe9e9",
+      "surface-diff-hidden-base": "#e8f0fc",
+      "text-base": "#403f53",
+      "text-weak": "#7a8181",
+      "text-strong": "#1a1a1a",
+      "syntax-string": "#c96765",
+      "syntax-primitive": "#aa0982",
+      "syntax-property": "#4876d6",
+      "syntax-type": "#994cc3",
+      "syntax-constant": "#2aa298",
+      "syntax-info": "#4876d6",
+      "markdown-heading": "#4876d6",
+      "markdown-text": "#403f53",
+      "markdown-link": "#4876d6",
+      "markdown-link-text": "#2aa298",
+      "markdown-code": "#2aa298",
+      "markdown-block-quote": "#7a8181",
+      "markdown-emph": "#994cc3",
+      "markdown-strong": "#c96765",
+      "markdown-horizontal-rule": "#90a7b2",
+      "markdown-list-item": "#4876d6",
+      "markdown-list-enumeration": "#2aa298",
+      "markdown-image": "#4876d6",
+      "markdown-image-text": "#2aa298",
+      "markdown-code-block": "#403f53"
+    }
+  },
+  "dark": {
+    "seeds": {
+      "neutral": "#011627",
+      "primary": "#82aaff",
+      "success": "#c5e478",
+      "warning": "#ecc48d",
+      "error": "#ef5350",
+      "info": "#82aaff",
+      "interactive": "#82aaff",
+      "diffAdd": "#c5e478",
+      "diffDelete": "#ef5350"
+    },
+    "overrides": {
+      "background-base": "#011627",
+      "background-weak": "#0b253a",
+      "background-strong": "#001122",
+      "background-stronger": "#000c17",
+      "border-weak-base": "#1d3b53",
+      "border-weak-hover": "#234561",
+      "border-weak-active": "#2a506f",
+      "border-weak-selected": "#82aaff",
+      "border-weak-disabled": "#0f2132",
+      "border-weak-focus": "#82aaff",
+      "border-base": "#3a5a75",
+      "border-hover": "#456785",
+      "border-active": "#507494",
+      "border-selected": "#82aaff",
+      "border-disabled": "#1a3347",
+      "border-focus": "#82aaff",
+      "border-strong-base": "#5f7e97",
+      "border-strong-hover": "#6e8da6",
+      "border-strong-active": "#7d9cb5",
+      "border-strong-selected": "#82aaff",
+      "border-strong-disabled": "#2c4a63",
+      "border-strong-focus": "#82aaff",
+      "surface-diff-add-base": "#0a2e1a",
+      "surface-diff-delete-base": "#2d1b1b",
+      "surface-diff-hidden-base": "#0b253a",
+      "text-base": "#d6deeb",
+      "text-weak": "#5f7e97",
+      "text-strong": "#ffffff",
+      "syntax-string": "#ecc48d",
+      "syntax-primitive": "#f78c6c",
+      "syntax-property": "#82aaff",
+      "syntax-type": "#c5e478",
+      "syntax-constant": "#7fdbca",
+      "syntax-info": "#82aaff",
+      "markdown-heading": "#82aaff",
+      "markdown-text": "#d6deeb",
+      "markdown-link": "#82aaff",
+      "markdown-link-text": "#7fdbca",
+      "markdown-code": "#c5e478",
+      "markdown-block-quote": "#5f7e97",
+      "markdown-emph": "#c792ea",
+      "markdown-strong": "#ecc48d",
+      "markdown-horizontal-rule": "#5f7e97",
+      "markdown-list-item": "#82aaff",
+      "markdown-list-enumeration": "#7fdbca",
+      "markdown-image": "#82aaff",
+      "markdown-image-text": "#7fdbca",
+      "markdown-code-block": "#d6deeb"
+    }
+  }
+}

+ 1 - 1
packages/util/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/util",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "private": true,
   "type": "module",
   "license": "MIT",

+ 1 - 1
packages/web/package.json

@@ -2,7 +2,7 @@
   "name": "@opencode-ai/web",
   "type": "module",
   "license": "MIT",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "scripts": {
     "dev": "astro dev",
     "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",

+ 59 - 6
packages/web/src/content/docs/config.mdx

@@ -32,21 +32,74 @@ different order of precedence.
 Configuration files are **merged together**, not replaced.
 :::
 
-Configuration files are merged together, not replaced. Settings from the following config locations are combined. Where later configs override earlier ones only for conflicting keys. Non-conflicting settings from all configs are preserved.
+Configuration files are merged together, not replaced. Settings from the following config locations are combined. Later configs override earlier ones only for conflicting keys. Non-conflicting settings from all configs are preserved.
 
 For example, if your global config sets `theme: "opencode"` and `autoupdate: true`, and your project config sets `model: "anthropic/claude-sonnet-4-5"`, the final configuration will include all three settings.
 
 ---
 
+### Precedence order
+
+Config sources are loaded in this order (later sources override earlier ones):
+
+1. **Remote config** (from `.well-known/opencode`) - organizational defaults
+2. **Global config** (`~/.config/opencode/opencode.json`) - user preferences
+3. **Custom config** (`OPENCODE_CONFIG` env var) - custom overrides
+4. **Project config** (`opencode.json` in project) - project-specific settings
+5. **`.opencode` directories** - agents, commands, plugins
+6. **Inline config** (`OPENCODE_CONFIG_CONTENT` env var) - runtime overrides
+
+This means project configs can override global defaults, and global configs can override remote organizational defaults.
+
+---
+
+### Remote
+
+Organizations can provide default configuration via the `.well-known/opencode` endpoint. This is fetched automatically when you authenticate with a provider that supports it.
+
+Remote config is loaded first, serving as the base layer. All other config sources (global, project) can override these defaults.
+
+For example, if your organization provides MCP servers that are disabled by default:
+
+```json title="Remote config from .well-known/opencode"
+{
+  "mcp": {
+    "jira": {
+      "type": "remote",
+      "url": "https://jira.example.com/mcp",
+      "enabled": false
+    }
+  }
+}
+```
+
+You can enable specific servers in your local config:
+
+```json title="opencode.json"
+{
+  "mcp": {
+    "jira": {
+      "type": "remote",
+      "url": "https://jira.example.com/mcp",
+      "enabled": true
+    }
+  }
+}
+```
+
+---
+
 ### Global
 
-Place your global OpenCode config in `~/.config/opencode/opencode.json`. You'll want to use the global config for things like themes, providers, or keybinds.
+Place your global OpenCode config in `~/.config/opencode/opencode.json`. Use global config for user-wide preferences like themes, providers, or keybinds.
+
+Global config overrides remote organizational defaults.
 
 ---
 
 ### Per project
 
-You can also add a `opencode.json` in your project. Settings from this config are merged with and can override the global config. This is useful for configuring providers or modes specific to your project.
+Add `opencode.json` in your project root. Project config has the highest precedence among standard config files - it overrides both global and remote configs.
 
 :::tip
 Place project specific config in the root of your project.
@@ -60,20 +113,20 @@ This is also safe to be checked into Git and uses the same schema as the global
 
 ### Custom path
 
-You can also specify a custom config file path using the `OPENCODE_CONFIG` environment variable.
+Specify a custom config file path using the `OPENCODE_CONFIG` environment variable.
 
 ```bash
 export OPENCODE_CONFIG=/path/to/my/custom-config.json
 opencode run "Hello world"
 ```
 
-Settings from this config are merged with and **can override** the global and project configs.
+Custom config is loaded between global and project configs in the precedence order.
 
 ---
 
 ### Custom directory
 
-You can specify a custom config directory using the `OPENCODE_CONFIG_DIR`
+Specify a custom config directory using the `OPENCODE_CONFIG_DIR`
 environment variable. This directory will be searched for agents, commands,
 modes, and plugins just like the standard `.opencode` directory, and should
 follow the same structure.

+ 3 - 1
packages/web/src/content/docs/index.mdx

@@ -76,9 +76,11 @@ You can also install it with the following commands:
 - **Using Homebrew on macOS and Linux**
 
   ```bash
-  brew install opencode
+  brew install anomalyco/tap/opencode
   ```
 
+  > We recommend using the OpenCode tap for the most up to date releases. The official `brew install opencode` formula is maintained by the Homebrew team and is updated less frequently.
+
 - **Using Paru on Arch Linux**
 
   ```bash

+ 23 - 0
packages/web/src/content/docs/mcp-servers.mdx

@@ -44,6 +44,29 @@ You can also disable a server by setting `enabled` to `false`. This is useful if
 
 ---
 
+### Overriding remote defaults
+
+Organizations can provide default MCP servers via their `.well-known/opencode` endpoint. These servers may be disabled by default, allowing users to opt-in to the ones they need.
+
+To enable a specific server from your organization's remote config, add it to your local config with `enabled: true`:
+
+```json title="opencode.json"
+{
+  "$schema": "https://opencode.ai/config.json",
+  "mcp": {
+    "jira": {
+      "type": "remote",
+      "url": "https://jira.example.com/mcp",
+      "enabled": true
+    }
+  }
+}
+```
+
+Your local config values override the remote defaults. See [config precedence](/docs/config#precedence-order) for more details.
+
+---
+
 ## Local
 
 Add local MCP servers using `type` to `"local"` within the MCP object.

+ 1 - 1
sdks/vscode/package.json

@@ -2,7 +2,7 @@
   "name": "opencode",
   "displayName": "opencode",
   "description": "opencode for VS Code",
-  "version": "1.1.4",
+  "version": "1.1.6",
   "publisher": "sst-dev",
   "repository": {
     "type": "git",