Expand description
The PluginInstaller trait — the method surface later agents implement.
This crate lives at the infra layer and has no access to AppState
(bamboo-server, an app-layer crate). Actually registering capabilities
— merging into config.json, calling mcp_manager.start_server, appending
to prompt-presets.json, writing workflow files — all need AppState (or
equivalent handles), so a REAL implementation of this trait has to live in
bamboo-server (or a sibling crate that depends on it), implemented by a
later agent (see PLUGIN_PLAN.md § Installer-core agent). Because the
trait is foreign there and the implementing type is local, that’s a
perfectly ordinary downstream impl — no orphan-rule issue.
LocalPluginInstaller below is a reference skeleton that implements
everything this crate CAN implement without AppState (manifest
validation, platform gating, MCP-entry token resolution, the
disposition/upgrade decision, the provides.skills-authoritative check,
provenance listing) and returns PluginError::NotImplemented at the
exact points that need capability-registration wiring, with a comment
enumerating what goes there and citing the exact files. It is a
reference/example, not a requirement to reuse verbatim — Wave-2’s
installer-core agent may replace it entirely with a type that holds an
AppState handle.
§Ownership + upgrade contract (why uninstall is provably safe)
Two invariants make uninstall/upgrade never touch a user’s own entries:
- Only plugin-created entries are ever recorded as removable. For the
REFUSE-on-conflict capabilities (MCP servers, workflow files) the
installer MUST run
crate::registry::reconcile_exclusiveagainst the live shared store before touching anything: a declared id/filename that already exists and is not owned by this plugin lands inforeign_conflictsand the install is REFUSED (PluginError::Conflict) — it is never registered and never written intocrate::registry::RegisteredCapabilities. So theregisteredset anInstalledPlugincarries contains ONLY entries this plugin genuinely created;uninstalliterating that set can only ever delete the plugin’s own entries. (Prompt presets are the one exception: they rename on collision via bamboo-server’sensure_unique_preset_idinstead of refusing, and the RENAMED id is what gets recorded.) - Upgrade de-registers what the new version dropped.
installwithInstallDisposition::Upgradeloads the priorInstalledPlugin, computescrate::registry::RegisteredCapabilities::removed_since(old minus new), de-registers those dropped capabilities, THEN registers the new set and upserts provenance — so a capability the old version had and the new one dropped can’t leak as an orphan.
Structs§
- Local
Plugin Installer - Reference skeleton — see module docs. Holds only a
bamboo_dirso tests can point it at a tempdir instead of the real~/.bamboo.
Enums§
- Install
Disposition - How
PluginInstaller::installmust treat a plugin id that is ALREADY installed. Maps directly to the CLI verbs:bamboo plugin installusesSelf::FailIfInstalled,bamboo plugin update(orinstall --force) usesSelf::Upgrade.
Traits§
- Plugin
Installer - The installer method surface.
install/uninstall/listare the verbs the CLI (bamboo plugin install/list/remove/update) and the HTTP routes (/api/v1/plugins) both call through.
Functions§
- load_
previous_ for_ disposition - Loads prior provenance for
plugin_idfrominstalled_json_pathand applies theInstallDispositiongate:InstallDisposition::FailIfInstallederrorsPluginError::AlreadyInstalledwhen a COMPLETED entry already exists;InstallDisposition::Upgradepasses through either way. Returns the previous entry (Nonefor a fresh install), which the caller needs for the upgrade drop-diff (BLOCKER 2) and for crash recovery. - on_
disk_ skill_ dirs - Directory names directly under
<plugin_dir>/skills/that contain aSKILL.md(i.e. what discovery would actually pick up in place). Used bypreflight_installto enforce thatprovides.skillsis authoritative (MAJOR 4). - preflight_
install - Everything an
installcan validate/resolve WITHOUT touchingAppState: manifest validation, platform gating, per-entry MCP resolution (so a malformed entry — e.g. an empty stdio command — fails fast before any registration happens), and theprovides.skills/provides.workflowson-disk existence + authoritative checks (MAJOR 4).