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
2026-01-17 06:26:27 +00:00
parent bd32cc40e6
commit e7953d8164
2 changed files with 37 additions and 0 deletions

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);
});
});

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 };