feat(tui): suggest plugins from the prompt, not only /plugin suggest - #5663
feat(tui): suggest plugins from the prompt, not only /plugin suggest#5663Hmbown wants to merge 2 commits into
Conversation
Sending a task now toasts the next review step when the prompt strongly matches an installed-but-idle plugin or a marketplace catalog the user added. Description-only matches stay quiet. /plugin suggest ranks manifest keywords and catalog candidates the same way and still never installs, trusts, or enables anything. On-disk plugin changes also nudge /plugin reload between turns, not only on send. Plugin skills already reach the model catalogue with their descriptions once trusted and enabled; the test now asserts that. Does not close #5579: ranking is still local metadata, not a model route, and there is no inotify watcher.
|
Claude finished @Hmbown's task in 3m 0s —— View job Claude's review
SummarySolid, well-scoped follow-up to #5579. The read-only/no-side-effect invariant ( Findings1. Proactive threshold can fire on generic single-word name fragments, not just real keyword/name matches (
2. Both functions open the Nothing blocking; #1 is the one worth a second look before merge since it's a real precision gap in the "never nag" design goal. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92b28b49cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for candidate in marketplace { | ||
| if candidate.has_errors() { | ||
| continue; |
There was a problem hiding this comment.
Exclude unsupported candidates from install suggestions
When a catalog retains a valid candidate whose install_plan is MarketplaceInstallPlan::Unsupported—for example, a Codex entry marked NOT_AVAILABLE—has_errors() can still be false, so this candidate is ranked and presented as /plugin marketplace install .... The marketplace install handler then unconditionally rejects that command as unsupported, making both /plugin suggest and the proactive toast recommend an action that cannot succeed; filter on install_plan.is_supported() or present a non-install action instead.
Useful? React with 👍 / 👎.
| ( | ||
| format!("installed:{}", plugin.name()), | ||
| RegistryEntry { |
There was a problem hiding this comment.
Keep index bookkeeping out of semantic name matching
When a prompt contains the ordinary word installed, such as “use the installed plugin to handle this,” recommend_remote_skills tokenizes this synthetic map key as a plugin name and awards every installed bundle a name-match score above the proactive threshold. The result can be a toast for an arbitrary alphabetically ranked inactive plugin despite no task-specific match; use an opaque key that the matcher does not score, or pass the actual plugin name separately from the collision-avoidance key.
Useful? React with 👍 / 👎.
| let index = RegistryDocument { | ||
| skills: entries.iter().cloned().collect::<BTreeMap<_, _>>(), | ||
| }; | ||
| let ranked = recommend_remote_skills(task, &index, options.limit.saturating_mul(2)); |
There was a problem hiding this comment.
Filter active plugins before truncating proactive results
In proactive mode limit is 1, so this requests only the top two ranked entries before later discarding already-active plugins. If two active plugins rank ahead of another strongly matching inactive plugin, both retained entries are filtered out and no toast is produced even though an eligible match exists; exclude active entries before ranking or rank enough entries to apply the eligibility filters before enforcing the requested limit.
Useful? React with 👍 / 👎.
| let Ok(state) = store.load() else { | ||
| return Vec::new(); | ||
| }; |
There was a problem hiding this comment.
Surface marketplace state failures from
/plugin suggest
When marketplaces.json is malformed, has an unsupported schema, or fails the store's permission checks, store.load() returns an error but this helper silently treats it as an empty catalog. The explicit /plugin suggest command can consequently report that no catalog plugin matched even though it searched incomplete state, unlike the marketplace commands that surface the fail-closed error; propagate the error to the command while allowing the unsolicited-toast path to remain quiet if desired.
Useful? React with 👍 / 👎.
| if let Some(message) = crate::plugins::plugin_reload_nudge( | ||
| self.plugin_registry.as_ref(), | ||
| &mut self.plugin_reload_nudge_stamp, | ||
| ) { |
There was a problem hiding this comment.
Fingerprint plugin contents during idle reload polling
When an existing plugin.toml, skill, or other file inside a bundle is edited in place, PluginCatalogStamp::capture still records only the containing plugin directory's modification time; on normal filesystems, changing file contents does not update that directory timestamp. This newly added idle poll therefore never emits the advertised reload nudge for such rewrites and detects only directory-entry changes such as adding or removing a bundle; include relevant file metadata or content in the live fingerprint.
Useful? React with 👍 / 👎.
| .and_then(|candidate| candidate.description.clone()) | ||
| .filter(|description| !description.trim().is_empty()) | ||
| .unwrap_or_else(|| "Catalog plugin.".to_string()), |
There was a problem hiding this comment.
Localize the new catalog suggestion copy
When the UI locale is not English, the newly added catalog suggestion path still renders English fallback and state text such as Catalog plugin. and not installed (along with the new English-only result framing), producing a partially untranslated /plugin suggest response despite complete locale packs. Add typed MessageId entries for this new prose and compose only commands and glyphs in code.
AGENTS.md reference: crates/tui/AGENTS.md:L22-L23
Useful? React with 👍 / 👎.
Keep the send-time toast. A Grok-style keyword matcher (word boundaries,
domains, longest keyword) drives a one-line composer CTA while you type:
Install {name} plugin? Review runs the existing /plugin trust,
marketplace-install, or /plugin install path and never auto-installs.
Dismiss stays dismissed for that name this session; already-active
plugins stay hidden.
Matching idle or catalog plugins also append a bounded
<recommended_plugins> user-turn block on send (not the pinned system
prefix). request_plugin_install surfaces that review for the human and
does not mutate disk.
|
Claude finished @Hmbown's task in 2m 24s —— View job Claude's review
SummaryThe new commit ( However, three issues flagged in the prior review round on Findings1. Unsupported marketplace candidates are still recommended as installable (unaddressed, now in 2 more places) —
2. Proactive toast path still scores on generic name-word fragments, including its own bookkeeping key (unaddressed) — The new precise matcher (
Given 3. New literals introduced for the catalog-candidate path — Nits (not blocking)
Nothing here is a regression introduced by this commit — findings #1 and #2 carry over from the prior review round on |
Summary
The remaining #5579 UX: if someone writes a prompt about Supabase and they have that plugin (or a catalog that lists it), toast the next step instead of making them type
/plugin suggest./plugin trust,/plugin enable, or/plugin marketplace install <catalog> <name>./plugin suggestuses the same ranking, including catalog candidates that are not installed yet./plugin reloadbetween turns (idle poll), not only on send.Does not close #5579: ranking is still local metadata (no cheap-model matcher), and the disk check is a fingerprint poll, not inotify.
Testing
cargo fmt --all -- --checkcodewhale-tui --libtests for ranking,/plugin suggest, send-time toast, and plugin skill catalogue copypython3 scripts/check-tui-locale-parity.pycargo clippy --workspace --all-targets --all-features --locked(CI)cargo test --workspace --all-features --locked(CI)Checklist