summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-11-26 15:33:43 -0800
committerGitHub <[email protected]>2025-11-26 17:33:43 -0600
commit99d7ff47c409a80a9c3b8217e8ca09383f4e70ac (patch)
tree4d6224c7ec2bf62ab24300c96bb55bf1c3171019
parent3ff0eb3065e477af8f9c7c50c37c2b04e919e689 (diff)
downloadopencode-99d7ff47c409a80a9c3b8217e8ca09383f4e70ac.tar.gz
opencode-99d7ff47c409a80a9c3b8217e8ca09383f4e70ac.zip
enable parcel file watcher, expand parcel ignore patterns, replace fs watcher for git branches with parcel (#4805)
-rw-r--r--packages/opencode/src/file/ignore.ts10
-rw-r--r--packages/opencode/src/file/watcher.ts52
-rw-r--r--packages/opencode/src/project/project.ts8
-rw-r--r--packages/opencode/src/project/vcs.ts50
-rw-r--r--packages/sdk/js/src/gen/types.gen.ts19
5 files changed, 77 insertions, 62 deletions
diff --git a/packages/opencode/src/file/ignore.ts b/packages/opencode/src/file/ignore.ts
index 2e1d1428f..7230f67af 100644
--- a/packages/opencode/src/file/ignore.ts
+++ b/packages/opencode/src/file/ignore.ts
@@ -6,6 +6,7 @@ export namespace FileIgnore {
"bower_components",
".pnpm-store",
"vendor",
+ ".npm",
"dist",
"build",
"out",
@@ -22,12 +23,21 @@ export namespace FileIgnore {
".output",
"desktop",
".sst",
+ ".cache",
+ ".webkit-cache",
+ "__pycache__",
+ ".pytest_cache",
+ "mypy_cache",
+ ".history",
+ ".gradle",
])
const FILES = [
"**/*.swp",
"**/*.swo",
+ "**/*.pyc",
+
// OS
"**/.DS_Store",
"**/Thumbs.db",
diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts
index d5985b582..4459d20e9 100644
--- a/packages/opencode/src/file/watcher.ts
+++ b/packages/opencode/src/file/watcher.ts
@@ -1,6 +1,5 @@
import z from "zod"
import { Bus } from "../bus"
-import { Flag } from "../flag/flag"
import { Instance } from "../project/instance"
import { Log } from "../util/log"
import { FileIgnore } from "./ignore"
@@ -8,6 +7,7 @@ import { Config } from "../config/config"
// @ts-ignore
import { createWrapper } from "@parcel/watcher/wrapper"
import { lazy } from "@/util/lazy"
+import type ParcelWatcher from "@parcel/watcher"
export namespace FileWatcher {
const log = Log.create({ service: "file.watcher" })
@@ -44,32 +44,46 @@ export namespace FileWatcher {
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 ?? [])],
+ const subscribe: ParcelWatcher.SubscribeCallback = (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" })
+ }
+ }
+
+ const subs = []
+ const cfgIgnores = cfg.watcher?.ignore ?? []
+
+ subs.push(
+ await watcher().subscribe(Instance.directory, subscribe, {
+ ignore: [...FileIgnore.PATTERNS, ...cfgIgnores],
backend,
- },
+ }),
)
- return { sub }
+
+ const vcsDir = Instance.project.vcsDir
+ if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
+ subs.push(
+ await watcher().subscribe(vcsDir, subscribe, {
+ ignore: ["hooks", "info", "logs", "objects", "refs", "worktrees", "modules", "lfs"],
+ backend,
+ }),
+ )
+ }
+
+ return { subs }
},
async (state) => {
- if (!state.sub) return
- await state.sub?.unsubscribe()
+ if (!state.subs) return
+ await Promise.all(state.subs.map((sub) => sub?.unsubscribe()))
},
)
export function init() {
- if (!Flag.OPENCODE_EXPERIMENTAL_WATCHER) return
+ // if (!Flag.OPENCODE_EXPERIMENTAL_WATCHER) return
state()
}
}
diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts
index 381559e80..74e969145 100644
--- a/packages/opencode/src/project/project.ts
+++ b/packages/opencode/src/project/project.ts
@@ -12,6 +12,7 @@ export namespace Project {
.object({
id: z.string(),
worktree: z.string(),
+ vcsDir: z.string().optional(),
vcs: z.literal("git").optional(),
time: z.object({
created: z.number(),
@@ -80,9 +81,16 @@ export namespace Project {
.cwd(worktree)
.text()
.then((x) => x.trim())
+ const vcsDir = await $`git rev-parse --path-format=absolute --git-dir`
+ .quiet()
+ .nothrow()
+ .cwd(worktree)
+ .text()
+ .then((x) => x.trim())
const project: Info = {
id,
worktree,
+ vcsDir,
vcs: "git",
time: {
created: Date.now(),
diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts
index c3b21f3be..a8d5e91b3 100644
--- a/packages/opencode/src/project/vcs.ts
+++ b/packages/opencode/src/project/vcs.ts
@@ -1,10 +1,10 @@
import { $ } from "bun"
-import { watch, type FSWatcher } from "fs"
import path from "path"
import z from "zod"
import { Log } from "@/util/log"
import { Bus } from "@/bus"
import { Instance } from "./instance"
+import { FileWatcher } from "@/file/watcher"
const log = Log.create({ service: "vcs" })
@@ -39,49 +39,31 @@ export namespace Vcs {
const state = Instance.state(
async () => {
- if (Instance.project.vcs !== "git") {
- return { branch: async () => undefined, watcher: undefined }
+ const vcsDir = Instance.project.vcsDir
+ if (Instance.project.vcs !== "git" || !vcsDir) {
+ return { branch: async () => undefined, unsubscribe: undefined }
}
let current = await currentBranch()
log.info("initialized", { branch: current })
- const gitDir = await $`git rev-parse --git-dir`
- .quiet()
- .nothrow()
- .cwd(Instance.worktree)
- .text()
- .then((x) => x.trim())
- .catch(() => undefined)
- if (!gitDir) {
- log.warn("failed to resolve git directory")
- return { branch: async () => current, watcher: undefined }
- }
-
- const gitHead = path.join(gitDir, "HEAD")
- let watcher: FSWatcher | undefined
- // we should probably centralize file watching (see watcher.ts)
- // but parcel still marked experimental rn
- try {
- watcher = watch(gitHead, async () => {
- const next = await currentBranch()
- if (next !== current) {
- log.info("branch changed", { from: current, to: next })
- current = next
- Bus.publish(Event.BranchUpdated, { branch: next })
- }
- })
- log.info("watching", { path: gitHead })
- } catch (e) {
- log.warn("failed to watch git HEAD", { error: e })
- }
+ const head = path.join(vcsDir, "HEAD")
+ const unsubscribe = Bus.subscribe(FileWatcher.Event.Updated, async (evt) => {
+ if (evt.properties.file !== head) return
+ const next = await currentBranch()
+ if (next !== current) {
+ log.info("branch changed", { from: current, to: next })
+ current = next
+ Bus.publish(Event.BranchUpdated, { branch: next })
+ }
+ })
return {
branch: async () => current,
- watcher,
+ unsubscribe,
}
},
async (state) => {
- state.watcher?.close()
+ state.unsubscribe?.()
},
)
diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts
index efa5c9946..bf23f77ec 100644
--- a/packages/sdk/js/src/gen/types.gen.ts
+++ b/packages/sdk/js/src/gen/types.gen.ts
@@ -589,6 +589,14 @@ export type EventSessionError = {
}
}
+export type EventFileWatcherUpdated = {
+ type: "file.watcher.updated"
+ properties: {
+ file: string
+ event: "add" | "change" | "unlink"
+ }
+}
+
export type EventVcsBranchUpdated = {
type: "vcs.branch.updated"
properties: {
@@ -647,14 +655,6 @@ export type EventServerConnected = {
}
}
-export type EventFileWatcherUpdated = {
- type: "file.watcher.updated"
- properties: {
- file: string
- event: "add" | "change" | "unlink"
- }
-}
-
export type Event =
| EventInstallationUpdated
| EventInstallationUpdateAvailable
@@ -677,12 +677,12 @@ export type Event =
| EventSessionDeleted
| EventSessionDiff
| EventSessionError
+ | EventFileWatcherUpdated
| EventVcsBranchUpdated
| EventTuiPromptAppend
| EventTuiCommandExecute
| EventTuiToastShow
| EventServerConnected
- | EventFileWatcherUpdated
export type GlobalEvent = {
directory: string
@@ -692,6 +692,7 @@ export type GlobalEvent = {
export type Project = {
id: string
worktree: string
+ vcsDir?: string
vcs?: "git"
time: {
created: number