diff --git a/src/controller.ts b/src/controller.ts index bc7c04d..74cda47 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -10,6 +10,10 @@ import { WorkspaceModel } from "./workspace.js"; export const CHECKPOINT_ENTRY = "review-loop/checkpoint"; +export function isIgnoredWatchPath(repoRoot: string, path: string): boolean { + return relative(repoRoot, path).split(sep).some((part) => part === ".git" || part === "node_modules"); +} + function isCheckpoint(value: unknown): value is ReviewCheckpoint { if (value == null || typeof value !== "object") return false; const item = value as Partial; @@ -93,11 +97,9 @@ export class ReviewController { private async startWatcher(): Promise { this.watcher = watch(this.repoRoot, { ignoreInitial: true, - ignored: (path) => { - const rel = relative(this.repoRoot, path); - return rel === ".git" || rel.startsWith(`.git${sep}`) || rel === "node_modules" || rel.startsWith(`node_modules${sep}`); - }, + ignored: (path) => isIgnoredWatchPath(this.repoRoot, path), }); + this.watcher.on("error", () => {}); this.watcher.on("all", (_event, path) => { const repoPath = this.toRepoPath(path); if (repoPath == null) return; diff --git a/test/core.test.ts b/test/core.test.ts index 6dac346..6620807 100644 --- a/test/core.test.ts +++ b/test/core.test.ts @@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; import test from "node:test"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import { isIgnoredWatchPath } from "../src/controller.js"; import { composeFeedback } from "../src/prompt.js"; import { createCheckpoint, decodeStored, parsePorcelainPaths, scanAgainstCheckpoint } from "../src/git.js"; import { WorkspaceModel } from "../src/workspace.js"; @@ -31,6 +32,12 @@ async function git(cwd: string, ...args: string[]): Promise { await execFileAsync("git", args, { cwd }); } +test("ignores generated dependency and git trees at any depth", () => { + assert.equal(isIgnoredWatchPath("/repo", "/repo/packages/app/node_modules/pkg/file.js"), true); + assert.equal(isIgnoredWatchPath("/repo", "/repo/packages/app/.git/index"), true); + assert.equal(isIgnoredWatchPath("/repo", "/repo/src/file.ts"), false); +}); + test("parses porcelain paths including renames", () => { const output = " M src/a.ts\0R src/new.ts\0src/old.ts\0?? new file.ts\0"; assert.deepEqual(parsePorcelainPaths(output), ["src/a.ts", "src/new.ts", "src/old.ts", "new file.ts"]);