fix(watchers): revive dead skill and memory file watchers#113
Merged
Conversation
chokidar v4+ dropped glob support, so the literal glob paths handed to chokidar (skills/watcher.ts, memory/index.ts) matched nothing: the watchers registered but never fired. Skill hot-reload was fully dead, and root-level .md edits never triggered a memory reindex. Watch the containing directories instead and filter events in the handler: - skills: watch the skill roots, react only to SKILL.md basenames - memory: watch the workspace root + sessions dir, filter to indexed files - replace the unanchored substring ignore regexes (/build/, /dist/, ...) with exact path-segment matching scoped below the watch roots, so a skill named 'prompt-builder' or a memory file like 'node_modules-notes.md' is no longer silently pruned Add integration tests that write real files and assert the watcher fires, and that vendored dirs / non-target files stay ignored.
rich7420
force-pushed
the
fix/dead-file-watchers
branch
from
July 8, 2026 15:51
92f45ad to
2bd5d17
Compare
The chokidar `ignored` function predicate receives a forward-slash-normalized path on every platform, but makeIgnored built its watch roots with the native separator and split on it. On Windows the root prefix never matched the normalized path, so .git/node_modules (and the skills junk dirs) were never pruned — the watcher over-descended into vendored trees. Fails-open (extra watches + spurious debounced fires), not a dead watcher, and macOS/Linux were unaffected since sep is already '/'. Normalize both the roots and the incoming path to POSIX separators before the segment match (a no-op on POSIX). Export makeIgnored and add unit tests that assert vendored-segment pruning, root/skill-folder preservation, and the substring-vs-segment distinction for both '/' and '\\' inputs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
chokidar v4+ dropped glob support, but two watchers still handed it literal glob
paths (
skills/watcher.ts,memory/index.ts). In v5 a path containing*istreated as a literal filename that matches nothing, so both watchers registered
successfully — returned a live
FSWatcher, attached their.on("all")handler —yet never fired. The failure was invisible: no log, no error.
This restores them by watching the containing directories (non-glob) and
filtering events in the handler, and hardens the ignore matching so legitimately
named skill/memory files are no longer pruned.
Before
skills/watcher.tswatched~/.hawky/skills/*/SKILL.mdand<ws>/skills/*/SKILL.md. Both matched nothing → skill hot-reload was fullydead: editing/adding/removing a skill on disk had no effect until a full
process restart.
memory/index.tswatched<ws>/*.md(dead glob) plus<ws>/memoryand thesessions dir (real dirs, worked). So editing a top-level
.mdsuch asHAWKY.mdnever triggered a memory reindex — the index silently driftedfrom the files it claims to mirror.
/build/,/dist/,/node_modules/, …). Once a watcher is live, these prune any path containingthe substring — e.g. a skill named
prompt-builder(contains "build") would besilently skipped.
After
whose basename is
SKILL.md, so edits, adds and removes propagate within onedebounce window.
paths to exactly what the indexer consumes (top-level
.md, thememory/subtree, session
.jsonl), so top-level.mdedits now reindex.so
prompt-builder(or even a skill namedbuild) and a memory file likenode_modules-notes.mdare kept, while real vendored dirs inside a skill arestill skipped.
Validation
tsc --noEmit: clean.bun test ./tests/test-*.ts ./tests/test-*.tsx ./tests/unit/*.test.ts:4728 pass, 5 skip, 0 fail (235 files).
SKILL.md, includingprompt-builder/build-named skills, and stays quiet for aSKILL.mdnested in a vendorednode_modules/;createMemoryWatcherfires on a top-level.md, filters non-targets, and nolonger prunes
node_modules-notes.md;MemoryIndex: a root.mdwritten after startupbecomes searchable — the exact behavior this fix restores.
Limitations / Follow-ups
(
depth: 2) to catch top-level.md; fine for the default~/.hawky/workspace, a minor cost for a large injected workspace.makeIgnoredand the path-prefix check are near-duplicated acrossskills/watcher.ts,memory/watcher.ts, andmemory/index.ts— candidatefor a shared
isPathInside/makeSegmentIgnorehelper.~/.hawky; unifying on the configured rootis tracked separately as the
HAWKY_HOMEcleanup.