Skip to content

Pre-select already-installed plugins in the installer TUI so re-runs don't start from scratch #11

Description

@fvoska

Background

The installer's interactive plugin picker (promptPluginMultiselect in bin/install.js) runs once per marketplace source — the base infinum/ai marketplace, plus any private overlays added via --extend. Each run shows a @clack/prompts multiselect of that source's plugins so you tick the ones you want.

Re-running the installer is the intended way to pick up updates (bundled rules have no auto-update; the stack-essentials hook explicitly nudges you to re-run — see #6). So this picker is something you hit repeatedly, not just once.

Problem

The plugin multiselect starts from an empty selection every time. On each re-run you have to re-tick every plugin you previously installed, from scratch, or you risk losing them. This makes updating tedious and error-prone.

Notably, the installer already does the right thing for rule bundles: selectBundles() reads the previously-selected bundles and passes them as initialValues, so bundle checkboxes come pre-checked with your prior choices (bin/install.js, the multiselect({ ..., initialValues }) call around line 550). Plugins just never got the same treatment — promptPluginMultiselect() builds its multiselect({ ... }) with no initialValues (around line 761).

Proposed improvement

Pre-select, per marketplace, the plugins that are already installed for that marketplace, so the checkboxes reflect my current setup and I only toggle deltas.

The clean part: the installer already knows how to query this, and doesn't need to start tracking plugin state to do it.

  • liveInstalledPluginIds() (bin/install.js ~line 861) runs claude plugin list --json and returns ids as <plugin>@<marketplace>. --remove already uses it, filtering by the @<marketplace> suffix.
  • promptPluginMultiselect(source, plugins) could compute the currently-installed plugin names for source.marketplaceName (filter live ids by @${source.marketplaceName}, strip the suffix) and pass them as initialValues, mirroring what selectBundles does for bundles.

This respects the deliberate design decision that the manifest does not track which plugins are installed — "that's Claude Code's state, read live when needed" (header comment, bin/install.js lines ~25-28, echoed at liveInstalledPluginIds). Live query = no new state to keep in sync, and it stays correct even if the user installs/uninstalls plugins directly via /plugin between runs.

Pointers

  • Empty-initialValues plugin picker: promptPluginMultiselect() in bin/install.js (~line 761)
  • The pattern to copy — bundles already pre-select: selectBundles() in bin/install.js (~line 517, initialValues at ~550)
  • Live installed-plugin query (<plugin>@<marketplace> ids): liveInstalledPluginIds() in bin/install.js (~line 861)
  • Per-source picker caller: installSelectedPlugins() in bin/install.js (~line 774)

Considerations

  • Interactive path only. This affects the TTY multiselect. Non-interactive runs already require an explicit --plugins <list> and are unaffected.
  • Installed vs. enabled. Decide whether pre-selection keys off "installed" or "installed and enabled" — a plugin the user installed but then disabled via /plugin probably shouldn't come back pre-checked. Check what claude plugin list --json exposes.
  • Graceful when the query fails. If claude plugin list --json errors or returns nothing (fresh machine, CLI hiccup), fall back to the current behaviour (empty selection) rather than blocking the picker.
  • Auto-installed plugins. stack-essentials (in AUTO_INSTALL_PLUGINS) is already filtered out of the base source's picker — make sure pre-selection doesn't reintroduce it into the list.

Contributions welcome — this is up for grabs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions