docs/plans/2026-07-01-extension-runtime-refresh-implementation.md
Tracking issue: https://github.com/QwenLM/qwen-code/issues/3696
Working branch: feat/extension-runtime-refresh
Qwen Code should keep the existing user experience where extension UI mutations take effect automatically. Installing, updating, enabling, disabling, or uninstalling an extension should continue to refresh runtime state without requiring the user to run a manual command.
The remaining work is to make that automatic path complete, cheaper, and more
visible to the model. A separate /reload-plugins path should be added later
for external file changes that Qwen Code did not initiate, such as editing
installed extension files by hand.
ExtensionManager already refreshes part of the runtime after extension
mutations:
enable/disable/install/update/uninstall
-> ExtensionManager.refreshTools()
-> refreshMemory()
-> ToolRegistry.restartMcpServers()
-> SkillManager.refreshCache()
-> SubagentManager.refreshCache()
-> refreshHierarchicalMemory()
This is useful but incomplete:
Extension UI mutations should remain automatic. This differs from Claude Code's
plugin flow, where plugin mutations primarily set a stale flag and ask the user
to run /reload-plugins.
For Qwen Code, automatic mutation refresh is the better default because it preserves the current UX: when a user toggles an extension, the extension should actually become usable or unusable in the running session.
/reload-plugins should be a repair/resync path for file changes outside Qwen
Code's mutation APIs:
This command should reuse the same runtime refresh orchestration as automatic mutations.
Refreshing runtime state is not enough. The model also needs an accurate view of which capabilities are available.
Runtime refresh updates what can actually execute. Model notification updates what the model believes it can call. These are related but separate concerns.
Extension skills are loaded by SkillManager.refreshCache() from active
extensions. SkillTool.refreshSkills() updates the in-memory runtime sets.
Problem: the model-facing <available_skills> path is currently add-only after
startup. Disabling an extension removes the skill from runtime state, but the
old listing may remain in conversation history.
Planned behavior:
SkillManager.refreshCache();Extension commands are user-visible slash commands by default. Some are also
model-visible when modelInvocable === true; those enter the Skill tool's
available-skill list and can be invoked by name through SkillTool.
Problem: there is no core command manager. Command reload is a CLI/UI-layer operation, and extension mutations do not have a clear command refresh contract.
Planned behavior:
Extension MCP server config is merged through Config.getMergedMcpServers().
Problem: extension mutations currently call restartMcpServers(), which
restarts and rediscovers all MCP servers.
Planned behavior:
Extension agents are loaded by SubagentManager.refreshCache(). AgentTool
listens for subagent changes and updates its description and subagent_type
schema enum, then calls geminiClient.setTools().
Problem: this updates runtime/tool schema, but there is no explicit conversation reminder for added or removed agent types. In practice, the model may not notice new extension agents unless prompted.
Planned behavior:
SubagentManager.refreshCache() and AgentTool schema refresh;agent_listing_delta;Agent tool's subagent_type parameter.Extension lspServers can be read by the LSP config loader.
Problem: LSP startup currently initializes the native LSP service once. Runtime extension changes do not reinitialize LSP server configuration.
Planned behavior:
lsp tool and server state is better surfaced through status and
tool results.Extension hooks should be treated as runtime extension components.
Problem: extension mutation refresh does not currently have a clear hook reload step.
Planned behavior:
Create a small shared orchestration function used by extension mutations.
Scope:
/reload-plugins;Expected value:
Extend the orchestrator to refresh all runtime components that should update after extension UI mutations.
Scope:
Expected value:
Replace full MCP restart with incremental reconcile for extension-driven changes.
Scope:
setTools() still runs through the
MCP update path.Expected value:
Fix stale model-visible capability lists.
Scope:
Expected value:
/reload-pluginsAdd a manual reload command for external extension file changes.
Scope:
Expected value:
Detect external extension file changes and notify the user.
Scope:
qwen-extension.json;.qwen-extension-install.json;commands/**;skills/**;agents/**;hooks/**;GEMINI.md;/reload-plugins;Expected value:
<available_skills> model./reload-plugins behave in non-interactive mode, if at all?Use this document to keep PR scope narrow. Each PR should update this file when its part of the plan is completed or intentionally changed.