Acme-Claude-Shell

 view release on metacpan or  search on metacpan

bin/acme_claude_shell  view on Meta::CPAN


sub show_help {
    my $c = $opts{'color'};

    if ($c) {
        print colored(['bold', 'cyan'], "\n  Acme::Claude::Shell"), " - AI-powered interactive shell\n\n";
    } else {
        print "\n  Acme::Claude::Shell - AI-powered interactive shell\n\n";
    }

    print_section("Usage", $c);
    print "    acme_claude_shell [options]\n";
    print "    acme_claude_shell -c \"find all large log files\"\n\n";

    print_section("Options", $c);
    print_opt("-n, --dry-run", "Preview mode - show commands without executing", $c);
    print_opt("-u, --unsafe", "Disable safety confirmations for dangerous commands", $c);
    print_opt("--no-color", "Disable colored output", $c);
    print_opt("-d, --directory DIR", "Set working directory (default: current)", $c);
    print_opt("-c, --command CMD", "Run single command and exit (use '-' for stdin)", $c);
    print_opt("-m, --model MODEL", "Claude model to use (e.g., claude-opus-4-5)", $c);
    print_opt("-h, --help", "Show this help", $c);
    print_opt("-v, --version", "Show version", $c);
    print "\n";

    print_section("Examples", $c);
    print "    acme_claude_shell                         # Start interactive shell\n";
    print "    acme_claude_shell --dry-run               # Preview mode\n";
    print "    acme_claude_shell -d /var/log             # Start in specific directory\n";
    print "    acme_claude_shell -c \"list perl files\"    # Single command\n";
    print "    acme_claude_shell -m claude-opus-4-5      # Use a specific model\n";
    print "    echo \"list files\" | acme_claude_shell -c - # Piped input\n\n";

    print_section("Interactive Commands", $c);
    print "    help      Show help inside the shell\n";
    print "    history   Show executed command history\n";
    print "    clear     Clear the screen\n";
    print "    exit      Exit the shell (or 'quit')\n\n";

    print_section("Example Session", $c);
    if ($c) {
        print colored(['green'], "    acme_claude_shell> "), "find all perl files larger than 10k\n";
        print colored(['cyan'], "    Thinking...\n");
        print "    I'll find .pl and .pm files over 10KB:\n\n";
        print colored(['cyan'], "    i "), "Command: find . -name '*.p[lm]' -size +10k\n\n";
        print "    Action:\n";
        print "      [", colored(['cyan'], "a"), "] Approve and run\n";
        print "      [", colored(['cyan'], "d"), "] Dry-run (show only)\n";
        print "      [", colored(['cyan'], "e"), "] Edit command\n";
        print "      [", colored(['cyan'], "x"), "] Cancel\n";

bin/acme_claude_shell  view on Meta::CPAN

        print "    (Claude remembers the previous files...)\n\n";
    } else {
        print "    acme_claude_shell> find all perl files larger than 10k\n";
        print "    Thinking...\n";
        print "    I'll find .pl and .pm files over 10KB:\n";
        print "    Command: find . -name '*.p[lm]' -size +10k\n";
        print "    > a\n";
        print "    ./lib/Acme/Claude/Shell.pm\n\n";
    }

    print_section("SDK Features Demonstrated", $c);
    print "    This module showcases all Claude::Agent SDK features:\n";
    print "    - query()     Single-shot commands (-c option)\n";
    print "    - session()   Multi-turn context (interactive mode)\n";
    print "    - MCP tools   Custom shell execution tools\n";
    print "    - Hooks       Safety confirmation before running\n";
    print "    - Dry-run     Preview without executing\n";
    print "    - IO::Async   Non-blocking spinners\n";
    print "    - CLI utils   Colored output, menus, spinners\n\n";
}

sub show_version {
    my $c = $opts{'color'};
    if ($c) {
        print colored(['bold', 'cyan'], "Acme::Claude::Shell"), " version $VERSION\n";
    } else {
        print "Acme::Claude::Shell version $VERSION\n";
    }
}

sub print_section {
    my ($title, $color) = @_;
    if ($color) {
        print colored(['bold', 'yellow'], "$title:\n");
    } else {
        print "$title:\n";
    }
}

sub print_opt {
    my ($opt, $desc, $color) = @_;

lib/Acme/Claude/Shell/Hooks.pm  view on Meta::CPAN


                    my $duration = time() - ($session->{_session_start} // time());
                    my $tool_count = $session->{_tool_count} // 0;
                    my $tool_errors = $session->{_tool_errors} // 0;
                    my $history_count = scalar(@{$session->_history // []});

                    if ($session->colorful) {
                        print "\n";
                        print colored(['cyan'], "─" x 40) . "\n";
                        status('info', "Session Statistics");
                        printf "  Duration: %.1f seconds\n", $duration;
                        printf "  Tools used: %d\n", $tool_count;
                        printf "  Tool errors: %d\n", $tool_errors if $tool_errors > 0;
                        printf "  Commands in history: %d\n", $history_count;
                        print colored(['cyan'], "─" x 40) . "\n";
                    }
                    else {
                        print "\n--- Session Statistics ---\n";
                        printf "Duration: %.1f seconds\n", $duration;
                        printf "Tools used: %d\n", $tool_count;
                        printf "Tool errors: %d\n", $tool_errors if $tool_errors > 0;
                        printf "Commands in history: %d\n", $history_count;
                        print "--------------------------\n";
                    }

                    return Claude::Agent::Hook::Result->proceed();
                }],
            ),
        ],

t/03-dangerous-patterns.t  view on Meta::CPAN


# Test the dangerous command pattern detection in Tools.pm

# We need to access the internal _check_dangerous function
# Since it's not exported, we'll test it by loading the module and using
# the package namespace

use_ok('Acme::Claude::Shell::Tools');

# Get access to the dangerous patterns check
# This tests the module's internal security patterns
package Acme::Claude::Shell::Tools;

package main;

# Define the same patterns for testing purposes
my @DANGEROUS_PATTERNS = (
    { pattern => qr/\brm\s+(-[rf]+|--recursive|--force)/i,
      reason  => 'Recursive or forced file deletion' },
    { pattern => qr/\bsudo\b/,
      reason  => 'Superuser command' },



( run in 0.589 second using v1.01-cache-2.11-cpan-39bf76dae61 )