App-Raider

 view release on metacpan or  search on metacpan

.claude/skills/perl-ai-langertha/SKILL.md  view on Meta::CPAN


<raider>
## Raider — Autonomous Agent

```perl
use Langertha::Raider;

my $raider = Langertha::Raider->new(
    engine         => $engine,        # With MCP servers
    mission        => 'You are a code reviewer.',
    max_iterations => 10,             # Max tool rounds per raid
    # Optional:
    max_context_tokens         => 4000,
    context_compress_threshold => 0.75,
    compression_engine         => $cheap_model,
    raider_mcp                 => 1,   # Enable self-tools (ask_user, pause, abort)
    plugins                    => ['Langfuse'],
);

# Raid (autonomous tool-calling loop)
my $result = await $raider->raid_f('Review lib/App.pm');

.claude/skills/perl-ai-langertha/SKILL.md  view on Meta::CPAN

if ($result->is_question) {
    my $next = await $raider->respond_f('Yes, go ahead.');
}

# History management
$raider->add_history('user', $content);  # Replay from DB
$raider->clear_history;                  # Reset

# Metrics
my $m = $raider->metrics;
say "Iterations: $m->{iterations}";
say "Tool calls: $m->{tool_calls}";
```

### Raid Loop (simplified)

1. Auto-compress history if context threshold exceeded
2. Gather tools from MCP servers + inline tools + self-tools
3. Build conversation: mission + history + new messages
4. Call LLM with tools
5. If tool calls: execute via MCP, add results to conversation, loop
6. If no tool calls: extract final text, persist to history, return result
7. Max iterations safety limit
</raider>

<plugins>
## Plugin System

```perl
package Langertha::Plugin::MyGuardrails;
use Langertha qw( Plugin );

async sub plugin_before_tool_call {

.claude/skills/perl-mcp/SKILL.md  view on Meta::CPAN

        mcp_servers => [$mcp],
    );

    # 4a. One-shot tool calling
    my $response = await $engine->chat_with_tools_f('Add 42 and 17');

    # 4b. Or Raider for multi-turn
    my $raider = Langertha::Raider->new(
        engine         => $engine,
        mission        => 'You are a calculator.',
        max_iterations => 10,
    );
    my $result = await $raider->raid_f('Add 42 and 17');
}

main()->get;
```
</async-integration>

<tool-description-tips>
## Writing Good Tool Descriptions

Changes  view on Meta::CPAN

      fails with "invalid to load directly"; now detected via
      Term::ReadLine->new + ->ReadLine =~ /Gnu/.
    - Readline history now persists across Ctrl-C / SIGTERM via signal
      handlers that call WriteHistory before exit.

0.001     2026-04-20 17:44:05Z

    - Initial release.
    - CLI agent (App::Raider) wrapping Langertha::Raider with a default
      viking persona ("Langertha"), caveman-style communication, and
      effectively unlimited tool-calling iterations per raid.
    - Tools: filesystem (list_files, read_file, write_file, edit_file),
      bash (MCP::Server::Run::Bash), web_search and web_fetch
      (Net::Async::WebSearch + Net::Async::HTTP).
    - Engine auto-detection from available *_API_KEY env var with cheap
      per-engine default models (claude-haiku-4-5, gpt-4o-mini, etc.).
    - .raider.md for persona customization; hot-reload via /reload;
      /prompt spawns a sub-agent prompt-builder that writes .raider.md.
    - .raider.yml for engine options (temperature, response_size, ...)
      with flat or default/<engine> layers; -o key=value CLI override.
    - Skill / profile loading: --claude loads CLAUDE.md and

bin/raider  view on Meta::CPAN


GetOptions(
  'e|engine=s'       => \$opt{engine},
  'm|model=s'        => \$opt{model},
  'r|root=s'         => \$opt{root},
  'M|mission=s'      => \$opt{mission},
  'k|api-key=s'      => \$opt{api_key},
  'o|option=s@'      => \@raw_engine_opts,
  'i|interactive'    => \$opt{interactive},
  'json'             => \$opt{json},
  'max-iterations=i' => \$opt{max_iterations},
  'no-color'         => \$opt{no_color},
  'trace!'           => \$opt{trace},
  'customize-prompt'     => \$opt{customize_prompt},
  'claude'               => \$opt{profile_claude},
  'openai|codex'         => \$opt{profile_openai},
  'skills=s@'            => \@{ $opt{skill_dirs} //= [] },
  'export-skill:s'       => \$opt{export_skill},
  'export-claude-skill:s'=> \$opt{export_claude_skill},
  'h|help'               => \$opt{help},
) or die "Bad options. Try --help.\n";

bin/raider  view on Meta::CPAN

  -k, --api-key KEY        API key (overrides *_API_KEY env var)
  -o, --option KEY=VALUE   Engine attribute (repeatable), e.g.
                           -o temperature=0.2 -o response_size=4096
                           Merged over .raider.yml; CLI wins.
  -r, --root DIR           Working directory (default: cwd). File tools are
                           confined to this directory.
  -M, --mission TEXT       System prompt / mission
  -i, --interactive        REPL mode (default when stdin is a TTY with no
                           prompt argv and no pipe; forces it otherwise)
      --json               Emit JSON ({response, metrics, elapsed}) and exit
      --max-iterations N   Hard safety cap on tool rounds per raid
                           (default: 10000 — effectively unlimited)
      --no-color           Disable ANSI colors
      --no-trace           Hide live tool-call progress output
      --customize-prompt   Launch the prompt-builder at startup
      --claude             Load Claude Code layout: CLAUDE.md +
                           .claude/skills/*/SKILL.md.
      --openai / --codex   Load AGENTS.md (the OpenAI Codex / cross-tool
                           convention).
      --skills DIR         Load *.md files from DIR as skills (repeatable).
      --export-skill [PATH]

bin/raider  view on Meta::CPAN

  exit 0;
}

my %args;
$args{engine}         = $opt{engine}         if defined $opt{engine};
$args{model}          = $opt{model}          if defined $opt{model};
$args{root}           = $opt{root}           if defined $opt{root};
$args{mission}        = $opt{mission}        if defined $opt{mission};
$args{api_key}        = $opt{api_key}        if defined $opt{api_key};
$args{trace}          = $opt{trace}          if defined $opt{trace};
$args{max_iterations} = $opt{max_iterations} if defined $opt{max_iterations};
$args{engine_options} = \%engine_opts        if %engine_opts;

my @skill_specs;
my @cli_profiles;
if ($opt{profile_claude}) {
  push @skill_specs, @{ $App::Raider::AGENT_PROFILES{claude} };
  push @cli_profiles, 'claude';
}
if ($opt{profile_openai}) {
  push @skill_specs, @{ $App::Raider::AGENT_PROFILES{openai} };

bin/raider  view on Meta::CPAN

  if ($cmd eq 'clear') {
    $app->raider->clear_history;
    my $t = $app->trace_plugin;
    $t->token_stats({ prompt => 0, completion => 0, total => 0, calls => 0 }) if $t;
    print_meta("history cleared.");
    return;
  }
  if ($cmd eq 'metrics') {
    my $m = $app->raider->metrics;
    print_meta(sprintf(
      "raids=%d iterations=%d tool_calls=%d time_ms=%d",
      $m->{raids}, $m->{iterations}, $m->{tool_calls}, $m->{time_ms},
    ));
    return;
  }
  if ($cmd eq 'stats') {
    my $s = $app->token_stats;
    unless ($s) {
      print_meta("stats unavailable (trace is off — start without --no-trace).");
      return;
    }
    print_meta(sprintf(

bin/raider  view on Meta::CPAN

    "/done" to return to the main agent (the main agent will auto-reload the
    new persona).
  - If the user asks you to cancel, do not write anything; just confirm.
  - Do NOT call bash or any tool other than read_file / write_file / edit_file
    during this session.
EOM

  my $builder_raider = Langertha::Raider->new(
    engine         => $app->_engine,
    mission        => $meta_mission,
    max_iterations => 20,
  );

  print_meta("entering prompt-builder. /done to return, /cancel to discard.");
  my $ps = 'raider:prompt> ';

  while (1) {
    print $ps;
    my $line = <STDIN>;
    last unless defined $line;
    chomp $line;

lib/App/Raider.pm  view on Meta::CPAN

Tools (MCP):
  - list_files(path)
  - read_file(path)
  - write_file(path, content)
  - edit_file(path, old_string, new_string)
  - bash(command, [working_directory], [timeout])
  - web_search(query, [limit])
  - web_fetch(url, [as_html])

How you work:
  - User turn = task. Pursue with tools until done. Unlimited iterations.
  - Read before write. No guessing file contents.
  - After write_file / edit_file: verify. Re-read, or run check (perl -c,
    tests, etc.).
  - Small targeted edits > full rewrites.
  - bash is full shell, not sandbox. Use freely.
  - Skip irreversible ops (rm -rf, git reset --hard, force pushes) unless
    user explicit ask.

Communication (Caveman mode, default):
  - Terse. Drop articles (a/an/the), filler (just/really/basically/

lib/App/Raider.pm  view on Meta::CPAN

);


has allowed_commands => (
  is        => 'ro',
  isa       => 'ArrayRef[Str]',
  predicate => 'has_allowed_commands',
);


has max_iterations => (
  is      => 'ro',
  isa     => 'Int',
  default => 10_000,
);


has trace => (
  is      => 'ro',
  isa     => 'Bool',
  default => sub { -t STDOUT ? 1 : 0 },

lib/App/Raider.pm  view on Meta::CPAN

  my @plugins;
  if ($self->trace) {
    # Pass as name(s) so PluginHost injects `host` (required by
    # Langertha::Plugin). The flat-list form is [Name, {args}, Name, ...].
    push @plugins, '+App::Raider::Plugin::Trace';
  }
  push @plugins, '+App::Raider::Plugin::Situation';
  return Langertha::Raider->new(
    engine                     => $self->_engine,
    mission                    => $self->mission,
    max_iterations             => $self->max_iterations,
    max_context_tokens         => $self->max_context_tokens,
    context_compress_threshold => $self->context_compress_threshold,
    (@plugins ? (plugins => \@plugins) : ()),
  );
}


async sub raid_f {
  my ($self, @messages) = @_;
  for my $mcp (@{$self->_mcps}) {

lib/App/Raider.pm  view on Meta::CPAN


Working directory for tool operations. Defaults to the current process cwd.
File tools are chrooted to this directory; bash commands inherit it as their
default working directory.

=head2 allowed_commands

Optional arrayref restricting which bash commands may run (first word match).
When undef, any command is allowed.

=head2 max_iterations

Maximum tool-calling iterations per raid. Defaults to 10_000 — effectively
unlimited, so a raid only ends when the model itself stops emitting tool
calls. The conversation history is preserved between raids, so the next user
message in the REPL simply continues the same thread.

Set this to a smaller number if you want a hard safety cap.

=head2 trace

Emit live ANSI-colored progress output (iteration markers, tool calls, tool
results) via L<App::Raider::Plugin::Trace>. Defaults to on when STDOUT is a

lib/App/Raider/Skill.pm  view on Meta::CPAN

  temperature: 0.7
  response_size: 8192
```

CLI `-o key=value` overrides the file.

## Context window and rate limits

- `max_context_tokens = 40000`
- `context_compress_threshold = 0.7`
- `max_iterations = 10000`

At 70% of the token budget, `Langertha::Raider` compresses the history
automatically. Each raid prints `history N msgs, X/Y tok (Z%)` so you can
see how close you are.

## Environment variables for API keys

`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `DEEPSEEK_API_KEY`, `GROQ_API_KEY`,
`MISTRAL_API_KEY`, `GEMINI_API_KEY`, `MINIMAX_API_KEY`, `CEREBRAS_API_KEY`,
`OPENROUTER_API_KEY`.



( run in 1.526 second using v1.01-cache-2.11-cpan-96521ef73a4 )