Skip to content

feat: plugin / MCP / IDE management — editable UX + hot refresh (#44 #47 #49 #41) - #87

Merged
Crsei merged 5 commits into
Crsei:rust-litefrom
yaohaowei0914:claude/xenodochial-banzai-f1e7c8
Apr 21, 2026
Merged

feat: plugin / MCP / IDE management — editable UX + hot refresh (#44 #47 #49 #41)#87
Crsei merged 5 commits into
Crsei:rust-litefrom
yaohaowei0914:claude/xenodochial-banzai-f1e7c8

Conversation

@yaohaowei0914

Copy link
Copy Markdown
Contributor

Summary

Ships the four epic-related commands as a coordinated set so they share one "server list + enable/disable + edit config" foundation:

What changed (by commit)

  1. 2f23599 foundation — IPC protocol extensions (McpCommand::{QueryConfig, UpsertConfig, RemoveConfig}, McpEvent::{ConfigList, ConfigChanged, ConfigError}, PluginCommand::{Reload, Uninstall}, PluginEvent::{RefreshNeeded, Reloaded}, new IdeCommand/IdeEvent, ConfigScope, McpServerConfigEntry, IdeInfo), reload_plugins() primitive in plugins/refresh.rs with ReloadReport, shared ServerListEditor.tsx React component.
  2. 2deb29c /reload-plugins (Team C) — new reload_plugins_cmd.rs, registered in commands/mod.rs, output format Reloaded N plugin(s) in Mms with per-error lines.
  3. 0596364 /ide (Team D) — new ide/mod.rs (PATH + TERM_PROGRAM heuristics, selectedIde persistence, stdio MCP bridge config), new ide_cmd.rs, cc-mcp::discovery::set_ide_hook merges the IDE's bridge between plugins and user settings.
  4. ea0301f /mcp editable (Team A) — full command rewrite with list/status/add/edit/remove/connect/disconnect/reconnect, scope-aware discovery via ScopedMcpServer + DiscoveryScope, build_mcp_server_config_entries attributes real origin scope, handler persists to {data_root}/settings.json or {cwd}/.cc-rust/settings.json, read-only scope rejection.
  5. 2506501 /plugin layered (Team B) — command rewrite with installed/disabled/errors/status/uninstall [--purge], new uninstall_plugin() + needs_refresh() + compute_drift() in plugins/mod.rs, handler wires PluginCommand::Uninstall to emit StatusChanged { status: "not_installed" }.

Reconciliation notes

Three of the four parallel teams (A, B, D) started from a worktree that didn't include the foundation commit, so each independently rebuilt portions of the IPC types. The merge keeps the foundation's shape as canonical:

  • PluginEvent::Reloaded { count, had_error } (not B's {count, added, removed, updated} delta variant).
  • McpEvent::ConfigError { server_name, error } (not A's message).
  • ConfigScope::User (not D's Global).

Team A's DiscoveryScope::Ide(String) and Team D's set_ide_hook coexist: IDE configs inject via the hook and get tagged with Ide scope at discovery time.

Test plan

  • cargo build -p claude-code-rs — clean (0 new warnings; only 2 pre-existing web/handlers.rs dead_code).
  • cargo test -p claude-code-rs --bin claude-code-rs -- --test-threads=11352/1352 pass.
  • cargo test -p cc-mcp27/27 pass.
  • cargo test ipc::subsystem plugins::refresh commands::mcp_cmd commands::plugin_cmd commands::reload_plugins_cmd commands::ide_cmd ide — all targeted tests green.
  • Known: 12 pre-existing global-state races visible only under parallel testing (unrelated to this PR — config_cmd, daemon, teams, tools::worktree). Serial-test run is green.

Not in scope (follow-ups)

  • React views wiring ServerListEditor for /mcp, /plugin, /ide. The component ships with the foundation and the TS protocol types are all mirrored; only the view-layer glue is deferred.
  • Live reconnect semantics for /ide reconnect (currently schedules via ConnectionStateChanged; the MCP manager picks up on next discovery pass).
  • Process-list scanning for IDE running (currently env-only; would need sysinfo).
  • Dispatch-table cache invalidation after /reload-pluginsQueryEngine keeps a long-lived tools snapshot; the existing /plugin enable|disable path has the same limitation and this PR matches that behavior. Documented in reload_plugins_cmd.rs.

🤖 Generated with Claude Code

Crsei and others added 5 commits April 21, 2026 09:03
…rsei#47 Crsei#49 Crsei#41)

Shared infrastructure for the plugin/MCP/IDE management epic:

- IPC protocol: add McpCommand::{QueryConfig, UpsertConfig, RemoveConfig} +
  McpEvent::{ConfigList, ConfigChanged, ConfigError} so /mcp can round-trip
  editable entries distinct from runtime status. Add PluginCommand::{Reload,
  Uninstall} + PluginEvent::{RefreshNeeded, Reloaded} for hot-refresh.
  Introduce IdeCommand + IdeEvent + IdeInfo as a fresh subsystem.
- plugins::refresh: extract reload_plugins() primitive that wraps
  clear_plugins + init_plugins and emits PluginEvent::Reloaded on the bus,
  with a ReloadReport surfacing per-plugin error state. Wire it through
  PluginCommand::Reload in the IPC handler.
- ServerListEditor: generic React/opentui widget for list + cursor +
  action shortcuts + optional edit slot. Shared by /mcp, /plugin, /ide.

Scope: foundation only. Each of the four slash commands remains a
text-oriented stub; Team A-D pick up the feature work in follow-ups.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the foundation primitive plugins::reload_plugins() in a new slash
command handler. The command emits "Reloaded {count} plugin(s) in
{duration_ms}ms." and appends one error line per failing plugin when
ReloadReport::errors is non-empty.

Register the command next to /plugin in the command registry and add
it to the TS autocomplete catalog so tab-completion picks it up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges Team D's /ide work on top of the foundation commit, reusing the
IPC types (IdeCommand, IdeEvent, IdeInfo, ConfigScope::Ide) landed in
2f23599 instead of Team D's parallel duplicates.

- New `ide` module (crates/claude-code-rs/src/ide/mod.rs): detect_ides()
  via PATH + TERM_PROGRAM heuristics for vscode, cursor, and JetBrains;
  select_ide / clear_selection / selected_ide persisted under
  `selectedIde` in {data_root}/settings.json; ide_mcp_config builds a
  stdio bridge config; emits Ide events via EVENT_TX.
- New `/ide` slash command (commands/ide_cmd.rs): detect, status, select,
  clear, reconnect subcommands.
- cc-mcp::discovery: new set_ide_hook mirrors set_plugin_hook; the host
  registers `ide::selected_ide_mcp_config` so the selected IDE's bridge
  merges between plugins and user settings.
- Real handle_ide_command wired to crate::ide::{select_ide,
  clear_selection, reconnect_selected}; build_ide_info_list now calls
  crate::ide::detect_ides.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges Team A's /mcp work on top of the foundation commit, reusing the
ConfigScope/McpServerConfigEntry types landed in 2f23599 and reconciling
with Team D's IDE hook.

- cc-mcp::discovery: scope-aware discovery via ScopedMcpServer +
  DiscoveryScope. New set_scoped_plugin_hook preserves the owning
  plugin id; IDE hook (issue Crsei#41) sits between plugins and user
  settings so user settings can still override.
- plugins: new discover_plugin_mcp_servers_scoped returns
  (plugin_id, config) pairs; legacy discover_plugin_mcp_servers
  delegates to it.
- /mcp command rewrite (commands/mcp_cmd.rs): list groups by scope
  with live status, add/edit accept --command/--arg/--env/--url/
  --transport/--scope/--browser, remove auto-picks the matching
  editable scope (or asks with --scope when ambiguous),
  connect/disconnect/reconnect queue runtime lifecycle ops.
- handle_mcp_command QueryConfig/UpsertConfig/RemoveConfig handlers
  land with real persistence (user → {data_root}/settings.json,
  project → {cwd}/.cc-rust/settings.json), editable-scope guard,
  and ConfigError on validation/IO failure.
- build_mcp_server_config_entries attributes each entry to its real
  origin scope (not all User as in the foundation stub).
- ConfigScope::label() helper for the /mcp list grouping output.

Kept from foundation / Team D: ConfigError field name (`error`),
PluginEvent::{RefreshNeeded, Reloaded}, IdeEvent, Plugin::{Reload,
Uninstall} handlers, handle_ide_command, build_ide_info_list.

Tests: handler-level upsert/remove round-trips (user/project), scope
rejection for read-only Plugin/Ide, slash-command add/edit/remove/
ambiguity, scoped-discovery precedence. All env-mutating tests are
#[serial_test::serial].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
)

Merges Team B's /plugin work on top of the foundation commit, keeping
the foundation's PluginEvent::Reloaded { count, had_error } shape and
ReloadReport (used by Team C's /reload-plugins) rather than Team B's
delta-oriented variant.

- /plugin command rewrite (commands/plugin_cmd.rs): layered table view
  with install/enabled/active columns; new subcommands
  list/installed/disabled/errors/status/enable/disable/
  uninstall [--purge]/help; drift-aware post-action hints emit
  PluginEvent::RefreshNeeded via plugins::emit_event_external.
- plugins::mod: new uninstall_plugin(id, purge_cache), needs_refresh(),
  compute_drift() with DriftReport, status_variant_differs(),
  cache_path_for() (handles `cache/{mp}/{name}/{version}` and fallback
  layouts), and emit_event_external().
- Cache purge removes the plugin-level directory (one level above the
  version subdir) so --purge wipes every cached version.
- handle_plugin_command::Uninstall now calls uninstall_plugin and emits
  PluginEvent::StatusChanged { status: "not_installed" }; absent plugin
  returns a SystemInfo.
- Existing registry-touching tests marked #[serial_test::serial] so the
  new serial CC_RUST_HOME tests don't race with them.

Drift semantics: ids added/removed on disk, or PluginStatus variant
mismatch, counts as drift. Error-to-error status shifts are NOT drift
(messages can fluctuate on transient conditions; the existing
StatusChanged event carries the new message).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Crsei
Crsei merged commit de28245 into Crsei:rust-lite Apr 21, 2026
1 of 4 checks passed
yaohaowei0914 pushed a commit to yaohaowei0914/claude-code-rust that referenced this pull request Apr 21, 2026
Integrates the fork-agent infrastructure and ancillary command/IPC
updates that landed on rust-lite after this branch opened:

- PR Crsei#85: fork-agent infra + /btw /simplify /advisor (Crsei#33 Crsei#37 Crsei#62)
- PR Crsei#87: MCP/plugin/reload-plugins/ide/reload-plugins commands (Crsei#41 Crsei#44 Crsei#47 Crsei#49)
- PR Crsei#88: /loop /schedule /team-onboarding (Crsei#43 Crsei#58 Crsei#60 Crsei#63)

## Conflict resolution

Git's recursive merge handled every hunk on its own — each of the five
files flagged as overlapping had non-overlapping line ranges between
my workspace-split edits and rust-lite's feature edits:

- `crates/cc-engine/src/types/app_state.rs` — my move to cc-engine vs.
  advisor/fork-related AppState fields. Clean auto-merge.
- `crates/claude-code-rs/src/engine/agent/mod.rs` — my `cc_types::agent_*`
  import rewrites vs. rust-lite's `pub mod fork;` addition. Clean.
- `crates/claude-code-rs/src/engine/lifecycle/deps.rs` — my
  `cc_types::background_agents` retyping and `hook_runner()` impl vs.
  rust-lite's `advisor_model` stripping logic. Clean.
- `crates/claude-code-rs/src/query/deps.rs` — my `hook_runner()` /
  `drain_background_results` signature changes vs. rust-lite's new
  `ModelCallParams::advisor_model` field. Clean.
- `crates/claude-code-rs/src/query/loop_impl.rs` — my hook-runner-trait
  rewiring vs. rust-lite's advisor model plumbing. Clean.

All 40+ other touched files (api/*, commands/*, ide/*, ipc/*,
services/*, etc.) merged without conflict — they're in regions my
branch didn't touch.

## Verification

- `cargo check --workspace`: clean (2 pre-existing warnings).
- `cargo test -p cc-types`: 4/4 passing (teams + background_agents).
- `cargo test -p cc-engine`: status_line + types suite unchanged.
- `cargo test --bin claude-code-rs engine::agent query::loop_impl`:
  69/69 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants