Acme-Claude-Shell
view release on metacpan or search on metacpan
lib/Acme/Claude/Shell/Query.pm view on Meta::CPAN
package Acme::Claude::Shell::Query;
use 5.020;
use strict;
use warnings;
use Types::Standard qw(InstanceOf Str);
use Marlin
'loop!' => InstanceOf['IO::Async::Loop'],
'dry_run?' => sub { 0 },
'safe_mode?' => sub { 1 },
'working_dir?' => sub { '.' },
'colorful?' => sub { 1 },
'model?' => Str,
'_spinner==.';
use Claude::Agent qw(query create_sdk_mcp_server);
use Claude::Agent::Options;
use Claude::Agent::CLI qw(
start_spinner stop_spinner
header divider status
);
use Acme::Claude::Shell::Tools qw(shell_tools);
use Acme::Claude::Shell::Hooks qw(safety_hooks);
use Future::AsyncAwait;
# Fun spinner styles with matching colors
my @SPINNERS = (
{ spinner => 'moon', spinner_color => 'yellow' },
{ spinner => 'earth', spinner_color => 'cyan' },
{ spinner => 'clock', spinner_color => 'blue' },
{ spinner => 'dots', spinner_color => 'magenta' },
{ spinner => 'material', spinner_color => 'green' },
{ spinner => 'circle_half', spinner_color => 'cyan' },
{ spinner => 'color_circles', spinner_color => 'white' },
);
sub _random_spinner {
return %{ $SPINNERS[rand @SPINNERS] };
}
=head1 NAME
Acme::Claude::Shell::Query - Single-shot query mode for Acme::Claude::Shell
=head1 SYNOPSIS
use Acme::Claude::Shell::Query;
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
my $query = Acme::Claude::Shell::Query->new(
loop => $loop,
dry_run => 0,
safe_mode => 1,
);
my $result = $query->run("find all large log files")->get;
=head1 DESCRIPTION
Executes a single natural language command using Claude's query() function.
Does not maintain session context between calls - each run() is independent.
This is useful for scripting or when you want a one-shot command without
starting an interactive session.
Uses Claude::Agent SDK features:
=over 4
=item * C<query()> - Single-shot prompt execution
=item * SDK MCP tools - execute_command, read_file, list_directory, search_files, get_system_info, get_working_directory
=item * Hooks - PreToolUse (audit), PostToolUse (stats), PostToolUseFailure (errors), Stop (statistics), Notification (logging)
=item * CLI utilities - Spinners and colored output
=back
=head2 Attributes
=over 4
=item * C<loop> (required) - IO::Async::Loop instance
=item * C<dry_run> - Preview mode, don't execute commands (default: 0)
=item * C<safe_mode> - Confirm dangerous commands (default: 1)
=item * C<working_dir> - Starting directory (default: '.')
=item * C<colorful> - Use colored output (default: 1)
=item * C<model> - Claude model to use (optional)
=back
=head2 Methods
=over 4
=item * C<run($prompt)> - Execute a single prompt and return a Future
=back
=cut
async sub run {
my ($self, $prompt) = @_;
# Show header
if ($self->colorful) {
header("Acme::Claude::Shell - Query Mode");
}
# Create SDK MCP server with shell tools
my $mcp = create_sdk_mcp_server(
name => 'shell-tools',
tools => shell_tools($self),
);
# Create options with hooks
my $options = Claude::Agent::Options->new(
permission_mode => 'bypassPermissions',
mcp_servers => { 'shell-tools' => $mcp },
hooks => safety_hooks($self),
dry_run => $self->dry_run,
system_prompt => $self->_system_prompt,
($self->has_model ? (model => $self->model) : ()),
);
# Store spinner in self so hooks can stop it before reading STDIN
# Pick a random fun spinner each time
$self->_spinner(start_spinner("Thinking...", $self->loop,
( run in 0.586 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )