* Start watching for file changes and auto-syncing. * * Uses native OS file events (FSEvents on macOS, inotify on Linux 19+, * ReadDirectoryChangesW on Windows) with debouncing to avoid thrashing. * * @param options - Watch options (debounce delay, callbacks) * @returns true if wat
(options: WatchOptions = {})
| 1055 | * @returns true if watching started successfully |
| 1056 | */ |
| 1057 | watch(options: WatchOptions = {}): boolean { |
| 1058 | if (this.watcher?.isActive()) return true; |
| 1059 | |
| 1060 | this.watcher = new FileWatcher( |
| 1061 | this.projectRoot, |
| 1062 | async (paths?: string[]) => { |
| 1063 | const result = await this.sync({ paths }); |
| 1064 | // sync() returns this exact zero-shape iff it failed to acquire the |
| 1065 | // file lock (a real empty sync always has filesChecked > 0 because |
| 1066 | // scanDirectory ran). Surface that to the watcher as a typed error |
| 1067 | // so it keeps pendingFiles + reschedules instead of clearing them |
| 1068 | // (#449). |
| 1069 | if (result.filesChecked === 0 && result.durationMs === 0) { |
| 1070 | throw new LockUnavailableError(); |
| 1071 | } |
| 1072 | const filesChanged = result.filesAdded + result.filesModified + result.filesRemoved; |
| 1073 | return { filesChanged, durationMs: result.durationMs }; |
| 1074 | }, |
| 1075 | options |
| 1076 | ); |
| 1077 | |
| 1078 | return this.watcher.start(); |
| 1079 | } |
| 1080 | |
| 1081 | /** |
| 1082 | * Stop watching for file changes. |