Skip to content

Add interactive selection for ambiguous package matches - #6575

Open
AmirMS (AmelBawa-msft) wants to merge 3 commits into
user/amelbawa/source-filterfrom
user/amelbawa/interactive-package-selection
Open

AmirMS (AmelBawa-msft) wants to merge 3 commits into
user/amelbawa/source-filterfrom
user/amelbawa/interactive-package-selection

Conversation

@AmelBawa-msft

@AmelBawa-msft AmirMS (AmelBawa-msft) commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

📖 Description

Let users resolve ambiguous matches without restarting single-package install, show, and download commands.

  • Display numbered candidates using the existing table formatter, preserving source-specific identities.
  • Apply source priority before prompting and continue with the selected package and original command options.
  • Support invalid-input recovery and cancellation with 0 or Ctrl+C.
  • Preserve ambiguity errors for noninteractive, silent, redirected, and truncated-result scenarios.

Includes localized strings, regression coverage, release notes, and a UX specification. Narrow terminals use existing table truncation.

🎞️ Demo

winget-vs-wingetdev-selection

🔗 References

After #6514 merges, rebase and retarget this PR to feature/multi-source-deduplication.

🔍 Validation

  • x64 Debug native builds passed.
  • Focused automated tests passed: 1,428 assertions across 74 test cases.
  • Deployed wingetdev passed 13 Windows ConPTY scenarios covering selection, versions, invalid/empty/long input, narrow-terminal output, cancellation, and noninteractive behavior.
  • Install/download terminal scenarios cancelled before acting on a package.

Full CI and manual GUI-terminal/accessibility testing remain outstanding.

✅ Checklist

🤖 AI Assistance

  • AI assistance was used and has been disclosed in this PR
  • No AI assistance was used

GitHub Copilot assisted with implementation, tests, documentation, code review, and local validation.

📋 Issue Type

  • Bug fix
  • Feature
  • Task
Microsoft Reviewers: Open in CodeFlow

@github-actions

This comment has been minimized.

@AmelBawa-msft
AmirMS (AmelBawa-msft) marked this pull request as ready for review September 28, 2026 23:13
@AmelBawa-msft
AmirMS (AmelBawa-msft) requested a review from a team as a code owner September 28, 2026 23:13

@JohnMcPMS JohnMcPMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like the code should be structured as such:

  • WorkflowBase :: Does the top-level decision on flow support for selection, generates table and decides on strings.
  • PromptFlow :: Takes in table+strings and determines if prompting is allowed/possible. This is a generic selection flow task. Handles output of the values, input validation and repitition.
  • Reporter :: Handles improved input with support for cancellation. Performs no output.

Yes, this also means refactoring the table output into non-templated functions. That is probably for the best anyway.


## UI/UX Design

Use the existing search table layout with a leading selection number. Show the Source column only when candidates span sources. For example:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disagree; we should show the source regardless. Just because they all come from FOO doesn't mean I as the user know they come from FOO.

Workflow::SearchSourceForSingle <<
Workflow::HandleSearchResultFailures <<
Workflow::EnsureOneMatchFromSearchResult(OperationType::Download) <<
Workflow::EnsureOneMatchFromSearchResult(OperationType::Download, true) <<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would greatly prefer this to be an enum so we know what we are asking for instead of just true. And to be clear and complete, I mean replace the use of bool for this semantic globally with something like enum class PackageSelectionBehavior.

namespace AppInstaller::CLI::Workflow
{
namespace
bool IsInteractivityAllowed(Execution::Context& context)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentionally hidden here so that all prompt flows would be in this path. I would consider this feature to be introducing a prompt. Why is it not in this file?

context <<
SearchSourceForSingle <<
SelectSinglePackageVersionForInstallOrUpgrade(m_operationType) <<
SelectSinglePackageVersionForInstallOrUpgrade(m_operationType, false, m_allowSelection) <<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put new non-optional parameters before optional parameters. You are supplying it here unconditionally, so it isn't really optional.

return m_consoleStreams && Info().IsEnabled();
}

std::optional<size_t> Reporter::PromptForSelection(size_t count, std::function<bool()> isCancelled)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should not be in the Reporter; we have PromptFlow.cpp. It requests user input already. If there are improvements to be made around waiting for input, it makes sense for the Reporter to own some of that but it should never have a function that is outputting resource strings for specific workflows.

bool selectionSupported = m_allowSelection &&
(m_operationType == OperationType::Install || m_operationType == OperationType::Show || m_operationType == OperationType::Download);
bool canSelect = selectionSupported && !searchResult.Truncated &&
!context.Args.Contains(Execution::Args::Type::Silent) &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have prior art for having the silent option suppressing interactive prompts? I think maybe the 🤖 thought it meant something it doesn't.

Comment thread src/AppInstallerCLICore/Workflows/WorkflowBase.cpp
auto source = available->GetSource();
std::string versionString = version ? version->GetProperty(PackageVersionProperty::Version).get() : std::string{};
std::string sourceName = source ? source.GetDetails().Name : std::string{};
line[3] = versionString.empty() ? Resource::LocString{ Resource::String::Unavailable }.get() : versionString;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It starts as the Unavailable value; don't assign that again.

line[3] = versionString.empty() ? Resource::LocString{ Resource::String::Unavailable }.get() : versionString;
line[4] = sourceName.empty() ? Resource::LocString{ Resource::String::Unavailable }.get() : sourceName;
lines.emplace_back(line);
line[0].clear();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

std::string sourceName = source ? source.GetDetails().Name : std::string{};
line[3] = versionString.empty() ? Resource::LocString{ Resource::String::Unavailable }.get() : versionString;
line[4] = sourceName.empty() ? Resource::LocString{ Resource::String::Unavailable }.get() : sourceName;
lines.emplace_back(line);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move?

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.

2 participants