Skip to content

feat: manage Claude Code and Codex plugins from Settings → Plugins - #580

Merged
Tryanks merged 5 commits into
mainfrom
feat/provider-plugins
Oct 6, 2026
Merged

Tryanks merged 5 commits into
mainfrom
feat/provider-plugins

Conversation

@Tryanks

@Tryanks Tryanks commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

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.

Settings → Plugins
  per provider profile
    context line (project or user-only)
    status (loading / stale / error verbatim)
    installed plugins
      per-scope sub-rows, only host-computed actions
      details (declared components, diagnostics)
    Add plugin picker
    marketplaces
  challenge dialogs (rendered from replicated state, survive reconnect)
    accept-command: CLI's own text, command, sha256
    destructive confirm: lists the uninstalls
crates/
├── agent/src/
│   ├── lib.rs                # ProviderPluginEntry, PluginAction, Caps.plugin_management
│   ├── claude_plugins.rs     # `claude plugin … --json` per operation
│   └── codex/plugins.rs      # temporary `codex app-server` per operation
├── protocol/                 # ProvidersStatus.plugins, 8 commands, PROTOCOL_VERSION 7 → 8
├── runtime/src/app/plugins.rs  # in-memory catalog per profile, one op at a time, challenge binding
└── ui/src/plugins_settings.rs  # the page above

Rules applied: every action runs through a non-interactive native path, never -y or --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:

  • The Claude management process strips CLAUDECODE, CLAUDE_CODE_ENTRYPOINT and CLAUDE_CODE_CHILD_SESSION. With them set the CLI silently ignores --accept-command (bisected per variable).
  • Codex marketplace/remove leaves 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 instead commands-<provider>.json is keyed by provider and native home and invalidated after a plugin change.

Evidence

  • Before: no plugin management in Tcode; plugins had to be managed from each CLI's TUI.
    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.
  • Tests: Claude adapter on recorded fixtures (grouping by scope, challenge from the last JSON line, argv never auto-accepts); Codex adapter on recorded frames (requests carry cwds, never contain bypass flags, only the first-start sqlite race is retried); runtime with a fake claude (a stale challenge hash is refused; a plugin change invalidates only its home's cache); headless SettingsPage (only host-computed actions render, Accept answers exactly one op).
cargo fmt / clippy -D warnings / machete         ✓
cargo nextest run --workspace --locked           883 passed, 6 skipped
cargo check -p agent --no-default-features       ✓

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

Update: master and per-provider switches (04bc273)

Settings gains a machine-owned plugins group: 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 from ProvidersStatus.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 plugins objects, 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 list on 0.159.3 reports computer_use stable, so Codex has its own computer use. Per Principle 1, Caps.native_computer_use withholds the tcode_computer_use registration (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 existing session_launch_preserves_approval_policy_and_scopes_mcp_registrations test 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_request workflow 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_VERSION stays 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 get plugin_management: NONE and native_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 iterates ProviderKind::NATIVE instead of a hand-written list. Checks on the merge commit: fmt ✓, clippy ✓, cargo check -p agent --no-default-features with -D warnings ✓, nextest 907 passed / 6 skipped, machete ✓.

Wire change: yes (ProvidersStatus.plugins, eight plugin commands, plugin toasts, settings plugins group) — noted under Unreleased per Principle 9.

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).
@Tryanks Tryanks changed the title feat: manage provider-native plugins from Settings → Plugins feat: manage Claude Code and Codex plugins from Settings → Plugins Oct 6, 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.
@Tryanks Tryanks closed this Oct 6, 2026
@Tryanks Tryanks reopened this Oct 6, 2026
@Tryanks
Tryanks marked this pull request as ready for review October 6, 2026 07:35
…-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
@Tryanks
Tryanks merged commit 1ef55bd into main Oct 6, 2026
7 checks passed
@Tryanks
Tryanks deleted the feat/provider-plugins branch October 6, 2026 08:26
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.

1 participant