Claude-Agent

 view release on metacpan or  search on metacpan

lib/Claude/Agent/CLI.pm  view on Meta::CPAN

my $_term;

sub _term {
    $_term //= Term::ReadLine->new('Claude::Agent');
}

=head1 FUNCTIONS

=head2 Spinners

=head3 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();
    });

=cut

sub with_spinner {
    my ($message, $code) = @_;

    my $ps = Term::ProgressSpinner->new();
    $ps->message("{spinner} $message");
    $ps->start(100);  # Indeterminate mode

    my $result = $code->();

    $ps->finish();
    status('success', $message);

    return $result;
}

=head3 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");

=cut

my $_active_spinner;

sub start_spinner {
    my ($message, $loop, %opts) = @_;

    # Extract our options vs Term::ProgressSpinner options
    my $interval = delete $opts{interval} // 0.1;

    # Build spinner options with defaults
    my %spinner_opts = (
        spinner       => $opts{spinner}       // 'dots',
        spinner_color => $opts{spinner_color} // 'cyan',
        message       => $opts{message}       // "{spinner} $message",
    );

    # Pass through any additional Term::ProgressSpinner options
    for my $key (qw(terminal_line terminal_height output)) {
        $spinner_opts{$key} = $opts{$key} if defined $opts{$key};
    }

    $_active_spinner = Term::ProgressSpinner->new(%spinner_opts);

    if ($loop) {
        # Async mode - spinner animates via IO::Async timer
        $_active_spinner->start_async($loop, interval => $interval);
    } else {
        # Non-async mode - start and draw once
        $_active_spinner->start(1);
        $_active_spinner->draw($_active_spinner);
    }

    return $_active_spinner;
}

=head3 stop_spinner

    stop_spinner($spinner, $success_message);

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

=cut

lib/Claude/Agent/CLI.pm  view on Meta::CPAN

=cut

sub divider {
    my ($char, $width) = @_;
    $char  //= '-';
    $width //= 60;
    print $char x $width, "\n";
}

=head3 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");

=cut

sub status {
    my ($type, $message) = @_;
    my %colors = (
        success => 'green',
        error   => 'red',
        warning => 'yellow',
        info    => 'cyan',
    );
    my %icons = (
        success => "\x{2713}",  # Check mark
        error   => "\x{2717}",  # X mark
        warning => "\x{26A0}", # Warning sign
        info    => "\x{2139}",  # Info symbol
    );
    my $color = $colors{$type} // 'white';
    my $icon  = $icons{$type} // '*';
    print colored([$color], "$icon $message"), "\n";
}

=head2 Terminal Control

=head3 clear_line

    clear_line();

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

=cut

sub clear_line {
    print "\r\033[K";
}

=head3 move_up

    move_up($n);

Move cursor up N lines.

=cut

sub move_up {
    my ($n) = @_;
    $n //= 1;
    print "\033[${n}A";
}

=head1 DEPENDENCIES

=over 4

=item * Term::ANSIColor (core module)

=item * Term::ReadLine (core module)

=item * Term::Choose

=item * Term::ReadKey (recommended, used by Term::Choose)

=item * Term::ProgressSpinner

=back

=head1 AUTHOR

LNATION, C<< <email at lnation.org> >>

=head1 LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut

1;



( run in 0.897 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )