/** * Suggestion Review Queue (Phase 2 — identification engine) * * Pure decision logic for the suggested/identity-correction tiers of * {@link AutoAssignResult}. Before this module every call site computed the * suggested tier and silently dropped it (Votifier→nuvotifier was the live * casualty); these helpers make the queue actionable from both the TUI * dialog (SourceSuggestionsDialog) and the CLI (`pluginator assign --review`). * * Everything here is pure (no IO, no state) so both surfaces share one * implementation of the accept/skip semantics and stay in lockstep. * * Privacy note: suggestion `detail` strings are produced upstream under the * watermark privacy rule (only {channel, resourceId} ever survive extraction) * — these helpers only pass them through, never enrich them. * * @since v2.13.0 (Phase 2 — identification engine) */ import type { AutoAssignResult, SourceAssignment } from './auto-source-types.js'; /** * Confidence at/above which bulk accept actions (dialog [A], CLI * `assign --accept-all`) will apply a suggestion. Below this, suggestions * must be accepted individually. */ export declare const HIGH_CONFIDENCE_THRESHOLD = 0.8; /** Which review tier a pending entry came from. */ export type SuggestionKind = 'suggestion' | 'identity-correction'; /** A pending review-queue entry: a {@link SourceAssignment} tagged with its tier. */ export interface SourceSuggestion extends SourceAssignment { kind: SuggestionKind; } /** Result of an accept decision: what to apply and what stays pending. */ export interface SuggestionDecision { /** Entries to run through `AutoSourceAssigner.applyAssignments`. */ accepted: SourceSuggestion[]; /** Entries still pending after the decision. */ remaining: SourceSuggestion[]; } /** * Stable identity for a pending entry: plugin + proposed channel + * identifiers. Two entries with the same key are the same proposal (possibly * reached via different methods) and collapse to one row. */ export declare function suggestionKey(suggestion: SourceAssignment): string; /** * Persisted dismissals: {@link suggestionKey} → confidence at the time the * user skipped it. Stored in the preferences file * (`PreferencesService.getDismissedSuggestions`); this module stays pure and * only consumes the map. */ export type DismissedSuggestions = Record; /** * Should this suggestion stay hidden? Dismissed entries stay hidden until * their evidence gets STRONGER — a higher confidence than the one the user * saw when skipping is new information and resurfaces the row. */ export declare function isSuggestionDismissed(suggestion: SourceAssignment, dismissed: DismissedSuggestions): boolean; /** * Merge an {@link AutoAssignResult}'s review tiers — plus optional * registry-correction suggestions from `proposeRegistryCorrections` — into a * single pending list. * * Ordering: identity corrections first (they contradict a CONFIGURED source — * the highest-stakes decision), then suggestions; descending confidence * within each tier, plugin name as the deterministic tiebreaker. Exact * duplicates (same {@link suggestionKey}) collapse keeping the * highest-confidence copy. * * `dismissed` (persisted skips from the preferences store) filters entries * the user already rejected — UNLESS the new suggestion's confidence is * higher than it was at dismissal time (see {@link isSuggestionDismissed}). */ export declare function collectSuggestions(result: Pick, registrySuggestions?: SourceAssignment[], dismissed?: DismissedSuggestions): SourceSuggestion[]; /** * Accept the entry at `index`. Other pending entries for the SAME plugin are * dropped from `remaining` — once the accepted source is applied they are * stale alternatives, not open questions. Out-of-range index is a no-op * (`accepted` empty, `remaining` unchanged). */ export declare function acceptSuggestionAt(suggestions: SourceSuggestion[], index: number): SuggestionDecision; /** * Skip (dismiss) the entry at `index` — removed from the pending list without * being applied. Persistence is the CALLER's job (this module is pure): the * dialog/CLI records `suggestionKey(entry) → entry.confidence` via * `PreferencesService.dismissSuggestion`, and future `collectSuggestions` * calls pass the persisted map so the entry stays hidden until its * confidence increases. Out-of-range index returns the list unchanged * (same reference, so callers can cheaply detect the no-op). */ export declare function skipSuggestionAt(suggestions: SourceSuggestion[], index: number): SourceSuggestion[]; /** * Accept the highest-confidence pending entry for `pluginName` * (case-insensitive; list-order wins ties). The CLI's `--accept ` * path. No match → `accepted` empty, `remaining` unchanged. */ export declare function acceptSuggestionsByName(suggestions: SourceSuggestion[], pluginName: string): SuggestionDecision; /** * Bulk accept: for each plugin, take its highest-confidence entry when that * entry is at/above `threshold` (other entries for the plugin drop as stale). * Plugins whose best entry is below threshold keep ALL their entries pending * — a low-confidence guess is exactly what the per-row review is for. */ export declare function acceptAllHighConfidence(suggestions: SourceSuggestion[], threshold?: number): SuggestionDecision; /** * Proposed target summary: `spigot/13932`, `modrinth/luckperms`, * `github/owner/repo` — or just the type when there are no identifiers * (e.g. uncheckable-store suggestions that propose `manual`). */ export declare function describeProposedSource(assignment: SourceAssignment): string; /** * One-line rendering for CLI output and logs: * `! Floodgate: modrinth/floodgate → geysermc/floodgate (1.00, registry) — detail` * (`!` identity correction, `+` suggestion; `prev → new` only when the entry * carries a previous source). */ export declare function formatSuggestionLine(suggestion: SourceSuggestion): string; //# sourceMappingURL=suggestion-review.d.ts.map