diff options
| author | Adam Malczewski <[email protected]> | 2026-06-04 16:16:45 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-04 16:16:45 +0900 |
| commit | 81a9cdbadf8c9d940d4fe9a2a0de607dee1f5f1a (patch) | |
| tree | dff8ce0e55f27b490445ddaf9fb6536d1313ffcf /packages/core/src/config/watcher.ts | |
| parent | 24bdaa6ca0333b91369ac50b23e929f83af01c3a (diff) | |
| download | dispatch-v2-deprecated.tar.gz dispatch-v2-deprecated.zip | |
feat(config): add subdirectory LSP config watchers and fix permission orderingv2-deprecated
- Add watchDirConfig() for per-directory config watching
- Register watchers for subdirectories with their own dispatch.toml
- Fix permission ordering: move "*" wildcard to front so findLast
reaches specific rules first (was silently breaking all specific
bash permission rules)
- Add comprehensive tests for watcher functionality
- Update mocks in test files
Diffstat (limited to 'packages/core/src/config/watcher.ts')
| -rw-r--r-- | packages/core/src/config/watcher.ts | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/packages/core/src/config/watcher.ts b/packages/core/src/config/watcher.ts index 9414ef6..ad55804 100644 --- a/packages/core/src/config/watcher.ts +++ b/packages/core/src/config/watcher.ts @@ -69,3 +69,61 @@ export function createConfigWatcher( }, }; } + +/** + * Watch a SINGLE directory's `dispatch.toml` (no global merge, no reload — just + * a debounced change signal). Used by the agent manager to invalidate its + * per-directory LSP cache when a tab's effective working directory is a + * SUBDIRECTORY with its own `dispatch.toml`: the main `createConfigWatcher` + * only watches the root + global configs, so without this a nested config edit + * would never clear `lspServersByDir[subdir]` and agents there would keep using + * stale LSP servers until a root-config change or restart. + * + * `onChange` fires (debounced) on add/change/unlink of `<dir>/dispatch.toml`. + */ +export function watchDirConfig(dir: string, onChange: () => void): { close(): void } { + const tomlPath = join(dir, "dispatch.toml"); + let debounceTimer: ReturnType<typeof setTimeout> | null = null; + + const watcher = watch(tomlPath, { + ignoreInitial: true, + persistent: false, + }); + + const handleChange = () => { + if (debounceTimer !== null) clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + debounceTimer = null; + try { + onChange(); + } catch (err) { + console.warn( + `dispatch: dir config watcher onChange error: ${err instanceof Error ? err.message : String(err)}`, + ); + } + }, 300); + }; + + watcher.on("change", handleChange); + watcher.on("add", handleChange); + watcher.on("unlink", handleChange); + watcher.on("error", (err) => { + console.warn( + `dispatch: dir config watcher error: ${err instanceof Error ? err.message : String(err)}`, + ); + }); + + return { + close() { + if (debounceTimer !== null) { + clearTimeout(debounceTimer); + debounceTimer = null; + } + watcher.close().catch((err) => { + console.warn( + `dispatch: error closing dir config watcher: ${err instanceof Error ? err.message : String(err)}`, + ); + }); + }, + }; +} |
