summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax <[email protected]>2025-09-16 00:17:10 -0400
committerGitHub <[email protected]>2025-09-16 00:17:10 -0400
commit14cb2d2af66992dbcd9cbd0d529b32fd862663cb (patch)
tree009bb9f192890bdfa307791401e377077f41bdd7
parent52fb571739cb0282d5d32ccb8429a790f865d8de (diff)
downloadopencode-14cb2d2af66992dbcd9cbd0d529b32fd862663cb.tar.gz
opencode-14cb2d2af66992dbcd9cbd0d529b32fd862663cb.zip
feat: improve file watcher with chokidar and better ignore patterns (#2621)
Co-authored-by: GitHub Action <[email protected]>
-rw-r--r--bun.lock1
-rw-r--r--opencode.json8
-rw-r--r--packages/opencode/package.json1
-rw-r--r--packages/opencode/src/cli/cmd/tui.ts4
-rw-r--r--packages/opencode/src/config/config.ts5
-rw-r--r--packages/opencode/src/file/ignore.ts61
-rw-r--r--packages/opencode/src/file/watch.ts46
-rw-r--r--packages/opencode/src/file/watcher.ts61
-rw-r--r--packages/opencode/src/flag/flag.ts4
-rw-r--r--packages/sdk/go/.release-please-manifest.json2
-rw-r--r--packages/sdk/go/.stats.yml4
-rw-r--r--packages/sdk/go/CHANGELOG.md8
-rw-r--r--packages/sdk/go/README.md2
-rw-r--r--packages/sdk/go/internal/version.go2
-rw-r--r--packages/sdk/go/sessionpermission.go29
-rw-r--r--packages/sdk/go/shared/union.go4
16 files changed, 180 insertions, 62 deletions
diff --git a/bun.lock b/bun.lock
index 4164ba1c3..013c7839a 100644
--- a/bun.lock
+++ b/bun.lock
@@ -143,6 +143,7 @@
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
+ "chokidar": "4.0.3",
"decimal.js": "10.5.0",
"diff": "8.0.2",
"gray-matter": "4.0.3",
diff --git a/opencode.json b/opencode.json
index 2fa644079..720ece5c1 100644
--- a/opencode.json
+++ b/opencode.json
@@ -1,9 +1,3 @@
{
- "$schema": "https://opencode.ai/config.json",
- "mcp": {
- "weather": {
- "type": "local",
- "command": ["opencode", "x", "@h1deya/mcp-server-weather"]
- }
- }
+ "$schema": "https://opencode.ai/config.json"
}
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index 3ba05429a..5bf59f26c 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -37,6 +37,7 @@
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
+ "chokidar": "4.0.3",
"decimal.js": "10.5.0",
"diff": "8.0.2",
"gray-matter": "4.0.3",
diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts
index e0fe49019..7281f6e4b 100644
--- a/packages/opencode/src/cli/cmd/tui.ts
+++ b/packages/opencode/src/cli/cmd/tui.ts
@@ -10,7 +10,7 @@ import { Installation } from "../../installation"
import { Config } from "../../config/config"
import { Bus } from "../../bus"
import { Log } from "../../util/log"
-import { FileWatcher } from "../../file/watch"
+import { FileWatcher } from "../../file/watcher"
import { Ide } from "../../ide"
import { Flag } from "../../flag/flag"
@@ -101,7 +101,6 @@ export const TuiCommand = cmd({
}
return undefined
})()
- FileWatcher.init()
const providers = await Provider.list()
if (Object.keys(providers).length === 0) {
return "needs_provider"
@@ -181,6 +180,7 @@ export const TuiCommand = cmd({
.then(() => Bus.publish(Ide.Event.Installed, { ide }))
.catch(() => {})
})()
+ FileWatcher.init()
await proc.exited
server.stop()
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 74413657c..7d86845e3 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -365,6 +365,11 @@ export namespace Config {
.record(z.string(), Command)
.optional()
.describe("Command configuration, see https://opencode.ai/docs/commands"),
+ watcher: z
+ .object({
+ ignore: z.array(z.string()).optional(),
+ })
+ .optional(),
plugin: z.string().array().optional(),
snapshot: z.boolean().optional(),
share: z
diff --git a/packages/opencode/src/file/ignore.ts b/packages/opencode/src/file/ignore.ts
new file mode 100644
index 000000000..53e2003b9
--- /dev/null
+++ b/packages/opencode/src/file/ignore.ts
@@ -0,0 +1,61 @@
+export namespace FileIgnore {
+ const DEFAULT_PATTERNS = [
+ // Dependencies
+ "**/node_modules/**",
+ "**/bower_components/**",
+ "**/.pnpm-store/**",
+ "**/vendor/**",
+
+ // vcs
+ "**/.git/**",
+
+ // Build outputs
+ "**/dist/**",
+ "**/build/**",
+ "**/out/**",
+ "**/.next/**",
+ "**/target/**", // Rust
+ "**/bin/**",
+ "**/obj/**", // .NET
+
+ // Version control
+ "**/.git/**",
+ "**/.svn/**",
+ "**/.hg/**",
+
+ // IDE/Editor
+ "**/.vscode/**",
+ "**/.idea/**",
+ "**/*.swp",
+ "**/*.swo",
+
+ // OS
+ "**/.DS_Store",
+ "**/Thumbs.db",
+
+ // Logs & temp
+ "**/logs/**",
+ "**/tmp/**",
+ "**/temp/**",
+ "**/*.log",
+
+ // Coverage/test outputs
+ "**/coverage/**",
+ "**/.nyc_output/**",
+ ]
+
+ const GLOBS = DEFAULT_PATTERNS.map((p) => new Bun.Glob(p))
+
+ export function match(
+ filepath: string,
+ opts: {
+ extra?: Bun.Glob[]
+ },
+ ) {
+ const extra = opts.extra || []
+ for (const glob of [...GLOBS, ...extra]) {
+ if (glob.match(filepath)) return true
+ }
+ return false
+ }
+}
diff --git a/packages/opencode/src/file/watch.ts b/packages/opencode/src/file/watch.ts
deleted file mode 100644
index 526b29c9e..000000000
--- a/packages/opencode/src/file/watch.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import z from "zod/v4"
-import { Bus } from "../bus"
-import fs from "fs"
-import { Log } from "../util/log"
-import { Flag } from "../flag/flag"
-import { Instance } from "../project/instance"
-
-export namespace FileWatcher {
- const log = Log.create({ service: "file.watcher" })
-
- export const Event = {
- Updated: Bus.event(
- "file.watcher.updated",
- z.object({
- file: z.string(),
- event: z.union([z.literal("rename"), z.literal("change")]),
- }),
- ),
- }
- const state = Instance.state(
- () => {
- if (Instance.project.vcs !== "git") return {}
- try {
- const watcher = fs.watch(Instance.directory, { recursive: true }, (event, file) => {
- log.info("change", { file, event })
- if (!file) return
- Bus.publish(Event.Updated, {
- file,
- event,
- })
- })
- return { watcher }
- } catch {
- return {}
- }
- },
- async (state) => {
- state.watcher?.close()
- },
- )
-
- export function init() {
- if (Flag.OPENCODE_DISABLE_WATCHER || true) return
- state()
- }
-}
diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts
new file mode 100644
index 000000000..fddff515f
--- /dev/null
+++ b/packages/opencode/src/file/watcher.ts
@@ -0,0 +1,61 @@
+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"
+
+export namespace FileWatcher {
+ const log = Log.create({ service: "file.watcher" })
+
+ export const Event = {
+ Updated: Bus.event(
+ "file.watcher.updated",
+ z.object({
+ file: z.string(),
+ event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
+ }),
+ ),
+ }
+
+ 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,
+ awaitWriteFinish: true,
+ ignored: (filepath) => {
+ return FileIgnore.match(filepath, {
+ extra: ignore,
+ })
+ },
+ })
+ 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 }
+ },
+ async (state) => {
+ state.watcher?.close()
+ },
+ )
+
+ export function init() {
+ if (!Flag.OPENCODE_EXPERIMENTAL_WATCHER) return
+ state()
+ }
+}
diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts
index 418f32283..a05379073 100644
--- a/packages/opencode/src/flag/flag.ts
+++ b/packages/opencode/src/flag/flag.ts
@@ -1,6 +1,5 @@
export namespace Flag {
export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE")
- export const OPENCODE_DISABLE_WATCHER = truthy("OPENCODE_DISABLE_WATCHER")
export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"]
export const OPENCODE_CONFIG_CONTENT = process.env["OPENCODE_CONFIG_CONTENT"]
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
@@ -10,6 +9,9 @@ export namespace Flag {
export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS")
export const OPENCODE_DISABLE_AUTOCOMPACT = truthy("OPENCODE_DISABLE_AUTOCOMPACT")
+ // Experimental
+ export const OPENCODE_EXPERIMENTAL_WATCHER = truthy("OPENCODE_EXPERIMENTAL_WATCHER")
+
function truthy(key: string) {
const value = process.env[key]?.toLowerCase()
return value === "true" || value === "1"
diff --git a/packages/sdk/go/.release-please-manifest.json b/packages/sdk/go/.release-please-manifest.json
index ed21d28cb..727e2bea9 100644
--- a/packages/sdk/go/.release-please-manifest.json
+++ b/packages/sdk/go/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.13.0"
+ ".": "0.14.0"
}
diff --git a/packages/sdk/go/.stats.yml b/packages/sdk/go/.stats.yml
index 7f4a9c119..9d47e52e1 100644
--- a/packages/sdk/go/.stats.yml
+++ b/packages/sdk/go/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 43
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-2e754dafcad0636137256cef499b2bcd72cf17de08f44ec03c3589b2a05341a2.yml
-openapi_spec_hash: 2d3cf84d3033068ce6c07386411527ef
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-0a4165f1eabf826d3092ea6b789aa527048278dcd4bd891f9e5ac890b9bcbb35.yml
+openapi_spec_hash: da60e4fc813eb0f9ac3ab5f112e26bf6
config_hash: 026ef000d34bf2f930e7b41e77d2d3ff
diff --git a/packages/sdk/go/CHANGELOG.md b/packages/sdk/go/CHANGELOG.md
index 018017066..9e13db9e0 100644
--- a/packages/sdk/go/CHANGELOG.md
+++ b/packages/sdk/go/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.14.0 (2025-09-14)
+
+Full Changelog: [v0.13.0...v0.14.0](https://github.com/sst/opencode-sdk-go/compare/v0.13.0...v0.14.0)
+
+### Features
+
+- **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77))
+
## 0.13.0 (2025-09-14)
Full Changelog: [v0.12.0...v0.13.0](https://github.com/sst/opencode-sdk-go/compare/v0.12.0...v0.13.0)
diff --git a/packages/sdk/go/README.md b/packages/sdk/go/README.md
index 2c48f53ce..96898209d 100644
--- a/packages/sdk/go/README.md
+++ b/packages/sdk/go/README.md
@@ -24,7 +24,7 @@ Or to pin the version:
<!-- x-release-please-start-version -->
```sh
-go get -u 'github.com/sst/[email protected]'
+go get -u 'github.com/sst/[email protected]'
```
<!-- x-release-please-end -->
diff --git a/packages/sdk/go/internal/version.go b/packages/sdk/go/internal/version.go
index 871f0965d..870e575ab 100644
--- a/packages/sdk/go/internal/version.go
+++ b/packages/sdk/go/internal/version.go
@@ -2,4 +2,4 @@
package internal
-const PackageVersion = "0.13.0" // x-release-please-version
+const PackageVersion = "0.14.0" // x-release-please-version
diff --git a/packages/sdk/go/sessionpermission.go b/packages/sdk/go/sessionpermission.go
index 4d49bd87b..4dbfe1d2d 100644
--- a/packages/sdk/go/sessionpermission.go
+++ b/packages/sdk/go/sessionpermission.go
@@ -8,12 +8,15 @@ import (
"fmt"
"net/http"
"net/url"
+ "reflect"
"github.com/sst/opencode-sdk-go/internal/apijson"
"github.com/sst/opencode-sdk-go/internal/apiquery"
"github.com/sst/opencode-sdk-go/internal/param"
"github.com/sst/opencode-sdk-go/internal/requestconfig"
"github.com/sst/opencode-sdk-go/option"
+ "github.com/sst/opencode-sdk-go/shared"
+ "github.com/tidwall/gjson"
)
// SessionPermissionService contains methods and other services that help with
@@ -60,7 +63,7 @@ type Permission struct {
Title string `json:"title,required"`
Type string `json:"type,required"`
CallID string `json:"callID"`
- Pattern string `json:"pattern"`
+ Pattern PermissionPatternUnion `json:"pattern"`
JSON permissionJSON `json:"-"`
}
@@ -107,6 +110,30 @@ func (r permissionTimeJSON) RawJSON() string {
return r.raw
}
+// Union satisfied by [shared.UnionString] or [PermissionPatternArray].
+type PermissionPatternUnion interface {
+ ImplementsPermissionPatternUnion()
+}
+
+func init() {
+ apijson.RegisterUnion(
+ reflect.TypeOf((*PermissionPatternUnion)(nil)).Elem(),
+ "",
+ apijson.UnionVariant{
+ TypeFilter: gjson.String,
+ Type: reflect.TypeOf(shared.UnionString("")),
+ },
+ apijson.UnionVariant{
+ TypeFilter: gjson.JSON,
+ Type: reflect.TypeOf(PermissionPatternArray{}),
+ },
+ )
+}
+
+type PermissionPatternArray []string
+
+func (r PermissionPatternArray) ImplementsPermissionPatternUnion() {}
+
type SessionPermissionRespondParams struct {
Response param.Field[SessionPermissionRespondParamsResponse] `json:"response,required"`
Directory param.Field[string] `query:"directory"`
diff --git a/packages/sdk/go/shared/union.go b/packages/sdk/go/shared/union.go
index 91c73305d..e01f54c89 100644
--- a/packages/sdk/go/shared/union.go
+++ b/packages/sdk/go/shared/union.go
@@ -2,6 +2,10 @@
package shared
+type UnionString string
+
+func (UnionString) ImplementsPermissionPatternUnion() {}
+
type UnionBool bool
func (UnionBool) ImplementsConfigProviderOptionsTimeoutUnion() {}