Skip to content
ConsoleMonster

Hosts, CI and transcripts

What ConsoleMonster writes on a real console, in CI, in background jobs, remoting and SSH, the ISE, transcripts and redirected output, and with NO_COLOR.

ConsoleMonster draws directly only on a real local interactive console. Everywhere else the cmdlets write through the PowerShell host, line by line as Write-Host does, so jobs complete, transcripts record and redirection captures whole lines.

Where the script runsWhat ConsoleMonster writes
A real local consoleDrawn directly, with colour and live dashboards.
A real console with Start-Transcript runningColoured text through the host UI; the transcript receives the same lines as plain text.
The ISEColoured text through the host UI. Prompts ask through the host.
Background jobs and remotingWrite-Host style information records (tag PSHOST) with foreground and background colours.
Redirected output, thread jobs and other hostsOne plain record per line, so files, transcripts and redirected streams get whole lines of plain text.
NO_COLOR, CONSOLEMONSTER_COLOR=none, plain modeNo colour on any route.

Colours on the host routes map to the 16 console colours by role: pass green, fail red, warn yellow, muted dark grey and accent cyan; the light theme uses the darker variants. A live dashboard in those hosts narrates as append-only lines.

Background jobs

A job has no console of its own, so ConsoleMonster writes its output as Write-Host style information records, tagged PSHOST, each piece with its foreground and background colour. Receive-Job replays those records in the console that receives them, so a table drawn inside the job arrives whole and in colour. The job starts in a new session, so it imports ConsoleMonster itself. The objects the job returns stay separate from the drawn table: $results = Receive-Job ... still shows the table and puts only the objects in $results.

PowerShell 5.1
PowerShell 7
PowerShelljob.ps1
$module = (Get-Module ConsoleMonster).Path
Start-Job -ArgumentList $module {
    param($Module)
    Import-Module $Module
    [pscustomobject] @{ Check = 'SPF'
        Status = 'Pass' }, [pscustomobject] @{ Check = 'DMARC'; Status = 'Fail' } |
        Show-ConsoleTable -Title 'From a background job' -StatusProperty Status
} | Receive-Job -Wait -AutoRemoveJob
Full program
Import-Module ConsoleMonster

$module = (Get-Module ConsoleMonster).Path
Start-Job -ArgumentList $module {
    param($Module)
    Import-Module $Module
    [pscustomobject] @{ Check = 'SPF'
        Status = 'Pass' }, [pscustomobject] @{ Check = 'DMARC'; Status = 'Fail' } |
        Show-ConsoleTable -Title 'From a background job' -StatusProperty Status
} | Receive-Job -Wait -AutoRemoveJob
The same job received in Windows PowerShell 5.1 and PowerShell 7, captured by the site tool in a pseudo console.

CI

When a CI system is detected (CI, GitHub Actions, Azure Pipelines, Jenkins, GitLab or TeamCity) or output is redirected, live sessions switch to stream mode: one line per meaningful change, a heartbeat summary every ten seconds and [mm:ss] prefixes in CI, where log viewers often lack their own timestamps.

To use it in a pipeline:

  1. Install the module in the job. On GitHub Actions, with PowerShell 7:

    - name: Checks
      shell: pwsh
      run: |
        Install-Module ConsoleMonster -Scope CurrentUser -Force
        Import-Module ConsoleMonster
        ./checks.ps1
    
  2. Nothing else to configure: GitHub Actions sets CI, so live output streams as lines. Other systems are detected by their own variables, and CI=true works for any of them.

  3. For logs with ASCII glyphs and no escape sequences, also set CONSOLEMONSTER_MODE: plain under the step's env:.

CONSOLEMONSTER_MODE=plain PowerShell 7.6
PowerShellformat-consoletable.ps1
# Build artifacts as a module build reports them.
$artifacts = @(
    [pscustomobject] @{ Name = 'ConsoleMonster.2.0.0.nupkg'; Kind = 'NuGet'; Length = 1482113
        Time = [timespan]::FromSeconds(48.2); Signed = $true
        Path = 'D:\Builds\ConsoleMonster\Packed\ConsoleMonster.2.0.0.nupkg' }
    [pscustomobject] @{ Name = 'ConsoleMonster.2.0.0.snupkg'; Kind = 'NuGet'; Length = 402331
        Time = [timespan]::FromSeconds(3.1); Signed = $true
        Path = 'D:\Builds\ConsoleMonster\Packed\ConsoleMonster.2.0.0.snupkg' }
    # … 6 more lines in the full program
)

$artifacts | Show-ConsoleTable -Title '[bold]Build artifacts[/]' -GroupBy Kind -Property @(
    @{ Name = 'Name'; Key = $true; Overflow = 'Never' }
    @{ Name = 'Length'; Header = 'Size'; Presenter = 'Bytes'; Priority = 9; Aggregate = 'Sum' }
    @{ Name = 'Time'; Header = 'Built in'; Presenter = 'Duration'; Priority = 6; Aggregate = 'Sum' }
    @{ Name = 'Signed'; Presenter = 'Boolean'; Priority = 5 }
    @{ Name = 'Path'; Overflow = 'TruncateMiddle'; Priority = 1; MinWidth = 20 }
) -ShowTotals
Full program
Import-Module ConsoleMonster

# Build artifacts as a module build reports them.
$artifacts = @(
    [pscustomobject] @{ Name = 'ConsoleMonster.2.0.0.nupkg'; Kind = 'NuGet'; Length = 1482113
        Time = [timespan]::FromSeconds(48.2); Signed = $true
        Path = 'D:\Builds\ConsoleMonster\Packed\ConsoleMonster.2.0.0.nupkg' }
    [pscustomobject] @{ Name = 'ConsoleMonster.2.0.0.snupkg'; Kind = 'NuGet'; Length = 402331
        Time = [timespan]::FromSeconds(3.1); Signed = $true
        Path = 'D:\Builds\ConsoleMonster\Packed\ConsoleMonster.2.0.0.snupkg' }
    [pscustomobject] @{ Name = 'ConsoleMonster-2.0.0.zip'; Kind = 'Module'; Length = 2904576
        Time = [timespan]::FromSeconds(11.7); Signed = $true
        Path = 'D:\Builds\ConsoleMonster\Packed\ConsoleMonster-2.0.0.zip' }
    [pscustomobject] @{ Name = 'Docs.zip'; Kind = 'Module'; Length = 51302
        Time = [timespan]::FromSeconds(1.2); Signed = $false
        Path = 'D:\Builds\ConsoleMonster\Packed\Docs.zip' }
)

$artifacts | Show-ConsoleTable -Title '[bold]Build artifacts[/]' -GroupBy Kind -Property @(
    @{ Name = 'Name'; Key = $true; Overflow = 'Never' }
    @{ Name = 'Length'; Header = 'Size'; Presenter = 'Bytes'; Priority = 9; Aggregate = 'Sum' }
    @{ Name = 'Time'; Header = 'Built in'; Presenter = 'Duration'; Priority = 6; Aggregate = 'Sum' }
    @{ Name = 'Signed'; Presenter = 'Boolean'; Priority = 5 }
    @{ Name = 'Path'; Overflow = 'TruncateMiddle'; Priority = 1; MinWidth = 20 }
) -ShowTotals
Show-ConsoleTable with CONSOLEMONSTER_MODE=plain: ASCII glyphs and no escape sequences, for logs that keep every byte.

Text that reaches stream and plain output is sanitised: escape sequences and control characters in log messages, captured lines or data are removed, so plain mode never emits an escape sequence.

Native CI logs and job summaries

Groups, annotations and job summaries use the provider's own protocol when GITHUB_ACTIONS=true, TF_BUILD=true or GITLAB_CI=true. Other CI systems keep ordinary output. CONSOLEMONSTER_CI=github, azure or gitlab forces that provider; none disables provider commands. This choice is independent of colour, glyphs and stream mode. GitLab's section markers deliberately contain the control sequences required by its log viewer, including in plain mode.

In a PowerShell terminal:

Import-Module ConsoleMonster

Use-ConsoleGroup 'Build' {
    dotnet build
    if ($LASTEXITCODE -ne 0) {
        Write-ConsoleAnnotation -Level Error -Title 'Build' -Message 'The build failed'
    }
}

$annotation = @{
    Level = 'Warning'
    Message = 'Configuration is missing a timeout'
    File = './build.ps1'
    Line = 12
    Column = 5
}
Write-ConsoleAnnotation @annotation

@(
    [pscustomobject]@{ Check = 'Build'; Result = 'Passed' }
    [pscustomobject]@{ Check = 'Tests'; Result = 'Passed' }
) | Add-ConsoleJobSummary
ProviderGroupsAnnotationsSummary
GitHub ActionsCollapsible log groupsError, Warning and Notice, with optional title and source positionAppends Markdown to GITHUB_STEP_SUMMARY
Azure PipelinesCollapsible log groupsError and Warning with source position; Notice is an ordinary lineUploads a UTF-8 Markdown file to the run's Extensions tab
GitLab CISections collapsed by defaultStyled log linesReturns Markdown for the script to save
Terminal or other CIMuted headingStyled status line; source links where supportedReturns Markdown without writing a file

Groups nest and close when an exception unwinds the script. Sequential Invoke-ConsoleRun puts each item's work and result inside a group. Parallel runs and Start-ConsoleRun group each completed item's result atomically, so groups cannot cross when workers finish out of order. Output emitted independently by parallel workers keeps its existing routing; use Use-ConsoleGroup for a serial block whose entire log should fold together.

Annotation files inside GITHUB_WORKSPACE, BUILD_SOURCESDIRECTORY or CI_PROJECT_DIR use paths relative to that checkout, with / separators. Paths outside the provider's workspace stay as supplied, with separators normalised. In a terminal, the source location appears once, relative to the current directory: src/App.cs:42. That label is a hyperlink when the host supports links and plain text otherwise. PowerShell resolves relative input from its current location.

Invoke-ConsolePester automatically annotates failing tests, with the assertion's file and line, and adds its results-by-file table and failures to the GitHub or Azure summary. -NoJobSummary turns off that summary. Pester's result object, throw policy, XML result file and exit code keep their native behaviour. Each invocation emits at most ten automatic annotations, followed by one line counting the remainder; all failures remain in the summary. With Enable-ConsoleFormatting, the Errors feature also annotates displayed script errors, including terminating errors with a script position, capped at ten per output pipeline. Explicit Write-ConsoleAnnotation calls have no ConsoleMonster limit. An annotation alone does not set an exit code.

GitHub summary writes append. Azure retains a separate consolemonster-summary-*.md file for each call in SYSTEM_DEFAULTWORKINGDIRECTORY (or the temporary directory when forced outside an agent). Keep those files available until the agent uploads them; the job workspace owns their cleanup. GitLab and terminal callers can save the returned Markdown themselves:

$env:CONSOLEMONSTER_CI = 'none'
Get-Process | Select-Object -First 5 Name, Id | Add-ConsoleJobSummary | Set-Content ./summary.md

The C# API shares those transports. AddSummary(string) accepts Markdown; the widget overload uses the existing optional Markdown exporter, without adding a dependency to the core package:

using ConsoleMonster;
using ConsoleMonster.Markdown;
using ConsoleMonster.Widgets;

using (ContinuousIntegration.Group("Build")) {
    ContinuousIntegration.Annotate(
        AnnotationLevel.Warning, "Review this setting", file: "build.cs", line: 12);
}
var table = new Table();
table.AddColumn("Check");
table.AddColumn("Result");
table.AddRow("Build", "Passed");
string markdown = ContinuousIntegration.AddSummary(
    table, provider: new MarkdownFrameExportProvider());

// A host can override detection for one async flow, then restore the previous choice.
using (ContinuousIntegration.UseProvider(ContinuousIntegrationProvider.None)) {
    ContinuousIntegration.Annotate(AnnotationLevel.Notice, "Local run");
}

The wire formats follow GitHub workflow commands, Azure logging commands and GitLab custom collapsible sections. GitHub and Azure escape their own property and message delimiters. Azure command-shaped text in headings, notices and rendered log data is neutralised before writing. ConsoleMonster owns Pester's CI annotations even when its live adapter is disabled, so the provider override and annotation limit apply to fallback runs too.

Prompts when nobody can answer

SituationWhat prompts do
CI, redirected input, pwsh -NonInteractive, CONSOLEMONSTER_NONINTERACTIVE=1Return the default, or fail with a message that names the parameter to pass.
Remoting, the ISEAsk through the host: numbered choices and PromptForChoice.
CONSOLEMONSTER_ACCESSIBLE=1Plain numbered questions with no redraws; every answer is confirmed as text.
CONSOLEMONSTER_NOMOUSE=1The usual prompts, with the mouse left to the terminal (selection, scrollback). InteractiveOptions.Default.Mouse = false does the same in C#.

Known limits

  • Windows Terminal does not expose its colour scheme, so its light schemes need CONSOLEMONSTER_THEME=light.
  • Output that was already printed cannot re-flow on a later resize. Use -Width or a fixed profile when output must fit a known width.
  • Transcript detection, and transcript writing on 5.1, use guarded reflection into PowerShell internals; it does nothing if a future version renames those members.

Next

Edit this page on GitHub