Claude-Agent

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.10    2026-01-11
        [Socket Lifecycle]
        - Added cleanup() method to Query for explicit socket cleanup
        - Fixes socket accumulation in iteration loops (e.g., refactor_until_clean)
        - SDK server sockets now cleaned up after each query iteration
        - _resolve_pending_futures_on_finish now delegates to cleanup()

0.08    2026-01-11
        [Async Support]
        - Async tool handlers: return Futures for non-blocking I/O
        - Async hooks: callbacks can return Futures for async validation
        - IO::Async::Loop passed to handlers/hooks for event-driven operations
        - Backward compatible: sync handlers/hooks still work unchanged

        [Debug Logging]
        - Comprehensive operational logging (set CLAUDE_AGENT_DEBUG=1)
        - Message lifecycle: receive, queue, deliver, process
        - Socket lifecycle: connect, accept, close
        - Tool execution: start, complete, results
        - Hook execution: matchers, decisions, blocked tools

README  view on Meta::CPAN

            last;
        }
    }

FEATURES

    - Async support via IO::Async and Future::AsyncAwait
    - Streaming message iteration (blocking and async)
    - Full tool support (Read, Write, Edit, Bash, Glob, Grep, WebFetch, etc.)
    - Hook system for intercepting tool calls
    - Permission callbacks for fine-grained control
    - MCP server integration (SDK, stdio, SSE, HTTP)
    - Custom subagent definitions
    - Structured JSON output with schema validation
    - Session management (resume, fork)

REQUIREMENTS

    - Perl 5.020 or higher
    - Claude Code CLI installed (npm install -g @anthropic-ai/claude-code)
    - Valid Anthropic API key or Claude subscription

examples/03-hooks.pl  view on Meta::CPAN

#!/usr/bin/env perl
#
# Perl Hooks Example
#
# This example demonstrates how to use Perl hook callbacks
# to intercept and control tool execution.
#

use 5.020;
use strict;
use warnings;

use lib 'lib';
use Claude::Agent qw(query);
use Claude::Agent::Options;

examples/13-async-hook.pl  view on Meta::CPAN

#!/usr/bin/env perl
#
# Async Hooks Example
#
# This example demonstrates how to use asynchronous Perl hook callbacks
# for operations that require I/O, such as:
# - HTTP requests to external validation services
# - Database lookups for permission checks
# - Async logging or metrics collection
#
# Hooks can return either:
# - A hashref (synchronous, backward compatible)
# - A Future that resolves to a hashref (asynchronous)
#

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

        #   - Aliases that expand to write commands
        #   - Less common destructive tools (shred, truncate via other means)
        #   - Custom scripts that perform write operations
        #
        # For security-critical applications, consider:
        #   1. Set CLAUDE_AGENT_DRY_RUN_STRICT=1 to block all non-whitelisted commands
        #   2. Running in a sandboxed environment (containers, VMs)
        #   3. Using custom PreToolUse hooks with stricter validation
        #
        # WARNING: Always print security notice to STDERR for Bash commands
        # This ensures users are aware of limitations even if callbacks suppress output
        if (!$ENV{CLAUDE_AGENT_DRY_RUN_QUIET}) {
            state $dry_run_warned = 0;
            warn "[DRY-RUN WARNING] Bash command detection is bypassable. "
                . "Set CLAUDE_AGENT_DRY_RUN_STRICT=1 for stricter protection.\n"
                unless $dry_run_warned++;
        }
        # More precise command detection: check if dangerous command is at start or after pipe/semicolon/&&
        # This avoids false positives like 'grep rm file.txt' or 'echo rm > log.txt'
        my @dangerous_cmds = qw(rm rmdir mv cp mkdir touch chmod chown dd truncate install ln patch rsync shred);
        for my $cmd (@dangerous_cmds) {

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

=item * SessionEnd - When a session ends

=item * Notification - For notifications

=back

=head1 HOOK CLASSES

=over 4

=item * L<Claude::Agent::Hook::Matcher> - Match tools and run callbacks

=item * L<Claude::Agent::Hook::Context> - Context passed to callbacks

=item * L<Claude::Agent::Hook::Result> - Factory for hook results

=back

=cut

# Hook event constants
use Const::XS qw(const);
const our $PRE_TOOL_USE         => 'PreToolUse';

lib/Claude/Agent/Hook/Context.pm  view on Meta::CPAN

    'cwd?'        => Str,
    'tool_name?'  => Str,
    'tool_input?';

=head1 NAME

Claude::Agent::Hook::Context - Hook context for Claude Agent SDK

=head1 DESCRIPTION

Context information passed to hook callbacks.

=head2 ATTRIBUTES

=over 4

=item * session_id - Current session ID

=item * cwd - Current working directory

=item * tool_name - Name of the tool being called

lib/Claude/Agent/Hook/Executor.pm  view on Meta::CPAN


    # Execute pre-tool-use hooks
    my $result = $executor->run_pre_tool_use($tool_name, $tool_input, $tool_use_id);

    if ($result->{decision} eq 'deny') {
        # Block the tool
    }

=head1 DESCRIPTION

This module executes Perl hook callbacks when tool use events occur.
It intercepts tool calls in the Query layer and runs matching hooks.

=head1 ATTRIBUTES

=head2 hooks

HashRef of hook event names to arrayrefs of L<Claude::Agent::Hook::Matcher> objects.

=head2 session_id

lib/Claude/Agent/Hook/Matcher.pm  view on Meta::CPAN

    'matcher',                    # Regex pattern for tool names (optional)
    'hooks'   => sub { [] },      # ArrayRef of coderefs
    'timeout' => sub { 60 };      # Timeout in seconds

=head1 NAME

Claude::Agent::Hook::Matcher - Hook matcher for Claude Agent SDK

=head1 DESCRIPTION

Defines a hook matcher that triggers callbacks for specific tools.

=head2 ATTRIBUTES

=over 4

=item * matcher - Optional regex pattern to match tool names

=item * hooks - ArrayRef of callback coderefs

=item * timeout - Timeout in seconds (default: 60)

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

    'loop?',                                       # Optional external IO::Async loop
    '_loop==.',                                    # Internal loop reference (rw, no init_arg)
    '_process==.',                                 # IO::Async::Process handle
    '_stdin==.',                                   # stdin pipe for sending messages
    '_messages==.' => sub { [] },                  # Message queue
    '_pending_futures==.' => sub { [] },           # Futures waiting for messages
    '_session_id==.',                              # Session ID from init message
    '_finished==.' => sub { 0 },                   # Process finished flag
    '_error==.',                                   # Error message if process failed
    '_sdk_servers==.' => sub { {} },               # SDK server wrappers (name => SDKServer)
    '_hook_executor==.',                            # Hook executor for Perl callbacks
    '_pending_tool_uses==.' => sub { {} },          # Track tool uses awaiting results
    '_processing_message==.' => sub { 0 },           # Guard against concurrent message processing
    '_cleaned_up==.' => sub { 0 },                     # Track if cleanup() has been called
    '_jsonl==.' => sub {
        JSON::Lines->new(
            utf8     => 1,
            error_cb => sub {
                my ($action, $error, $data) = @_;
                # Only log at trace level since parse errors are common
                # with streaming JSON and partial data



( run in 1.071 second using v1.01-cache-2.11-cpan-140bd7fdf52 )