Claude-Agent
view release on metacpan or search on metacpan
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
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 2.706 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )