functions/Get-ModuleCommand.ps1

Function Get-ModuleCommand {
    [cmdletbinding(DefaultParameterSetName = "name")]
    [Alias("gmh")]
    [OutputType("ModuleCommand")]

    Param(
        [Parameter(
            Position = 0,
            HelpMessage = "The name of an installed/available module",
            ValueFromPipelineByPropertyName
        )]
        [SupportsWildcards()]
        [string]$Name,

        [Parameter(
            HelpMessage = "Command name to search for"
        )]
        [SupportsWildcards()]
        [string]$CommandName,

        [switch]$ListAvailable
    )

    Begin {
        Write-Verbose "Starting $($MyInvocation.MyCommand)"
        $PSBoundParameters.Add("ErrorAction", "stop")

        #region local functions
        function getModuleInfo {
            [cmdletbinding()]
            param(
                $module,
                $CommandName
            )
            Write-Verbose "Using version $($module.version)"

            $cmds = @()
            Write-Verbose "Getting exported functions"
            $cmds += $module.ExportedFunctions.keys | Where-Object { $_ -like "$CommandName" } | Get-Command
            Write-Verbose "Getting exported cmdlets"
            $cmds += $module.ExportedCmdlets.keys | Where-Object { $_ -like "$CommandName" } | Get-Command

            Write-Verbose "Found $($cmds.count) functions and/or cmdlets"

            $out = foreach ($cmd in $cmds) {
                Write-Verbose "Processing $($cmd.name)"
                #get aliases, ignoring errors for those commands without one
                $alias = (Get-Alias -Definition $cmd.Name -ErrorAction SilentlyContinue).name

                #it is assumed you have updated help
                [PSCustomObject]@{
                    PSTypeName = "ModuleCommand"
                    Name       = $cmd.name
                    Alias      = $alias
                    Verb       = $cmd.verb
                    Noun       = $cmd.noun
                    Synopsis   = (Get-Help $cmd.name -ShowWindow:$false).synopsis.trim()
                    Type       = $cmd.CommandType
                    Version    = $cmd.version
                    Help       = $cmd.HelpUri
                    ModuleName = $module.name
                    ModulePath = $module.Path
                    Compatible = $module.CompatiblePSEditions
                    PSVersion  = $module.PowerShellVersion
                }
            } #foreach cmd

            $out
        }
        #endregion
    }

    Process {
        If ([string]::IsNullOrEmpty($Name) -and [string]::IsNullOrEmpty($CommandName)) {
            if ($ListAvailable) {
                $out = Get-Module -ListAvailable | ForEach-Object {
                    [PSCustomObject]@{
                        PSTypeName = "ModuleCommand"
                        Name       = $_.name
                        Alias      = ""
                        Verb       = ""
                        Noun       = ""
                        Synopsis   = $_.Description
                        Type       = $null
                        Version    = $_.version
                        Help       = $_.HelpInfoUri
                        ModuleName = "Available Modules"
                        ModulePath = $_.Path
                        Compatible = $_.CompatiblePSEditions
                        PSVersion  = $_.PowerShellVersion
                    }
                }
            }
            else {
                $out = Get-InstalledModule | ForEach-Object {
                    [PSCustomObject]@{
                        PSTypeName = "ModuleCommand"
                        Name       = $_.name
                        Alias      = ""
                        Verb       = ""
                        Noun       = ""
                        Synopsis   = $_.Description
                        Type       = $null
                        Version    = $_.version
                        Help       = $_.HelpInfoUri
                        ModuleName = "Installed Modules"
                        ModulePath = $_.Path
                        Compatible = $_.CompatiblePSEditions
                        PSVersion  = $_.PowerShellVersion
                    }
                }
            }
        }
        else {
            if ([string]::IsNullOrEmpty($CommandName)) { $CommandName = "*" }
            if ([string]::IsNullOrEmpty($Name)) { $Name = "*" }

            if ($ListAvailable) {
                $out = Get-Module -Name $Name -ListAvailable | ForEach-Object {
                    #We need to rebind to object (reason unknown!!)
                    getModuleInfo -module $_ -CommandName $CommandName
                }
            }
            else {
                $out = Get-Module -Name $Name | ForEach-Object {
                    getModuleInfo -module $_ -CommandName $CommandName
                }
            }
        }

        #display results sorted by name for better formatting
        $out | Sort-Object -Property ModuleName, Name
    }
    End {
        Write-Verbose "Ending $($MyInvocation.MyCommand)"
    }

} #close function

Register-ArgumentCompleter -CommandName Get-ModuleCommand -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    (Get-Module -Name "$WordToComplete*").name |
    ForEach-Object {
        # completion text,listItem text,result type,Tooltip
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
}