Repository navigation
feat: manage Claude Code and Codex plugins from Settings → Plugins - #580
Merged
Merged
Conversation
Surface each native CLI's own plugin system as a Tcode capability (Principle 1): Claude Code through `claude plugin … --json` and Codex through a temporary `codex app-server`, one bounded process per operation. Tcode persists no plugin state; the native files stay the truth and the host keeps an in-memory catalog per profile with Fresh/Stale/Error state. Contract (crates/agent): ProviderPluginEntry / PluginInstallation (one per native scope) / DeclaredComponents / PluginAction, with Caps.plugin_management declaring what each adapter implements. Actions are computed by the host for the listing's project context; the UI renders only those and never branches on the provider kind. Trust: `-y` is never passed and `--dangerously-bypass-hook-trust` is never used. A marketplace-declared install command surfaces as a PluginChallenge carrying the CLI's own text and sha256; acceptance is bound on the host to that pending operation and re-runs with `--accept-command`. Marketplace removal that would uninstall plugins (Claude) is confirmed first; for Codex, whose `marketplace/remove` would orphan installed plugins, Remove is offered only when nothing is installed from it. The management process strips CLAUDECODE, CLAUDE_CODE_ENTRYPOINT and CLAUDE_CODE_CHILD_SESSION, which make the CLI ignore `--accept-command`. Protocol v8: ProvidersStatus.plugins catalogs, eight plugin commands, plugin operation toasts. Command cache: `commands-<provider>.json` is now keyed by provider and the profile's native home and invalidated after a plugin change, so one profile's plugin commands no longer seed another home's menus. Draft seeding before the provider starts is kept. UI: Settings → Plugins, one section per enabled profile, with installed rows per scope, declared-component details, Add plugin and marketplace dialogs, and the challenge dialogs rendered from replicated state. Verified live against claude 2.1.285 and codex-cli 0.159.3 in isolated homes (fixtures recorded under crates/agent/tests/fixtures/plugins).
This was referenced Oct 3, 2026
Settings gains a machine-owned `plugins` group: a master switch and one switch per provider kind, keyed by the provider's settings key so a newer build's provider survives a load. Defaults: master on, Codex on, every other provider off, so an existing settings.json stays free of the key. Off means Tcode never touches that CLI's plugin state: the host accepts a refresh as a no-op, starts no plugin process or app-server, rejects mutations and challenge answers with `plugin_management_disabled`, abandons a pending challenge, forgets the catalog and omits it from the projection. Switching on refreshes from the page, which owns the project context. Settings → Plugins shows the master switch at the top and a switch in each provider section; an off section collapses to its header and one line.
Codex 0.159.3 ships a native computer-use feature (`codex features list` reports `computer_use stable`). Principle 1: the native capability is surfaced instead of a Tcode equivalent, so `Caps.native_computer_use` withholds the tcode_computer_use MCP registration (and its token) from Codex sessions. Settings → Computer Use, the provider card and the provider picker say so.
…-merge # Conflicts: # crates/agent/examples/probe.rs # crates/agent/src/lib.rs # crates/protocol/src/lib.rs # crates/services/src/store.rs
…-merge2 # Conflicts: # crates/runtime/src/app/store_write.rs # crates/services/src/store.rs
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
Tcode now surfaces each native CLI's own plugin system (Principle 1) on one Settings page. This PR covers the contract, the protocol and full lifecycle management for Claude Code and Codex. pi, OpenCode, Grok and Cursor follow in later PRs.
Rules applied: every action runs through a non-interactive native path, never
-yor--dangerously-bypass-hook-trust; native confirmations reach the user verbatim; TUI-only actions are skipped and say so. Native files remain the truth, the catalog is never persisted and is re-listed after every mutation.Two findings worth knowing:
CLAUDECODE,CLAUDE_CODE_ENTRYPOINTandCLAUDE_CODE_CHILD_SESSION. With them set the CLI silently ignores--accept-command(bisected per variable).marketplace/removeleaves installed plugins active but unlisted, so Remove is only offered when nothing is installed from that marketplace.Deviation from the plan, please confirm. The plan said "drop command cache pre-seeding". That would have removed
/commands from a new thread's first message, so insteadcommands-<provider>.jsonis keyed by provider and native home and invalidated after a plugin change.Evidence
After: full Claude lifecycle (marketplace add, install user/project, command-source install → accept, disable/enable, update, uninstall, marketplace remove → confirm) and full Codex lifecycle driven from the desktop app against isolated homes. Screenshots reviewed in both themes at wide, narrow and compact geometry.
cwds, never contain bypass flags, only the first-start sqlite race is retried); runtime with a fakeclaude(a stale challenge hash is refused; a plugin change invalidates only its home's cache); headlessSettingsPage(only host-computed actions render, Accept answers exactly one op).Not run locally: iOS, Android, Web and Windows CI jobs.
Merge Danger
Door: two-way
Nothing is persisted by Tcode; native plugin files are the only state and are written by the CLIs themselves. Reverting removes the page and the protocol fields.
Blast Radius: protocol
PROTOCOL_VERSION7 → 8, so every client must update with the host. The branch is currently conflicting withmainon that constant (feat: add Cursor and Grok as native providers over ACP dialects #584 and feat(orchestrate): move the bundled fleet into fleet.json #586 moved it since).cwds.detailscall per installed plugin (~0.8 s each, 4 concurrent).openai-curated-remote) install and uninstall follow the schema only and were not exercised against a real account.Update: master and per-provider switches (04bc273)
Settings gains a machine-owned
pluginsgroup: a master switch plus one switch per provider kind (keyed by the provider's settings key so a newer build's provider survives a load). Defaults: master on, Codex on, every other provider off; an untouched settings.json stays free of the key.Off means Tcode never touches that CLI's plugin state: refresh is accepted as a no-op, no plugin process or app-server is started, mutations and challenge answers are rejected with
plugin_management_disabled, a pending challenge is abandoned, the catalog is forgotten and omitted fromProvidersStatus.plugins. Switching on refreshes from the page (which owns the project context). The page shows the master switch at the top and a switch in each section; an off section collapses to its header and one line.Tests: runtime (default Claude off → refresh starts no process and install is refused; patch on → lists; patch off → catalog gone and the old challenge is unknown), core (literal legacy JSON and partial
pluginsobjects, unknown provider key tolerated, master off wins), UI headless (off section renders only its switch; clicking it patches settings; replicated settings trigger a refresh and show Add). Checks on the committed tree in a clean worktree: fmt ✓, clippy ✓, nextest 885 passed / 6 skipped, machete ✓.Update: Tcode computer use is not attached to Codex (cd39b87)
codex features liston 0.159.3 reportscomputer_use stable, so Codex has its own computer use. Per Principle 1,Caps.native_computer_usewithholds thetcode_computer_useregistration (and its token) from Codex sessions; preview/orchestrate/report are unchanged. Settings → Computer Use, the provider card and the provider picker show "Codex has its own computer use; Tcode's computer-use tools are not attached." The existingsession_launch_preserves_approval_policy_and_scopes_mcp_registrationstest now asserts Codex gets no computer-use registration while Claude does. Checks on the committed tree in a clean worktree: fmt ✓, clippy ✓, nextest 885 passed / 6 skipped, machete ✓.Note on CI: GitHub recorded no
pull_requestworkflow runs for this PR's pushes; the PR was closed and reopened to trigger the workflow.Update: merged main (53be54b)
Main added the Cursor and Grok provider kinds and Principle 9 (the protocol version is bumped at release, never in the PR). Resolution:
PROTOCOL_VERSIONstays at main's 9 with the wire change recorded as// Unreleased: provider plugin catalogs, their commands, challenges and switches.(our earlier 7→8 bump is withdrawn). Cursor and Grok caps rows getplugin_management: NONEandnative_computer_use: false(no verified evidence; Grok's plugin CLI is mapped in the plan but its adapter is not implemented), their command caches use the per-home key, and the computer-use note iteratesProviderKind::NATIVEinstead of a hand-written list. Checks on the merge commit: fmt ✓, clippy ✓,cargo check -p agent --no-default-featureswith-D warnings✓, nextest 907 passed / 6 skipped, machete ✓.Wire change: yes (ProvidersStatus.plugins, eight plugin commands, plugin toasts, settings
pluginsgroup) — noted under Unreleased per Principle 9.