Aracılığıyla paylaş


Geri bildirim sağlayıcısı oluşturma

PowerShell 7.4, geri bildirim sağlayıcıları kavramını kullanıma sunar. Geri bildirim sağlayıcısı, kullanıcı komut yürütme girişimlerini temel alan komut önerileri sağlamak için IFeedbackProvider arabirimini uygulayan bir PowerShell modülüdür. Başarılı veya başarısız bir yürütme olduğunda sağlayıcı tetikleniyor. Geri bildirim sağlayıcıları, geri bildirim sağlamak için başarı veya başarısızlıkla ilgili bilgileri kullanır.

Önkoşullar

Geri bildirim sağlayıcısı oluşturmak için aşağıdaki önkoşulları karşılamanız gerekir:

  • PowerShell 7.4 veya üzerini yükleme
    • Geri bildirim sağlayıcıları ve tahminciler için destek sağlamak için deneysel PSFeedbackProvider özelliğini etkinleştirmeniz gerekir. Daha fazla bilgi için bkz. Deneysel Özellikleri Kullanma.
  • .NET 8 SDK-8.0.0 veya üzerini yükleme
    • SDK'nın en son sürümünü edinmek için .NET 8.0'ı indirme sayfasına bakın.

Geri bildirim sağlayıcısına genel bakış

Geri bildirim sağlayıcısı, System.Management.Automation.Subsystem.Feedback.IFeedbackProvider arabirimini uygulayan bir PowerShell ikili modülüdür. Bu arabirim, komut satırı girişine göre geri bildirim almak için yöntemleri bildirir. Geri bildirim arabirimi, kullanıcı tarafından çağrılan komutun başarısına veya başarısızlığına bağlı olarak öneriler sağlayabilir. Öneriler istediğiniz her şey olabilir. Örneğin, bir hatayı gidermenin yollarını veya diğer adların kullanılmasını önleme gibi daha iyi yöntemler önerebilirsiniz. Daha fazla bilgi için bkz. Geri Bildirim Sağlayıcıları nedir? blog gönderisi.

Aşağıdaki diyagramda bir geri bildirim sağlayıcısının mimarisi gösterilmektedir:

geri bildirim sağlayıcısı mimarisinin Diyagramı.

Aşağıdaki örnekler, basit bir geri bildirim sağlayıcısı oluşturma işleminde size yol gösterir. Ayrıca, komut satırı tahmincisi deneyimine geri bildirim önerileri eklemek için sağlayıcıyı komut tahmin aracı arabirimine kaydedebilirsiniz. Tahminciler hakkında daha fazla bilgi için bkz. PSReadLine'de tahmincileri kullanma ve komut satırı tahmincisi oluşturma.

1. Adım - Yeni bir sınıf kitaplığı projesi oluşturma

Proje dizininde yeni bir proje oluşturmak için aşağıdaki komutu kullanın:

dotnet new classlib --name MyFeedbackProvider

System.Management.Automation dosyanıza .csproj paketi için bir paket başvurusu ekleyin. Aşağıdaki örnekte güncelleştirilmiş .csproj dosyası gösterilmektedir:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Management.Automation" Version="7.4.0-preview.3">
        <ExcludeAssets>contentFiles</ExcludeAssets>
        <PrivateAssets>All</PrivateAssets>
    </PackageReference>
  </ItemGroup>
</Project>

Not

System.Management.Automation derlemesinin sürümünü, hedeflediğiniz PowerShell önizleme sürümüyle eşleşecek şekilde değiştirmeniz gerekir. En düşük sürüm 7.4.0-preview.3'dür.

2. Adım - Sağlayıcınız için sınıf tanımını ekleme

Class1.cs dosyasının adını sağlayıcınızın adıyla eşleşecek şekilde değiştirin. Bu örnekte myFeedbackProvider.cskullanılır. Bu dosya, geri bildirim sağlayıcısını tanımlayan iki ana sınıfı içerir. Aşağıdaki örnekte sınıf tanımları için temel şablon gösterilmektedir.

using System.Management.Automation;
using System.Management.Automation.Subsystem;
using System.Management.Automation.Subsystem.Feedback;
using System.Management.Automation.Subsystem.Prediction;
using System.Management.Automation.Language;

namespace myFeedbackProvider;

public sealed class myFeedbackProvider : IFeedbackProvider, ICommandPredictor
{

}

public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{

}

3. Adım - Init sınıfını uygulama

Init sınıfı, geri bildirim sağlayıcısını alt sistem yöneticisine kaydeder ve kaydını çıkarır. İkili modül yüklenirken OnImport() yöntemi çalışır. İkili modül kaldırıldığında OnRemove() yöntemi çalışır. Bu örnek hem geri bildirim sağlayıcısını hem de komut tahmincisi alt sistemini kaydeder.

public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{
    private const string Id = "<ADD YOUR GUID HERE>";

    public void OnImport()
    {
        var feedback = new myFeedbackProvider(Id);
        SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, feedback);
        SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, feedback);
    }

    public void OnRemove(PSModuleInfo psModuleInfo)
    {
        SubsystemManager.UnregisterSubsystem<ICommandPredictor>(new Guid(Id));
        SubsystemManager.UnregisterSubsystem<IFeedbackProvider>(new Guid(Id));
    }
}

<ADD YOUR GUID HERE> yer tutucu değerini benzersiz bir Guid ile değiştirin. New-Guid cmdlet'ini kullanarak guid oluşturabilirsiniz.

New-Guid

Guid, sağlayıcınız için benzersiz bir tanımlayıcıdır. Sağlayıcının alt sisteme kaydedilebilmesi için benzersiz bir kimliği olmalıdır.

4. Adım - Sınıf üyeleri ekleme ve oluşturucuyu tanımlama

Aşağıdaki kod arabirimlerde tanımlanan özellikleri uygular, gerekli sınıf üyelerini ekler ve myFeedbackProvider sınıfı için oluşturucuyu oluşturur.

/// <summary>
/// Gets the global unique identifier for the subsystem implementation.
/// </summary>
private readonly Guid _guid;
public Guid Id => _guid;

/// <summary>
/// Gets the name of a subsystem implementation, this will be the name displayed when triggered
/// </summary>
public string Name => "myFeedbackProvider";

/// <summary>
/// Gets the description of a subsystem implementation.
/// </summary>
public string Description => "This is very simple feedback provider";

/// <summary>
/// Default implementation. No function is required for a feedback provider.
/// </summary>
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;

/// <summary>
/// Gets the types of trigger for this feedback provider.
/// </summary>
/// <remarks>
/// The default implementation triggers a feedback provider by <see cref="FeedbackTrigger.CommandNotFound"/> only.
/// </remarks>
public FeedbackTrigger Trigger => FeedbackTrigger.All;

/// <summary>
/// List of candidates from the feedback provider to be passed as predictor results
/// </summary>
private List<string>? _candidates;

/// <summary>
/// PowerShell session used to run PowerShell commands that help create suggestions.
/// </summary>
private PowerShell _powershell;

internal myFeedbackProvider(string guid)
{
    _guid = new Guid(guid); // Save guid
    _powershell = PowerShell.Create(); // Create PowerShell instance
}

5. Adım - GetFeedback() yöntemini oluşturma

GetFeedback yöntemi iki parametre alır: context ve token. context parametresi, önerilerle nasıl yanıt verebileceğinize karar verebilmeniz için tetikleyici hakkındaki bilgileri alır. token parametresi iptal için kullanılır. Bu işlev, öneriyi içeren bir FeedbackItem döndürür.

/// <summary>
/// Gets feedback based on the given commandline and error record.
/// </summary>
/// <param name="context">The context for the feedback call.</param>
/// <param name="token">The cancellation token to cancel the operation.</param>
/// <returns>The feedback item.</returns>
public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token)
{
    // Target describes the different kinds of triggers to activate on,
    var target = context.Trigger;
    var commandLine = context.CommandLine;
    var ast = context.CommandLineAst;

    // defining the header and footer variables
    string header;
    string footer;

    // List of the actions
    List<string>? actions = new List<string>();

    // Trigger on success code goes here

    // Trigger on error code goes here

    return null;
}

Aşağıdaki görüntüde, kullanıcıya görüntülenen önerilerde bu alanların nasıl kullanıldığı gösterilmektedir.

Örnek geri bildirim sağlayıcılarının ekran görüntüsü

Başarı tetikleyicisi için öneriler oluşturma

Başarılı bir çağrı için, son yürütmede kullanılan takma adları genişletmek istiyoruz. CommandLineAstkullanarak tüm takma adlı komutları tanımlar ve bunun yerine tam nitelikli komut adını kullanma önerisi oluştururuz.

// Trigger on success
if (target == FeedbackTrigger.Success)
{
    // Getting the commands from the AST and only finding those that are Commands
    var astCmds = ast.FindAll((cAst) => cAst is CommandAst, true);

    // Inspect each of the commands
    foreach(var command in astCmds)
    {

        // Get the command name
        var aliasedCmd = ((CommandAst) command).GetCommandName();

        // Check if its an alias or not, if so then add it to the list of actions
        if(TryGetAlias(aliasedCmd, out string commandString))
        {
            actions.Add($"{aliasedCmd} --> {commandString}");
        }
    }

    // If no alias was found return null
    if(actions.Count == 0)
    {
        return null;
    }

    // If aliases are found, set the header to a description and return a new FeedbackItem.
    header = "You have used an aliased command:";
    // Copy actions to _candidates for the predictor
    _candidates = actions;

    return new FeedbackItem(header, actions);
}

TryGetAlias() yöntemini uygulama

TryGetAlias() yöntemi, komutun diğer ad olup olmadığını belirtmek için boole değeri döndüren özel bir yardımcı işlevdir. Sınıf oluşturucusunda, PowerShell komutlarını çalıştırmak için kullanabileceğimiz bir PowerShell örneği oluşturduk. TryGetAlias() yöntemi, komutun diğer ad olup olmadığını belirlemek üzere GetCommand yöntemini çağırmak için bu PowerShell örneğini kullanır. AliasInfo tarafından döndürülen GetCommand nesnesi, diğer adı belirtilen komutun tam adını içerir.

/// <summary>
/// Checks if a command is an alias.
/// </summary>
/// <param name="command">The command to check if alias</param>
/// <param name="targetCommand">The referenced command by the aliased command</param>
/// <returns>True if an alias and false if not</returns>
private bool TryGetAlias(string command, out string targetCommand)
{
    // Create PowerShell runspace as a session state proxy to run GetCommand and check
    // if its an alias
    AliasInfo? pwshAliasInfo =
        _powershell.Runspace.SessionStateProxy.InvokeCommand.GetCommand(command, CommandTypes.Alias) as AliasInfo;

    // if its null then it is not an aliased command so just return false
    if(pwshAliasInfo is null)
    {
        targetCommand = String.Empty;
        return false;
    }

    // Set targetCommand to referenced command name
    targetCommand = pwshAliasInfo.ReferencedCommand.Name;
    return true;
}

Hata tetikleyicisi için öneriler oluşturma

Bir komut yürütmesi başarısız olduğunda, kullanıcının komutun nasıl kullanılacağı hakkında daha fazla bilgi almak için Get-Help önermek istiyoruz.

// Trigger on error
if (target == FeedbackTrigger.Error)
{
    // Gets the command that caused the error.
    var erroredCommand = context.LastError?.InvocationInfo.MyCommand;
    if (erroredCommand is null)
    {
        return null;
    }

    header = $"You have triggered an error with the command {erroredCommand}. Try using the following command to get help:";

    actions.Add($"Get-Help {erroredCommand}");
    footer = $"You can also check online documentation at https://learn.microsoft.com/en-us/powershell/module/?term={erroredCommand}";

    // Copy actions to _candidates for the predictor
    _candidates = actions;
    return new FeedbackItem(header, actions, footer, FeedbackDisplayLayout.Portrait);
}

6. Adım - Komut satırı tahmin aracına öneriler gönderme

Geri bildirim sağlayıcınızın kullanıcı deneyimini geliştirmesinin bir diğer yolu da ICommandPredictor arabirimine komut önerileri sağlamaktır. Komut satırı tahmincisi oluşturma hakkında daha fazla bilgi için bkz. Komut satırı tahmincisi oluşturma.

Aşağıdaki kod, geri bildirim sağlayıcınıza tahmine dayalı davranış eklemek için ICommandPredictor arabiriminden gerekli yöntemleri uygular.

  • CanAcceptFeedback() - Bu yöntem, tahmin sahibinin belirli bir geri bildirim türünü kabul edip etmediğini gösteren bir Boole değeri döndürür.
  • GetSuggestion() - Bu yöntem, tahmin aracı tarafından görüntülenecek önerileri içeren bir SuggestionPackage nesnesi döndürür.
  • OnCommandLineAccepted() - Bu yöntem, yürütülmek üzere bir komut satırı kabul edildiğinde çağrılır.
/// <summary>
/// Gets a value indicating whether the predictor accepts a specific kind of feedback.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="feedback">A specific type of feedback.</param>
/// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
{
    return feedback switch
    {
        PredictorFeedbackKind.CommandLineAccepted => true,
        _ => false,
    };
}

/// <summary>
/// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
/// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
/// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
public SuggestionPackage GetSuggestion(
    PredictionClient client,
    PredictionContext context,
    CancellationToken cancellationToken)
{
    if (_candidates is not null)
    {
        string input = context.InputAst.Extent.Text;
        List<PredictiveSuggestion>? result = null;

        foreach (string c in _candidates)
        {
            if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
            {
                result ??= new List<PredictiveSuggestion>(_candidates.Count);
                result.Add(new PredictiveSuggestion(c));
            }
        }

        if (result is not null)
        {
            return new SuggestionPackage(result);
        }
    }

    return default;
}

/// <summary>
/// A command line was accepted to execute.
/// The predictor can start processing early as needed with the latest history.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="history">History command lines provided as references for prediction.</param>
public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history)
{
    // Reset the candidate state once the command is accepted.
    _candidates = null;
}

7. Adım - Geri bildirim sağlayıcısını oluşturma

Artık geri bildirim sağlayıcınızı oluşturmaya ve kullanmaya başlamaya hazırsınız! Projeyi oluşturmak için aşağıdaki komutu çalıştırın:

dotnet build

Bu komut, PowerShell modülünü proje klasörünüzün aşağıdaki yolunda dll dosyası olarak oluşturur: bin/Debug/net8.0/myFeedbackProvider

Windows makinelerinde derleme yaparken error NU1101: Unable to find package System.Management.Automation. hatasıyla karşılaşabilirsiniz. Bunu düzeltmek için proje dizininize bir nuget.config dosyası ekleyin ve aşağıdakileri ekleyin:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <disabledPackageSources>
    <clear />
  </disabledPackageSources>
</configuration>

Geri bildirim sağlayıcısı kullanma

Yeni geri bildirim sağlayıcınızı test etmek için derlenmiş modülü PowerShell oturumunuza aktarın. Bu, derleme başarılı olduktan sonra açıklanan klasör içeri aktarılarak yapılabilir:

Import-Module ./bin/Debug/net8.0/myFeedbackProvider

Modülünüzden memnun olduktan sonra bir modül manifestosu oluşturmanız, bunu PowerShell Galerisi'nde yayımlamanız ve $Env:PSModulePath'a yüklemeniz gerekir. Daha fazla bilgi için bkz. Modül bildirimi oluşturma. modülün PowerShell oturumunda kullanılabilir olması için Import-Module betiğinize $PROFILE komutunu ekleyebilirsiniz.

Aşağıdaki komutu kullanarak yüklü geri bildirim sağlayıcılarının listesini alabilirsiniz:

Get-PSSubsystem -Kind FeedbackProvider
Kind              SubsystemType      IsRegistered Implementations
----              -------------      ------------ ---------------
FeedbackProvider  IFeedbackProvider          True {general}

Not

Get-PSSubsystem, PowerShell 7.1'de kullanıma sunulan deneysel bir cmdlet'tir Bu cmdlet'i kullanmak için PSSubsystemPluginModel deneysel özelliğini etkinleştirmeniz gerekir. Daha fazla bilgi için bkz. Deneysel Özellikleri Kullanma.

Aşağıdaki ekran görüntüsünde yeni sağlayıcıdan bazı örnek öneriler gösterilmektedir.

Başarı ve hata geri bildirim sağlayıcısı tetikleyicilerinin ekran görüntüsü

Aşağıda, tahmin aracı tümleştirmesinin yeni sağlayıcıdan nasıl çalıştığını gösteren bir GIF yer alır.

Geri bildirim sağlayıcısıGIF of predictor system working with feedback providerGIF of predictor system working with feedback providerile çalışan tahmin aracı sistemi'in GIF'i

Diğer geri bildirim sağlayıcıları

Daha ayrıntılı örnekler için iyi bir başvuru olarak kullanılabilecek başka bir geri bildirim sağlayıcısı oluşturduk.

komut bulunamadı

command-not-found geri bildirim sağlayıcısı, yerel komutları çalıştırmaya çalışıldığında ancak eksik olduğunda öneriler sağlamak için Linux sistemlerindeki command-not-found yardımcı programı aracını kullanır. Kodu GitHub Deposu bulabilir veya PowerShell Galerisi'nden kendiniz indirebilirsiniz.

PowerShell Bağdaştırıcısı

Microsoft.PowerShell.PowerShellAdapter, yerel komutlardan gelen metin çıkışlarını PowerShell nesnelerine dönüştürmenize yardımcı olan bir geri bildirim sağlayıcısıdır. Sisteminizde "bağdaştırıcıları" algılar ve yerel komutu kullandığınızda bunları kullanmanızı önerir. PowerShell Bağdaştırıcıları hakkında daha fazla bilgiyi, PowerShell Bağdaştırıcısı Geri Bildirim Sağlayıcısı blog gönderisinden edinebilirsiniz. Ayrıca kodu GitHub Deposu bulabilir veya PowerShell Galerisiindirebilirsiniz.

Ek - Tam uygulama kodu

Aşağıdaki kod, önceki örnekleri sağlayıcı sınıfının tam uygulamasını bulma ile birleştirir.

using System.Management.Automation;
using System.Management.Automation.Subsystem;
using System.Management.Automation.Subsystem.Feedback;
using System.Management.Automation.Subsystem.Prediction;
using System.Management.Automation.Language;

namespace myFeedbackProvider;

public sealed class myFeedbackProvider : IFeedbackProvider, ICommandPredictor
{
    /// <summary>
    /// Gets the global unique identifier for the subsystem implementation.
    /// </summary>
    private readonly Guid _guid;
    public Guid Id => _guid;

    /// <summary>
    /// Gets the name of a subsystem implementation, this will be the name displayed when triggered
    /// </summary>
    public string Name => "myFeedbackProvider";

    /// <summary>
    /// Gets the description of a subsystem implementation.
    /// </summary>
    public string Description => "This is very simple feedback provider";

    /// <summary>
    /// Default implementation. No function is required for a feedback provider.
    /// </summary>
    Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;

    /// <summary>
    /// Gets the types of trigger for this feedback provider.
    /// </summary>
    /// <remarks>
    /// The default implementation triggers a feedback provider by <see cref="FeedbackTrigger.CommandNotFound"/> only.
    /// </remarks>
    public FeedbackTrigger Trigger => FeedbackTrigger.All;

    /// <summary>
    /// List of candidates from the feedback provider to be passed as predictor results
    /// </summary>
    private List<string>? _candidates;

    /// <summary>
    /// PowerShell session used to run PowerShell commands that help create suggestions.
    /// </summary>
    private PowerShell _powershell;

    // Constructor
    internal myFeedbackProvider(string guid)
    {
        _guid = new Guid(guid); // Save guid
        _powershell = PowerShell.Create(); // Create PowerShell instance
    }

    #region IFeedbackProvider
    /// <summary>
    /// Gets feedback based on the given commandline and error record.
    /// </summary>
    /// <param name="context">The context for the feedback call.</param>
    /// <param name="token">The cancellation token to cancel the operation.</param>
    /// <returns>The feedback item.</returns>
    public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token)
    {
        // Target describes the different kinds of triggers to activate on,
        var target = context.Trigger;
        var commandLine = context.CommandLine;
        var ast = context.CommandLineAst;

        // defining the header and footer variables
        string header;
        string footer;

        // List of the actions
        List<string>? actions = new List<string>();

        // Trigger on success
        if (target == FeedbackTrigger.Success)
        {
            // Getting the commands from the AST and only finding those that are Commands
            var astCmds = ast.FindAll((cAst) => cAst is CommandAst, true);

            // Inspect each of the commands
            foreach(var command in astCmds)
            {

                // Get the command name
                var aliasedCmd = ((CommandAst) command).GetCommandName();

                // Check if its an alias or not, if so then add it to the list of actions
                if(TryGetAlias(aliasedCmd, out string commandString))
                {
                    actions.Add($"{aliasedCmd} --> {commandString}");
                }
            }

            // If no alias was found return null
            if(actions.Count == 0)
            {
                return null;
            }

            // If aliases are found, set the header to a description and return a new FeedbackItem.
            header = "You have used an aliased command:";
            // Copy actions to _candidates for the predictor
            _candidates = actions;

            return new FeedbackItem(header, actions);
        }

        // Trigger on error
        if (target == FeedbackTrigger.Error)
        {
            // Gets the command that caused the error.
            var erroredCommand = context.LastError?.InvocationInfo.MyCommand;
            if (erroredCommand is null)
            {
                return null;
            }

            header = $"You have triggered an error with the command {erroredCommand}. Try using the following command to get help:";

            actions.Add($"Get-Help {erroredCommand}");
            footer = $"You can also check online documentation at https://learn.microsoft.com/en-us/powershell/module/?term={erroredCommand}";

            // Copy actions to _candidates for the predictor
            _candidates = actions;
            return new FeedbackItem(header, actions, footer, FeedbackDisplayLayout.Portrait);
        }
        return null;
    }

    /// <summary>
    /// Checks if a command is an alias.
    /// </summary>
    /// <param name="command">The command to check if alias</param>
    /// <param name="targetCommand">The referenced command by the aliased command</param>
    /// <returns>True if an alias and false if not</returns>
    private bool TryGetAlias(string command, out string targetCommand)
    {
        // Create PowerShell runspace as a session state proxy to run GetCommand and check
        // if its an alias
        AliasInfo? pwshAliasInfo =
            _powershell.Runspace.SessionStateProxy.InvokeCommand.GetCommand(command, CommandTypes.Alias) as AliasInfo;

        // if its null then it is not an aliased command so just return false
        if(pwshAliasInfo is null)
        {
            targetCommand = String.Empty;
            return false;
        }

        // Set targetCommand to referenced command name
        targetCommand = pwshAliasInfo.ReferencedCommand.Name;
        return true;
    }
    #endregion IFeedbackProvider

    #region ICommandPredictor

    /// <summary>
    /// Gets a value indicating whether the predictor accepts a specific kind of feedback.
    /// </summary>
    /// <param name="client">Represents the client that initiates the call.</param>
    /// <param name="feedback">A specific type of feedback.</param>
    /// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
    public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
    {
        return feedback switch
        {
            PredictorFeedbackKind.CommandLineAccepted => true,
            _ => false,
        };
    }

    /// <summary>
    /// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
    /// </summary>
    /// <param name="client">Represents the client that initiates the call.</param>
    /// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
    /// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
    /// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
    public SuggestionPackage GetSuggestion(
        PredictionClient client,
        PredictionContext context,
        CancellationToken cancellationToken)
    {
        if (_candidates is not null)
        {
            string input = context.InputAst.Extent.Text;
            List<PredictiveSuggestion>? result = null;

            foreach (string c in _candidates)
            {
                if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
                {
                    result ??= new List<PredictiveSuggestion>(_candidates.Count);
                    result.Add(new PredictiveSuggestion(c));
                }
            }

            if (result is not null)
            {
                return new SuggestionPackage(result);
            }
        }

        return default;
    }

    /// <summary>
    /// A command line was accepted to execute.
    /// The predictor can start processing early as needed with the latest history.
    /// </summary>
    /// <param name="client">Represents the client that initiates the call.</param>
    /// <param name="history">History command lines provided as references for prediction.</param>
    public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history)
    {
        // Reset the candidate state once the command is accepted.
        _candidates = null;
    }

    #endregion;
}

public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{
    private const string Id = "<ADD YOUR GUID HERE>";

    public void OnImport()
    {
        var feedback = new myFeedbackProvider(Id);
        SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, feedback);
        SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, feedback);
    }

    public void OnRemove(PSModuleInfo psModuleInfo)
    {
        SubsystemManager.UnregisterSubsystem<ICommandPredictor>(new Guid(Id));
        SubsystemManager.UnregisterSubsystem<IFeedbackProvider>(new Guid(Id));
    }
}