Fix #1056: ignore heavy paths in skills watcher

On macOS, watching deep dependency trees can exhaust file descriptors and lead to spawn EBADF failures. The skills watcher only needs to observe skill changes, so ignore dotfiles, node_modules, and dist by default. Adds regression coverage.
This commit is contained in:
Roshan Singh authored and Peter Steinberger committed 2026-01-17 07:40:20 +00:00
1 parent 6d969fe58e
commit 6aaa302be5
2 files changed
+37

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
import { describe, expect, it, vi } from "vitest";
const watchMock = vi.fn(() => ({
on: vi.fn(),
close: vi.fn(async () => undefined),
}));
vi.mock("chokidar", () => {
return {
default: { watch: watchMock },
};
});
describe("ensureSkillsWatcher", () => {
it("ignores node_modules and dist by default", async () => {
const mod = await import("./refresh.js");
mod.ensureSkillsWatcher({ workspaceDir: "/tmp/workspace" });
expect(watchMock).toHaveBeenCalledTimes(1);
const opts = watchMock.mock.calls[0]?.[1] as { ignored?: unknown };
expect(Array.isArray(opts.ignored)).toBe(true);
const ignored = opts.ignored as RegExp[];
expect(ignored.some((re) => re.test("/tmp/workspace/skills/node_modules/pkg/index.js"))).toBe(
true,
);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/dist/index.js"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/.git/config"))).toBe(true);
});
});
+7
View File
@@ -125,6 +125,13 @@ export function ensureSkillsWatcher(params: { workspaceDir: string; config?: Cla
stabilityThreshold: debounceMs,
pollInterval: 100,
},
// Avoid FD exhaustion on macOS when a workspace contains huge trees.
// This watcher only needs to react to skill changes.
ignored: [
/(^|[\\/])\../, // dotfiles (includes .git)
/(^|[\\/])node_modules([\\/]|$)/,
/(^|[\\/])dist([\\/]|$)/,
],
});
const state: SkillsWatchState = { watcher, pathsKey, debounceMs };