cli

task: Display error with context and formatting use: cli_abort() with inline markup and bullet lists

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cli" with this command: npx skills add posit-dev/skills/posit-dev-skills-cli

CLI for R Packages

When to Use What

task: Display error with context and formatting use: cli_abort() with inline markup and bullet lists

task: Show warning with formatting use: cli_warn() with inline markup

task: Display informative message use: cli_inform() with inline markup

task: Show progress for counted operations use: cli_progress_bar() with total count

task: Show simple progress steps use: cli_progress_step() with status messages

task: Format code or function names use: {.code ...} or {.fn package::function}

task: Format file paths use: {.file path/to/file}

task: Format package names use: {.pkg packagename}

task: Format variable names use: {.var variable_name}

task: Format values use: {.val value}

task: Handle singular/plural text use: {?s} or {?y/ies} with pluralization

task: Create headers use: cli_h1() , cli_h2() , cli_h3()

task: Create alerts use: cli_alert_success() , cli_alert_danger() , cli_alert_warning() , cli_alert_info()

task: Create lists use: cli_ul() , cli_ol() , cli_dl() with cli_li()

Inline Markup Essentials

Use inline markup with {.class content} syntax to format text:

Basic formatting

cli_text("Function {.fn mean} calculates averages") cli_text("Install package {.pkg dplyr}") cli_text("See file {.file ~/.Rprofile}") cli_text("{.var x} must be numeric, not {.obj_type_of {x}}") cli_text("Got value {.val {x}}"))

Code formatting

cli_text("Use {.code sum(x, na.rm = TRUE)}")

Paths and arguments

cli_text("Reading from {.path /data/file.csv}") cli_text("Set {.arg na.rm} to TRUE")

Types and classes

cli_text("Object is {.cls data.frame}")

Emphasis

cli_text("This is {.emph important}") cli_text("This is {.strong critical}")

Fields

cli_text("The {.field name} field is required")

Vector Collapsing

Vectors are automatically collapsed with commas and "and":

pkgs <- c("dplyr", "tidyr", "ggplot2") cli_text("Installing packages: {.pkg {pkgs}}") #> Installing packages: dplyr, tidyr, and ggplot2

files <- c("data.csv", "script.R") cli_text("Found {length(files)} file{?s}: {.file {files}}") #> Found 2 files: data.csv and script.R

Escaping Braces

Use double braces {{ and }} to escape literal braces:

cli_text("Use {{variable}} syntax in glue") #> Use {variable} syntax in glue

For complete markup reference: See references/inline-markup.md for all 50+ inline classes, edge cases, nesting rules, and advanced patterns.

Pluralization Basics

Use {?} for pluralization with three patterns:

Single Alternative

nfile <- 1 cli_text("Found {nfile} file{?s}") #> Found 1 file

nfile <- 3 cli_text("Found {nfile} file{?s}") #> Found 3 files

Two Alternatives

ndir <- 1 cli_text("Found {ndir} director{?y/ies}") #> Found 1 directory

ndir <- 5 cli_text("Found {ndir} director{?y/ies}") #> Found 5 directories

Three Alternatives (zero/one/many)

nfile <- 0 cli_text("Found {nfile} file{?s}: {?no/the/the} file{?s}") #> Found 0 files: no files

nfile <- 1 cli_text("Found {nfile} file{?s}: {?no/the/the} file{?s}") #> Found 1 file: the file

nfile <- 3 cli_text("Found {nfile} file{?s}: {?no/the/the} file{?s}") #> Found 3 files: the files

Helpers: qty() and no()

Use no() to display "no" instead of zero:

nfile <- 0 cli_text("Found {no(nfile)} file{?s}") #> Found no files

Use qty() to set quantity explicitly:

nupd <- 3 ntotal <- 10 cli_text("{nupd}/{ntotal} {qty(nupd)} file{?s} {?needs/need} updates") #> 3/10 files need updates

For advanced pluralization: See references/inline-markup.md for edge cases and complex patterns.

CLI Conditions: Core Patterns

Use cli conditions instead of base R for better formatting:

cli_abort() - Formatted Errors

Before (base R)

stop("File not found: ", path)

After (cli)

cli_abort("File {.file {path}} not found")

With bullets for context

check_file <- function(path) { if (!file.exists(path)) { cli_abort(c( "File not found", "x" = "Cannot read {.file {path}}", "i" = "Check that the file exists" )) } }

cli_warn() - Formatted Warnings

Before (base R)

warning("Column ", col, " has missing values")

After (cli)

cli_warn("Column {.field {col}} has missing values")

With context

cli_warn(c( "Data quality issues detected", "!" = "Column {.field {col}} has {n_missing} missing value{?s}", "i" = "Consider using {.fn tidyr::drop_na}" ))

cli_inform() - Formatted Messages

Before (base R)

message("Processing ", n, " files")

After (cli)

cli_inform("Processing {n} file{?s}")

With structure

cli_inform(c( "v" = "Successfully loaded {.pkg dplyr}", "i" = "Version {packageVersion('dplyr')}" ))

Bullet Types

  • "x"

  • Error/problem (red X)

  • "!"

  • Warning (yellow !)

  • "i"

  • Information (blue i)

  • "v"

  • Success (green checkmark)

  • "*"

  • Bullet point

  • ">"

  • Arrow/pointer

For advanced error design: See references/conditions.md for error design principles, rlang integration, testing strategies, and real-world patterns.

Basic Progress Indicators

Simple Progress Steps

process_data <- function() { cli_progress_step("Loading data") data <- load_data()

cli_progress_step("Cleaning data") clean <- clean_data(data)

cli_progress_step("Analyzing data") analyze(clean) }

Basic Progress Bar

process_files <- function(files) { cli_progress_bar("Processing files", total = length(files))

for (file in files) { process_file(file) cli_progress_update() } }

Auto-Cleanup

Progress bars auto-close when the function exits:

process <- function() { cli_progress_bar("Working", total = 100) for (i in 1:100) { Sys.sleep(0.01) cli_progress_update() }

No need to call cli_progress_done() - auto-closes

}

For advanced progress: See references/progress.md for nested progress, custom formats, parallel processing, all progress variables, and Shiny integration.

Semantic CLI Elements

Headers

cli_h1("Main Section") cli_h2("Subsection") cli_h3("Detail")

Alerts

cli_alert_success("Operation completed successfully") cli_alert_danger("Critical error occurred") cli_alert_warning("Potential issue detected") cli_alert_info("Additional information available")

Text and Code

Regular text with markup

cli_text("This is formatted text with {.emph emphasis}")

Code blocks

cli_code(c( "library(dplyr)", "mtcars %>% filter(mpg > 20)" ))

Verbatim text (no formatting)

cli_verbatim("This is displayed exactly as-is: {not interpolated}")

Lists

Unordered list

cli_ul() cli_li("First item") cli_li("Second item") cli_end()

Ordered list

cli_ol() cli_li("First step") cli_li("Second step") cli_end()

Definition list

cli_dl() cli_li(c(name = "The name field")) cli_li(c(email = "The email address")) cli_end()

Common Workflows

Base R to CLI Migration

Before: Base R error handling

validate_input <- function(x, y) { if (!is.numeric(x)) { stop("x must be numeric") } if (length(y) == 0) { stop("y cannot be empty") } if (length(x) != length(y)) { stop("x and y must have the same length") } }

After: CLI error handling

validate_input <- function(x, y) { if (!is.numeric(x)) { cli_abort(c( "{.arg x} must be numeric", "x" = "You supplied a {.cls {class(x)}} vector", "i" = "Use {.fn as.numeric} to convert" )) }

if (length(y) == 0) { cli_abort(c( "{.arg y} cannot be empty", "i" = "Provide at least one element" )) }

if (length(x) != length(y)) { cli_abort(c( "{.arg x} and {.arg y} must have the same length", "x" = "{.arg x} has length {length(x)}", "x" = "{.arg y} has length {length(y)}" )) } }

Error Message with Rich Context

check_required_columns <- function(data, required_cols) { actual_cols <- names(data) missing_cols <- setdiff(required_cols, actual_cols)

if (length(missing_cols) > 0) { cli_abort(c( "Required column{?s} missing from data", "x" = "Missing {length(missing_cols)} column{?s}: {.field {missing_cols}}", "i" = "Data has {length(actual_cols)} column{?s}: {.field {actual_cols}}", "i" = "Add the missing column{?s} or check for typos" )) }

invisible(data) }

Function with Progress Bar

process_files <- function(files, verbose = TRUE) { n <- length(files)

if (verbose) { cli_progress_bar( format = "Processing {cli::pb_bar} {cli::pb_current}/{cli::pb_total} [{cli::pb_eta}]", total = n ) }

results <- vector("list", n)

for (i in seq_along(files)) { results[[i]] <- process_file(files[[i]])

if (verbose) {
  cli_progress_update()
}

}

results }

Resources & Advanced Topics

Reference Files

references/inline-markup.md - Complete catalog of inline classes organized by category, advanced patterns, nesting rules, and real-world examples

references/conditions.md - Advanced error design patterns, rlang integration, testing with testthat snapshots, migration guide, and anti-patterns

references/progress.md - Nested progress bars, custom formats, all progress variables, parallel processing, Shiny integration, and debugging

references/themes.md - Complete theming system with CSS-like selectors, container functions, color palettes, custom themes, and accessibility

references/ansi-operations.md - ANSI string operations (align, columns, nchar, etc.), hyperlinks, color detection, testing CLI output, and troubleshooting

External Resources

  • cli package documentation

  • cli GitHub repository

  • Building a semantic CLI (article)

Related Packages

  • rlang - Condition handling and error objects integrate with cli

  • glue - String interpolation powers cli's {} syntax

  • testthat - Snapshot testing for cli output

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

quarto-authoring

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

critical-code-reviewer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

brand-yml

No summary provided by upstream source.

Repository SourceNeeds Review