feat: hot-reload skills, commands, agents and config on file change - #43538
Open
mccaffrey-jonathan wants to merge 4 commits into
Open
feat: hot-reload skills, commands, agents and config on file change#43538mccaffrey-jonathan wants to merge 4 commits into
mccaffrey-jonathan wants to merge 4 commits into
Conversation
Behind OPENCODE_EXPERIMENTAL_HOT_RELOAD. The filesystem watcher gains subscriptions for config directories (global config dir and .opencode dirs) so changes there emit events even when the project watcher is off. A new HotReload service listens for those events, filters them to config-relevant paths, debounces, then reloads the instance through InstanceStore.reload and drops the v2 location-service layer, so both stacks reread disk state. Clients already re-sync on the existing server.instance.disposed event; the TUI additionally refreshes its v2 agent/command/skill stores. Plugin install artifacts (package.json, lockfiles) inside config dirs are ignored to avoid reload loops. Closes anomalyco#8751 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTuWSW35XoRbPYixZr8oKQ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTuWSW35XoRbPYixZr8oKQ
Persistent layer-level listener survives failed re-boots; whitelist relevance filter (config subdirs, skill dirs, exact config file paths) instead of blacklist, so plan saves and fixture opencode.json files no longer trigger reloads; reload forks are supervised by the layer scope; reload passes only the directory so project identity is re-derived and skips when the instance was disposed; location-service invalidation goes through a registry covering every built map and workspace-scoped refs; config-dir watches honor watcher.ignore; TUI refreshes all location data with the event location. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTuWSW35XoRbPYixZr8oKQ
Contributor
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
Contributor
|
The following comment was made by an LLM, it may be inaccurate: Based on my search results, I found a potentially related PR: Related PR:
Also note:
The most likely duplicate or related work is PR #43458, which already implements a reload command. You should verify if this PR #43538 is addressing a different aspect of reloading or if it duplicates that functionality. |
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
- The debounce was a leading-edge lock held for the whole reload: the first event armed it and everything arriving during the 200ms wait *and* the instance rebuild was discarded with no re-check, so an edit made while a reload ran never loaded. Replaced with a trailing-edge debounce plus a dirty flag the driver re-checks after each reload. Extracted as schedule/settle so the sequence is unit tested. - location-services registered every LayerMap in module state and never released it, and the ref index grew forever even after LayerMap evicted the entry. Registration is now acquireRelease-scoped to the layer, and refs are indexed by directory and dropped on invalidate. Also removes cross-test contamination: every test that built a map left a live entry behind. - Treat opencode.json[c] at every level between the instance directory and the worktree root as config - ConfigPaths.files loads all of them - and honour OPENCODE_CONFIG when it points inside a watched config directory. - Carry the project through the hot-reload reload so the emitted server.instance.disposed event is stamped like every other disposal. - Log refresh failures in the TUI instead of swallowing them; a silent rejection leaves exactly the stale skill list hot reload exists to replace. - Document that a config directory must exist at startup to be watched, and that a reload interrupts an in-flight turn.
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.
Issue for this PR
Closes #8751
Type of change
What does this PR do?
Opt-in hot reload behind
OPENCODE_EXPERIMENTAL_HOT_RELOAD=true..opencodedirs from config entries), so changes there emitfile.watcher.updatedeven when the whole-project watcher is off.HotReloadservice, armed byInstanceStoreafter each boot, listens for those events, filters to config-relevant paths (files under config/skill directories,opencode.json[c]anywhere), debounces 200ms, then reloads through the existingInstanceStore.reloadand invalidates the v2 location-service layer so both stacks reread disk state.server.instance.disposed; the TUI additionally refreshes its v2 agent/command/skill stores on that event.This is also one answer to the
QUESTION(Dax)comment inpackages/core/src/skill.ts: the reload policy here is "drop the location layer on watch events" rather than per-cache invalidation.Known limitations: file events landing in the short window between dispose and re-arm are missed; external skill directories are watched only once discovered (each reload re-discovers); the watcher arms when v2 location services build, which every TUI/desktop/web client does immediately.
How did you verify your code works?
End to end on Windows with
opencode serveand the flag set, while the server was running:/skilland/api/skillwithout restart.opencode/command/hotcmd.md— appears in/commandserver.instance.disposedon the/eventSSE stream on each reloadTests:
Built and tested with Claude assistance; I reviewed and verified the changes.
Checklist