NAME

Claude::Agent::CLI - Terminal UI utilities for interactive CLI applications

SYNOPSIS

use Claude::Agent::CLI qw(:all);

# Spinners
my $result = with_spinner("Processing...", sub {
    # Long-running operation
    return $data;
});

# Async spinners with IO::Async
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;

my $spinner = start_spinner("Processing...", $loop);
my $result = await $async_operation;
stop_spinner($spinner, "Processing complete");

# Prompts
my $name = prompt("Enter your name", "Anonymous");
my $continue = ask_yn("Continue?", "y");
my $choice = menu("Select action", [
    { key => 'a', label => 'Add' },
    { key => 'd', label => 'Delete' },
]);

# Interactive selection using Term::Choose (keyboard navigation)
my $selected = choose_from(\@options, prompt => "Pick one:");
my @selected = choose_multiple(\@options, prompt => "Pick several:");

# Display utilities
header("My Application");
divider();
status('success', "Operation completed");
status('error', "Something went wrong");

DESCRIPTION

Provides shared utilities for interactive terminal features including:

  • Spinners using Term::ProgressSpinner (with IO::Async support)

  • Input prompts using Term::ReadLine

  • Interactive keyboard-navigable selection using Term::Choose

  • Colored output using Term::ANSIColor

  • Terminal control utilities

EXPORTS

Nothing is exported by default. Use :all for everything, or import specific functions.

Export Tags

:all     - All functions
:spinner - with_spinner, start_spinner, stop_spinner
:prompt  - prompt, ask_yn, menu, select_option, choose_from, choose_multiple
:display - header, divider, status
:term    - clear_line, move_up

FUNCTIONS

Spinners

with_spinner

my $result = with_spinner($message, $code);

Display a spinner while executing code. Returns the result of the code block. Note: This is for synchronous code. For async operations, use start_spinner/stop_spinner.

my $data = with_spinner("Loading...", sub {
    return load_data();
});

start_spinner

my $spinner = start_spinner($message, $loop, %opts);

Start an async spinner for long-running operations. Returns the spinner object. Call stop_spinner($spinner) when the operation completes.

When an IO::Async loop is provided, the spinner animates automatically. Without a loop, the spinner displays but doesn't animate (useful for quick operations).

Options:

spinner        - Spinner style (default: 'dots')
                 Available: dots, bar, around, pipe, moon, circle,
                 color_circle, color_circles, color_square, color_squares,
                 earth, circle_half, clock, pong, material
spinner_color  - Color for spinner (default: 'cyan')
                 Available: black, red, green, yellow, blue, magenta, cyan, white
                 Also: bright_* variants, and "color on_background" combinations
message        - Custom message format (default: "{spinner} $message")
                 Placeholders: {spinner}, {elapsed}, {percent}, etc.
interval       - Animation interval in seconds (default: 0.1)
terminal_line  - Skip STDIN cursor query by providing line number

use IO::Async::Loop;
my $loop = IO::Async::Loop->new;

# Simple usage
my $spinner = start_spinner("Processing...", $loop);

# Customized spinner
my $spinner = start_spinner("Loading...", $loop,
    spinner       => 'moon',
    spinner_color => 'yellow',
    interval      => 0.2,
);

my $result = await $async_operation;
stop_spinner($spinner, "Processing complete");

stop_spinner

stop_spinner($spinner, $success_message);

Stop a spinner started with start_spinner. Optionally display a success message.

Prompts

prompt

my $answer = prompt($message, $default);

Prompt the user for text input with an optional default value.

ask_yn

my $yes = ask_yn($message, $default);

Ask a yes/no question. Returns true for yes, false for no. Default is 'y' if not specified.

if (ask_yn("Continue?", "y")) {
    # User said yes
}
my $choice = menu($title, $options);

Display a menu with keyed options using Term::Choose for keyboard navigation. Returns the selected key.

my $action = menu("Action", [
    { key => 'a', label => 'Approve' },
    { key => 'r', label => 'Revise' },
    { key => 's', label => 'Skip' },
]);

select_option

my $selected = select_option($options, %args);

Display options for selection using Term::Choose with keyboard navigation. Returns the selected option text, or undef if "Custom" was selected (when allow_custom => 1).

my $outline = select_option(\@outlines, allow_custom => 1);
if (!defined $outline) {
    # User wants to enter custom text
    $outline = prompt("Enter custom outline:");
}

choose_from

my $selected = choose_from($options, %args);

Interactive selection using Term::Choose with keyboard navigation. Users can use arrow keys, vim keys (hjkl), or Ctrl-F to search.

Options: prompt - Header text to display inline_prompt - Prompt shown inline with selection layout - 1 for columns (default), 2 for single column return_index - Return index instead of value (default: 0) mouse - Enable mouse support (default: 0)

my $title = choose_from(\@titles, prompt => "Select a title:");

choose_multiple

my @selected = choose_multiple($options, %args);

Interactive multi-selection using Term::Choose. Users press SpaceBar to mark items and Enter to confirm.

my @features = choose_multiple(
    [qw(feature1 feature2 feature3)],
    prompt => "Select features to enable:",
    preselected => [0, 2],  # Pre-select first and third
);

Display

header($text);

Display a styled header with border lines.

divider

divider($char, $width);

Print a divider line. Defaults to '-' x 60.

status

status($type, $message);

Print a status message with appropriate color and icon. Types: success, error, warning, info

status('success', "File saved");
status('error', "Operation failed");
status('warning', "Disk space low");
status('info', "Processing 10 items");

Terminal Control

clear_line

clear_line();

Clear the current line (useful for updating spinner messages).

move_up

move_up($n);

Move cursor up N lines.

DEPENDENCIES

  • Term::ANSIColor (core module)

  • Term::ReadLine (core module)

  • Term::Choose

  • Term::ReadKey (recommended, used by Term::Choose)

  • Term::ProgressSpinner

AUTHOR

LNATION, <email at lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)