diff options
| author | Adam Malczewski <[email protected]> | 2026-06-03 16:08:40 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-03 16:08:40 +0900 |
| commit | ebd68da7dfd6d4f2ef6c6b29a62ec848bbf15cef (patch) | |
| tree | 28de3a1dcd5e85a8fa9edb978ce0bad665ec722c /packages/core/src/config/watcher.ts | |
| parent | 5af9bd021c206b9e4330ab6a549dc8d013d91537 (diff) | |
| download | dispatch-ebd68da7dfd6d4f2ef6c6b29a62ec848bbf15cef.tar.gz dispatch-ebd68da7dfd6d4f2ef6c6b29a62ec848bbf15cef.zip | |
feat(config): merge home-directory global dispatch.toml under project config
Load an optional global config at ~/.config/dispatch/dispatch.toml
(override via DISPATCH_GLOBAL_CONFIG) and deep-merge it underneath every
project/working-directory dispatch.toml, so machine-wide settings — most
notably globally available LSP servers — work in any repo without per-repo
config. Local always wins on conflicts.
- loader: add getGlobalConfigPath(), loadGlobalConfig(), mergeConfigs();
loadConfig(dir) now loads+merges global. [lsp] and [[keys]] merge by id;
[permissions] merge per-group with global patterns emitted first so local
rules win at evaluation time (findLast). A malformed global config is
downgraded to empty rather than breaking every repo.
- watcher: watch BOTH global and local dispatch.toml so hot-reload re-merges
on either change (dedupes when paths coincide).
- export new loader fns from config/index and core index.
- types/agent-manager: doc updates reflecting merged LSP resolution.
- dispatch.toml: document global-default merge behavior; activate biome and
typescript-language-server LSP entries.
- tests: merge precedence, lsp/keys merge-by-id, permissions merge,
filesystem integration, malformed-global resilience; isolate global path
in existing loader tests.
Diffstat (limited to 'packages/core/src/config/watcher.ts')
| -rw-r--r-- | packages/core/src/config/watcher.ts | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/packages/core/src/config/watcher.ts b/packages/core/src/config/watcher.ts index 70821ed..9414ef6 100644 --- a/packages/core/src/config/watcher.ts +++ b/packages/core/src/config/watcher.ts @@ -1,16 +1,28 @@ import { join } from "node:path"; import { watch } from "chokidar"; import type { DispatchConfig } from "../types/index.js"; -import { loadConfig } from "./loader.js"; +import { getGlobalConfigPath, loadConfig } from "./loader.js"; +/** + * Watch BOTH the HOME-directory global `dispatch.toml` and the project/working- + * directory `dispatch.toml`. Either file changing triggers a reload that + * re-merges global + local (via {@link loadConfig}), so hot-reload works for + * global defaults and per-project overrides alike. + * + * When the global and local paths coincide (e.g. the working directory IS + * `~/.config/dispatch`, or `DISPATCH_GLOBAL_CONFIG` points at the local file) + * the duplicate is collapsed so chokidar only watches it once. + */ export function createConfigWatcher( dir: string, onChange: (config: DispatchConfig) => void, ): { close(): void } { - const tomlPath = join(dir, "dispatch.toml"); + const localPath = join(dir, "dispatch.toml"); + const globalPath = getGlobalConfigPath(); + const paths = globalPath === localPath ? [localPath] : [globalPath, localPath]; let debounceTimer: ReturnType<typeof setTimeout> | null = null; - const watcher = watch(tomlPath, { + const watcher = watch(paths, { ignoreInitial: true, persistent: false, }); @@ -21,7 +33,7 @@ export function createConfigWatcher( } debounceTimer = setTimeout(() => { debounceTimer = null; - console.log(`dispatch: reloading config from ${tomlPath}`); + console.log(`dispatch: reloading config (global + ${localPath})`); try { const config = loadConfig(dir); onChange(config); |
