#!/usr/bin/env perl
# PODNAME: raider
# ABSTRACT: Autonomous CLI agent with filesystem and bash access

use strict;
use warnings;

our $VERSION = '0.504';

use Langertha::Raider::CLI::Main;
use Langertha::Raider::Loadguard;

# LOADGUARD_CONFINE=1: start again inside a memory-limited systemd user scope
# (Langertha::Raider::Loadguard/confine_command). Off by default; a failed
# exec goes on unconfined.
{
  my $guard = Langertha::Raider::Loadguard->new;
  if ( my @confined = $guard->confine_command( command => [ $guard->self_command ] ) ) {
    $ENV{LOADGUARD_CONFINED} = 1;
    exec @confined;
  }
}

binmode STDOUT, ':encoding(UTF-8)';
binmode STDIN,  ':encoding(UTF-8)';

exit Langertha::Raider::CLI::Main->new->run(@ARGV);

__END__

=pod

=encoding UTF-8

=head1 NAME

raider - Autonomous CLI agent with filesystem and bash access

=head1 VERSION

version 0.504

=head1 SYNOPSIS

    raider "Find all TODO comments in lib/ and summarize them"
    raider -e openai -m gpt-4o "Run the test suite and fix any trivial failures"
    raider -i -r ~/dev/myproject
    raider --perl --pack testing-fu "Run prove -l t and explain failures"
    raider --json "Summarize README.md" > result.json
    raider --stream-json "Run the tests" | jq -c 'select(.type == "tool.call")'
    raider config explain -m gpt-4o
    raider config migrate --dry-run
    raider session list
    raider --continue "Now fix the first one"
    raider provider inspect provider.example --json
    raider --provider provider.example -m example-model -k "$KEY" "Summarize README.md"
    raider provider add acme provider.example --credential-env ACME_API_KEY
    raider provider list
    raider --provider acme "Summarize README.md"
    raider hall start --acp-port 38421
    raider acp prompt 127.0.0.1:38421 --raider bjorn "Summarize status"

=head1 DESCRIPTION

F<raider> is the command line of L<Langertha::Raider::CLI>: it runs
L<Langertha::Raider::CLI::Main>, which parses the options below and builds
a L<Langertha::Raider::CLI> for the working directory. That wires an LLM engine
(via L<Langertha>) to the file tools, C<bash> (L<Langertha::Raider::BashTools>) and the web tools,
then runs the L<Langertha::Raider> multi-turn agent loop on your prompt.
Optional profile, pack, and Perl-native tool flags let the same CLI load
project agent instructions, persona/power bundles, and C<perl_eval> /
C<perl_check> / C<perl_cpanm> tools.

Settings of a project live in its config file: F<.raider/config.yml> in the
working directory, or else the legacy F<.raider.yml> there. Both take the
same keys; where this page names F<.raider.yml>, the file in use is meant.
When both exist, only F<.raider/config.yml> is read: F<raider> warns on
standard error that it ignores F<.raider.yml>, and C<config explain> names
the file in use and the ignored one. What F<raider> saves (C</model>,
C<--claude>, C<--openai>, C<--skills>) goes to the file in use, and to
F<.raider.yml> when there is none.

Under the project file lies the home file F<~/.raider/config.yml>, with
the same keys: built-in defaults, then the home file, then the project
file, then the command line. The project replaces a home value, except
that C<skills> and C<no_detect> add up and C<detect:> rules are replaced
per pack. F<raider> never writes the home file. Run in the home directory
itself, F<~/.raider/config.yml> is the project file and is read once. A
home file that does not parse stops F<raider> like a broken project file.

C<project_tools> in F<~/.raider/config.yml> maps a workspace selector
(C<*>, a path glob starting with C</> or C<~/>, or a workspace name) to
the home tools a project gets. Only the home file grants: in a project file
it is ignored, and C<config explain> says so. For now C<config explain>
only shows which selectors match the project and which tools they grant;
nothing is mounted from it yet, and workspace names never match. An
invalid C<project_tools> stops F<raider> with exit status 3.

The project instructions follow the same rule: F<.raider/instructions.md>
in the working directory, or else the legacy F<.raider.md> there, is added
to the default persona. Where this page names F<.raider.md>, the file in
use is meant. When both exist, only F<.raider/instructions.md> is read,
F<raider> warns that it ignores F<.raider.md>, and C<config explain> names
the ignored file under C<instructions:>. The prompt-builder (C</prompt>)
writes the file in use, and F<.raider.md> when there is none.

C<raider config migrate [--dry-run] [-r DIR]> moves the legacy files of a
project to the new layout: F<.raider.yml> to F<.raider/config.yml> and
F<.raider.md> to F<.raider/instructions.md>, each only when it exists. It
first shows what it does -- per file the new file, the backup and whether
the content changes -- and with C<--dry-run> stops there. Each new file is
written atomically with the legacy file's permissions, then the legacy
file is renamed to F<.raider.yml.bak> or F<.raider.md.bak>, so afterwards
the same settings are read from the new files; F<.raider/> is created with
its F<.gitignore>. A step that fails leaves its legacy file in use. An
C<api_key> (top level, C<default:> or an engine section) is not copied
into F<.raider/config.yml>, which is meant to be shared: it is reported
with where it was, never printed, and belongs in F<~/.raider/config.yml>
or the engine's C<*_API_KEY> variable; its lines are dropped (comments
stay), and where that would change the meaning of the file the file is
rewritten from its parsed content, which drops comments and says so.
Nothing is merged: when a new file or a backup already exists, when
F<.raider> is no directory, or in the home directory itself (where
F<.raider/config.yml> is the home file), nothing is written and the exit
status is 1. Only C<config migrate> in front of the options is the
subcommand. Its report is for humans only; there is no C<--json> form.

Interactive mode (C<-i>) opens a REPL with conversation history retained
across turns. With L<Term::ReadLine::Gnu> installed, line editing and
persistent input history (F<~/.raider_history>) are enabled automatically —
including across Ctrl-C interrupts. Slash commands available in the REPL:
C</help>, C</clear>, C</metrics>, C</stats>, C</reload>, C</prompt>,
C</skill>, C</skill-claude>, C</config>, C</model>, C</packs>, C</pack>,
C</quit>.

Two more line prefixes run shell commands, with C<$SHELL -c> (C</bin/sh>
without C<SHELL>) in the root (C<--root>); Ctrl-C while one runs ends the
command, not F<raider>. C<!CMD> runs C<CMD> on the terminal (C<less> and
C<vim> work), prints a non-zero exit status, and sends nothing to the model
and records nothing in the session. C<?CMD> runs C<CMD> with its output
shown as it comes and captured (standard input is F</dev/null>), then sends
the command, its exit status and its output -- standard output and
standard error, cut to its first and last 10000 characters when longer
than 20000 -- to the model as the next prompt, recorded in the session like
any other. A C<?CMD> ended by Ctrl-C sends nothing. A line that is only
C<!> or C<?> is a prompt.

Subcommands are dispatched before normal raider startup: C<raider hall ...>
manages a Raider Hall daemon, and C<raider acp ...> runs the bundled ACP
client. C<raider config explain [options]> takes the normal options and
prints every effective setting with its source -- a command-line flag, a
layer of the config file, an environment variable or the built-in default --
without writing the config file or contacting a model; C</config> prints the
same inside the REPL. The options may also come first
(C<raider -e openai config explain>); there only the exact words
C<config explain> are the subcommand, any other prompt starting with
C<config> is sent as a prompt. C<raider provider inspect HOST> fetches,
validates and shows a provider's manifest, and C<raider --provider HOST>
runs on the endpoint it declares; see L</PROVIDERS>.

With C<--json>, C<--msgpack> or C<--yaml>, F<raider> prints one document
describing the run instead of the human-oriented output, which is useful
for scripting; C<--stream-json>, C<--stream-msgpack> and C<--stream-yaml>
follow the run as it happens. See L</MACHINE OUTPUT>.

When standard input is not a terminal, the REPL (C<-i>) reads its lines
from it and ends at its end.

=head1 TOOLS

The model gets one tool set per run, and the tool list in its prompt is
generated from exactly the tools the engine is offered:

=over

=item * C<list_files>, C<read_file>, C<write_file>, C<edit_file>, C<multi_edit>
(L<Langertha::Raider::FileTools>), answering as Claude Code's Read, Write,
Edit and MultiEdit (ADR 0023): C<read_file> in C<cat -n> form with
C<offset> / C<limit>; an existing file must have been read in the run before
it is written or edited. Confined to C<-r> and the additional directories;
in the Claude layout a path outside them is asked about instead (allow rules
such as C<Read(//tmp/**)> open it).

=item * C<bash> (L<Langertha::Raider::BashTools>), a real shell started in C<-r>,
120 seconds per command by default (at most 600); a command still running
then goes on in the background, as does one started with
C<run_in_background>; C<bash_output> reads it and C<kill_shell> stops it. It is not confined to C<-r>. A heavy
command (C<prove>, C<make test>, C<dzil test>, C<cpanm>, C<podman run>, ...)
is refused with the reason while the host is under memory pressure or its
heavy slots are taken (L<Langertha::Raider::Loadguard>, the C<LOADGUARD_*>
variables of the loadguard plugin: C<LOADGUARD_PSI_FULL>,
C<LOADGUARD_SWAP_USED>, C<LOADGUARD_HEAVY_SLOTS>, C<LOADGUARD_THROTTLE=0> for
off). C<LOADGUARD_CONFINE=1> starts raider in a memory-limited systemd user
scope; off by default.

=item * C<web_search>, C<web_fetch> (L<Langertha::Raider::WebTools>).

=item * C<glob>, C<grep> (L<Langertha::Raider::SearchTools>), confined to
C<-r>; C<grep> uses ripgrep when it is installed.

=item * C<task_create>, C<task_update>, C<task_list>, C<task_get>
(L<Langertha::Raider::TodoTools>), the task list of the conversation --
C<todo_write>, the task list of the run, instead with
C<RAIDER_ENABLE_TASKS=0> -- and C<ask_user> (L<Langertha::Raider::AskTools>),
a question to the person at the REPL -- refused in a run nobody can answer.

=item * C<enter_plan_mode> (L<Langertha::Raider::PlanTools>), which switches
to the C<plan> permission mode once the person agrees, and C<exit_plan_mode>
once the run has been in that mode (see L</PERMISSIONS>).

=item * C<perl_eval>, C<perl_check>, C<perl_cpanm>
(L<Langertha::Raider::PerlTools>) when granted: by C<--perl>, by C<perl:>
in F<.raider.yml> (C<perl: false> denies them), or else when an active pack
requests them -- the bundled C<perl> pack, detected in a Perl workspace.
Switching such a pack with C</pack> or re-detecting it with C</reload>
mounts or unmounts them for the next raid.

=item * C<telegram_reply>, C<hall_status>, C<hall_spawn>
(L<Langertha::Raider::HallTools>) only for a raider a Hall started (see
L</RAIDER_HALL_SOCKET>).

=back

C<raider config explain> shows whether the Perl tools are mounted and why;
C<--export-skill> writes the same tool list as a table.

=head1 PERMISSIONS

Every tool call passes a permission policy as in Claude Code (ADR 0021,
L<Langertha::Raider::Permissions>). Without a matching rule the mode
decides: C<default> runs reading tools and asks before writing, C<bash>,
the network and messages; C<acceptEdits> also writes files inside C<-r> and
the additional directories; C<auto> (Claude Code's C<defaultMode: "auto">)
is that plus bash commands from a fixed read-only list (C<ls>, C<git status>,
C<grep>, ... where every part of the command is on it, no substitution, no
redirection into a file); C<plan> reads only, refuses changes even with an
allow rule, and offers C<exit_plan_mode>; C<bypassPermissions> runs
everything a deny rule does not name. The mode comes from
C<--permission-mode> (or C<--dangerously-skip-permissions>), else
C<permissions.default_mode> of F<.raider/config.yml> and
F<~/.raider/config.yml>, else C<defaultMode> of the Claude settings, else
C<default>.

Rules in Claude Code syntax (C<Bash(npm run test:*)>, C<Edit(src/**)>,
C<Read(./.env)>, C<WebFetch(domain:example.com)>, C<mcp__server__tool>)
come from the Claude settings (in the Claude layout) and from
C<permissions:> (C<allow>, C<ask>, C<deny>, C<default_mode>,
C<additional_directories>) of the config files; deny before ask before
allow, whatever the source.

In the REPL a call that asks is put to you: yes once, yes for this session
(the rule it adds is shown), or no with a reason the model gets. With
nobody to ask -- a one-shot run, a machine output flag, the Hall, ACP --
such a call is refused, and the model is told which rule or mode would
allow it. C<raider config explain> and C</permissions> show the policy.

=head1 OPTIONS

=head2 -e, --engine NAME

The engine: C<anthropic>, C<openai>, C<deepseek>, C<groq>, C<mistral>,
C<gemini>, C<minimax>, C<cerebras>, C<openrouter> or C<ollama>. Without
it, C<-o engine=>, then C<engine:> in F<.raider.yml>, then the first API key
set in the environment (C<anthropic>, C<openai>, C<deepseek>, C<groq>,
C<mistral>, C<gemini>, in that order; see L</ENVIRONMENT>) decide, else
C<anthropic>.

=head2 -m, --model NAME

The model. Without it, C<-o model=>, then C<model:> in F<.raider.yml>, then
a cheap per-engine default (C<claude-haiku-4-5>, C<gpt-4o-mini>, ...), else
the engine's own default. C<openrouter> needs one.

=head2 -k, --api-key KEY

The API key, over C<api_key:> in F<.raider.yml> and the engine's
environment variable. With C<--provider> the only source of the key,
together with C<-o api_key=>.

=head2 --provider HOST[:PORT] | https://HOST[:PORT] | ALIAS

Runs on the model endpoint a provider's manifest declares, for this one
run; see L</Using a provider>. A name without dot, port or C<https://> is
always an alias (L</Stored aliases>); one that is not stored is a usage
error and nothing is fetched. Not with C<-e>, C<-o engine=>, C<-o url=>
or C<config explain>.

=head2 --provider-endpoint ID

With C<--provider>: the manifest endpoint to run on, when the model is
offered on several; see L</Several endpoints and failures>.

=head2 --allow-internal

With C<--provider> (and C<provider inspect>): releases loopback, private,
link-local and reserved provider addresses; see L</PROVIDERS>.

=head2 -o, --option KEY=VALUE

An engine attribute (repeatable), e.g. C<-o temperature=0.2 -o
response_size=4096>, merged over F<.raider.yml>; the command line wins.
Integers, decimals and C<true> / C<false> become numbers. raider's own
F<.raider.yml> keys (C<engine>, C<perl>, C<packs=a,b>, C<skills=a,b>,
C<detect>, C<no_detect=a,b>, C<preferred_lib_target>, C<web_fetch_model>) configure raider
instead and never reach the engine.

=head2 -r, --root DIR

The working directory, default the current one. The file tools are
confined to it, C<bash> starts in it, and F<.raider/config.yml> (or
F<.raider.yml>), F<.raider/instructions.md> (or F<.raider.md>) and
F<.raider/> are read from it.

=head2 -M, --mission TEXT

The instructions: replaces the default persona and F<.raider.md> for this
run. Skills, packs, the Claude memory and the tool list still apply.

=head2 --bare

An isolated context: no F<.raider.md>, no Claude Code layout, no skills,
no C<packs:>, no default packs, no detection. C<--pack NAME> and C</pack NAME> still work.

=head2 -i, --interactive

The REPL. It is the default when standard input is a terminal and no prompt
is given; C<-i> also forces it otherwise.

=head2 --json[=N], --msgpack[=N], --yaml[=N]

Print one document for the run, see L</MACHINE OUTPUT>.

=head2 --stream-json[=N], --stream-msgpack[=N], --stream-yaml[=N]

Print the run as events while it happens, ending with the document; see
L</Streams>.

=head2 --no-session

Record nothing; see L</SESSIONS>.

=head2 --session ID

Continue session C<ID>: its conversation is replayed and the run (or the
REPL) is recorded in it.

=head2 --continue

The same with the newest session of the project.

=head2 --max-iterations N

Hard cap on tool rounds per raid. Default 10000, effectively unlimited.

=head2 --no-color

No ANSI colors, as with C<ANSI_COLORS_DISABLED>.

=head2 --trace, --no-trace

Show or hide the live progress of tool calls. On by default when standard
output is a terminal; off with a machine output flag, where C<--trace>
sends it to standard error.

=head2 --perl

Mount the Perl tools (C<perl_eval>, C<perl_check>, C<perl_cpanm>), see
L</TOOLS>.

=head2 --pack NAME

Enable a pack (repeatable). Given at all, the listed packs replace
C<packs:> of F<.raider.yml> and the default packs; detection still adds.

=head2 --no-pack NAME

Switch a pack off (repeatable), also a default or detected one.

=head2 --no-detect, --detect

Switch pack detection off, or on over C<detect: false> in F<.raider.yml>.

=head2 --claude

Read the Claude Code layout also in a raider project (one with
F<.raider/>): the F<CLAUDE.md> hierarchy (user, repository top down to the
working directory, C<CLAUDE.local.md>, C<@imports>, nested files on first
touch), F<.claude/settings*.json> (its C<env> is set for the tools), the
skills of F<~/.claude/skills>, F<.claude/skills>, F<.agents/skills> and of
the enabled plugins. Saved as C<claude: true> in F<.raider.yml> on first
use (the older C<skills: [claude]> means the same). A project with only
Claude Code files reads that layout without the flag, and nothing is saved
there.

=head2 --openai, --codex

Load F<AGENTS.md> as instructions. Saved to C<skills:> in F<.raider.yml>
on first use.

=head2 --skills DIR

Offer the skills in C<DIR> (repeatable): every F<*/SKILL.md> and every
F<*.md> in it. The prompt lists them by name and description; the model
loads one with the C<skill> tool. Saved like C<--openai>.

=head2 --permission-mode MODE

The permission mode of the run: C<default>, C<acceptEdits>, C<auto>, C<plan> or
C<bypassPermissions> (see L</PERMISSIONS>). It wins over the mode of the
config files and the Claude settings. Any other name is a usage error.

=head2 --permission-channel PATH

Not for people: the hall (C<raider hall>) sets it on a run that belongs to
an ACP session. PATH is a Unix socket on which a tool call the permission
policy asks about is put to the hall, which asks the ACP client with
C<session/request_permission> (ADR 0021). No answer, a closed socket, a
cancelled run or any error is I<no>; it never allows by itself. Without the
option (and without a terminal) such a call is refused. Not shown by
C<--help>; ignored in the REPL (C<-i>).

=head2 --dangerously-skip-permissions

The same as C<--permission-mode bypassPermissions>: every tool call runs
unless a deny rule names it. Not together with C<--permission-mode>.

=head2 --customize-prompt

Start the REPL with the prompt-builder, which writes F<.raider.md> with
you (C</prompt> in the REPL).

=head2 --export-skill [PATH]

Write a plain-markdown "how to use raider" document for the current
configuration (default F<RAIDER-SKILL.md> in C<-r>) and exit.

=head2 --export-claude-skill [PATH]

Write the same as a Claude Code F<SKILL.md> with frontmatter (default
F<.claude/skills/raider/SKILL.md> in C<-r>) and exit.

=head2 --version

Print the version of raider (L<Langertha::Raider>) and of L<Langertha>
core on one line, C<raider VERSION (Langertha VERSION)>, and exit.

=head2 -h, --help

Print the option summary and exit.

=head1 MACHINE OUTPUT

C<--json>, C<--msgpack> and C<--yaml> print one document for the run, once,
when it has ended. C<--stream-json>, C<--stream-msgpack> and
C<--stream-yaml> print events while the run happens, the last of which
carries that same document (see L</Streams>). Nothing else goes to standard
output: the live trace is off unless C<--trace> asks for it, and then goes
to standard error like every diagnostic. The six flags exclude each other;
giving two is a usage error. A usage or configuration error (exit status
C<2> or C<3>) prints no document and no events, only its message on
standard error.

The flags write the same model in three encodings:

=over

=item C<--json> -- a pretty-printed JSON object with sorted keys, UTF-8.
C<--stream-json> writes JSON Lines: one compact object per event and line.

=item C<--msgpack> -- one MessagePack map; text is written as UTF-8 C<str>,
never C<bin>. Standard output is binary. C<--stream-msgpack> writes one map
per event, back to back (MessagePack objects delimit themselves).

=item C<--yaml> -- one YAML document, starting with C<--->, UTF-8.
C<--stream-yaml> writes one YAML document per event, each starting with
C<--->.

=back

Every field and type is the same in all three encodings.

=head2 The document

A run that completed:

    {
       "elapsed" : 2.731,
       "metrics" : { "iterations" : 2, "raids" : 1, "time_ms" : 2710.4, "tool_calls" : 1 },
       "response" : "The README describes ...",
       "status" : "completed",
       "version" : 1
    }

A run that failed:

    { "elapsed" : 0.42, "error" : "...", "reason" : "engine_error", "status" : "failed", "version" : 1 }

A run cancelled by C<SIGINT> (Ctrl-C; see L</EXIT STATUS>):

    { "elapsed" : 3.912, "reason" : "signal", "status" : "cancelled", "version" : 1 }

A run stopped by C<SIGTERM>, by a second C<SIGINT> while it is being
cancelled, or by either signal before it started:

    { "elapsed" : 5.107, "reason" : "signal", "signal" : "TERM", "status" : "interrupted", "version" : 1 }

=over

=item C<version> -- integer, the format version (see below).

=item C<status> -- how the run ended: C<completed>, C<failed>,
C<cancelled> or C<interrupted>. These are the run states of the redesign
(ADR 0009) that end a run and that the CLI can produce today; C<refused>
is reserved for when it can.

=item C<response> -- the agent's final answer (C<completed> only).

=item C<metrics> -- the raider's cumulative metrics: C<raids>,
C<iterations>, C<tool_calls>, C<time_ms>, and C<usage> -- the token counts of
every model call so far, subagents and C<web_fetch> prompts included:
C<input_tokens>, C<output_tokens>, C<cache_read_tokens>, C<cache_write_tokens>
(both cache counts are part of the input), C<calls>, and the same per model
under C<models> (C<completed> only; ADR 0026).

=item C<reason> -- a short code for why the run ended, whenever C<status>
is not C<completed> (ADR 0034); C<error> stays the text for a person. New
codes may come within a version, so a consumer must treat one it does not
know like C<error>:

=over

=item C<max_iterations> -- the raid used up C<--max-iterations> (C<failed>).

=item C<engine_error> -- a model request failed: the provider could not be
reached, answered with an error or with something raider cannot read
(C<failed>).

=item C<hook_blocked> -- a C<UserPromptSubmit> hook blocked the prompt
(C<failed>).

=item C<hook_stopped> -- a hook answered C<continue: false>: a
C<SessionStart> or C<UserPromptSubmit> hook (C<failed>), a tool hook
(C<cancelled>).

=item C<run_setup> -- the run could not be set up, for example the engine
for the model of a C</name> command (C<failed>).

=item C<provider> -- the C<--provider> could not be activated (C<failed> or
C<refused>).

=item C<user_cancel> -- cancelled by the person or the client: the REPL's
Ctrl-C, an ACP C<session/cancel> (C<cancelled>).

=item C<signal> -- ended by a signal: the first C<SIGINT> of a one-shot run
(C<cancelled>), C<SIGTERM> or a second C<SIGINT> (C<interrupted>, see
C<signal>).

=item C<error> -- any other failure (C<failed>).

=item C<refused> -- a C<refused> document that names no other code.

=back

=item C<error> -- the error message (C<failed> only).

=item C<signal> -- the signal that stopped the run, C<INT> or C<TERM>
(C<interrupted> only).

=item C<elapsed> -- seconds the run took, to the millisecond.

=item C<session> -- the session the run is recorded in (see
L</SESSIONS>): C<id> and C<path> of its journal. Missing with
C<--no-session> and for a signal during startup.

=back

=head2 Streams

A stream is a sequence of events, each flushed as soon as it happens. Every
event has these fields:

=over

=item C<version> -- the format version, as in the document.

=item C<type> -- what happened (below).

=item C<seq> -- 1, 2, 3, ... within the run.

=item C<time> -- when, in epoch seconds with fractions.

=back

The types of version 1, with the fields each adds:

=over

=item C<run.started> -- the run begins: C<engine> and, when there is one,
C<model>; C<session> (the session id) and C<run> (the run id within it,
C<r1>, C<r2>, ...), both missing with C<--no-session>; C<cwd>, the working
directory (inside a worktree, the worktree); C<permission_mode>; C<tools>,
the names of the tools the model is offered, in the order it gets them --
missing while a configured MCP server has not connected yet, since those
start with the run's first model request (see C<mcp.server>); and
C<raider_version>. A run interrupted during startup (reading the
configuration or the prompt) has none: its stream is the C<interrupted>
state change and C<run.finished>.

=item C<run.state> -- C<state>: C<running> when the run starts, then the
C<status> it ends with (see L</The document>).

=item C<tool.call> -- C<name> and C<arguments> of a tool call, before it
runs; C<call>, its id within the run (C<c1>, C<c2>, ...); C<status>,
C<dispatched>.

=item C<tool.result> -- C<call> and C<name> of the call; C<status>
(C<succeeded>, C<failed> when the tool reported an error, or C<cancelled>
for a call a cancelled run cut off); C<ok> (the
same as a boolean); C<size>, the length of the result text in characters;
C<content>, its first 1000 characters; and C<truncated> (a boolean, true
when C<content> was cut). The cut limits how much of a tool's output
reaches the stream, but it is no filter: a secret in a tool's arguments or
output can still show up.

=item C<message> -- a finished assistant message: C<role> (C<assistant>)
and C<content>. The final answer of the run; also an answer a C<Stop> hook
sent back with feedback (the run goes on), and an answer after which the
run goes on with the notifications of its background agents. The person's
own input is never a C<message> of the stream.

=item C<model.usage> -- after every model request that reports tokens, and
after a tool's own model call: C<purpose> (C<chat>, or C<web_fetch>),
C<model>, C<input_tokens>, C<output_tokens> and, when the provider reports
them, C<cache_read_tokens> and C<cache_write_tokens> (both part of
C<input_tokens>).

=item C<todo.updated> -- the model changed its task list: C<todos>, the
whole list, each item with C<content>, C<status> and the fields of its
tool (see L</TOOLS>).

=item C<permission.asked> -- the policy asks about a tool call (see
L</PERMISSIONS>): C<call>, C<tool>, C<tool_source>, C<mode>, C<reason>,
the C<rule> and its C<source> when a rule asks, C<suggested_rule> when one
would allow it. Also when C<exit_plan_mode> puts the plan to the person
(C<reason> C<review of the plan>).

=item C<permission.granted> -- the person allowed the call: C<call>,
C<tool>, C<scope> (C<once>, or C<session> with the C<rule> added), C<mode>.

=item C<permission.denied> -- the call does not run: C<call>, C<tool>,
C<tool_source>, C<mode>, C<reason>, C<rule> and C<source> of a deny rule,
and C<by> -- who refused it: C<rule>, C<mode>, C<hook>, C<worktree>, C<user>,
or C<noninteractive> when nobody could be asked.

=item C<permission.mode> -- the mode changed: C<from>, C<to>, C<why> (the
command or tool that changed it, such as C<enter_plan_mode> or
C</permissions>), and C<call> when a tool call changed it.

=item C<hook.ran> -- one hook of the project's Claude Code hooks ran or was
skipped: C<hook_event> (C<PreToolUse>, C<Stop>, ...), C<source> (the
settings file or C<plugin:NAME>), C<command>, C<outcome> (C<success>,
C<block>, C<error>, C<timeout>, C<skipped>, C<running> for an async hook,
C<cancelled>), and when known C<plugin>, C<matcher>, C<args>, C<exit>,
C<duration_ms>, C<message>, C<stdout> and C<stderr> (each cut at 2000
characters). A tool hook (C<PreToolUse>, C<PostToolUse>,
C<PostToolUseFailure>) carries the C<call> it ran for.

=item C<hook.message> -- what hooks say to the person: C<hook_event>,
C<kind> (C<system> for a C<systemMessage>, C<error>, C<stopped> for a
C<continue: false>) and C<text>.

=item C<mcp.server> -- a configured MCP server changed its state:
C<name>, C<scope>, C<transport>, C<status> (C<starting>, C<connected>,
C<failed>, C<stopped>), C<reason> of a failure, C<tools> (how many tools it
offers). The servers start with the run's first model request.

=item C<mcp.tools_changed> -- a server announced a new tool list: C<name>,
C<scope>. The next model request offers the new tools.

=item C<agent.started> -- the C<agent> tool started a subagent: C<call>
(the C<agent> call), C<agent>, C<agent_id>, C<background>,
C<description>, C<session> (its own journal), C<model>, and C<resumed> for
a continued agent.

=item C<agent.finished> -- the subagent ended: C<call>, C<agent>,
C<agent_id>, C<session>, C<status>.

=item C<agent.warning> -- a subagent starts with a problem worth a look,
such as a briefing over 64 kB of skill text: C<call>, C<agent>, C<message>.
Before its C<agent.started>.

=item C<worktree.entered> -- the session moved into a git worktree:
C<path>, C<branch>, C<created> (whether C<enter_worktree> created it).

=item C<worktree.exited> -- it left it: C<path>, C<branch>, C<removed>,
C<reason> (C<exit_worktree>, C<clear>, C<session_end>).

=item C<run.finished> -- always the last event, also when the run failed.
Its fields are exactly the document of L</The document>; a consumer that
only wants the result reads the last event.

=back

C<call> is the id of a tool call within the raid (C<c1>, C<c2>, ...): the
events of one call share it, also when calls run at the same time.

A run that calls one tool, as C<--stream-json>:

    {"cwd":"/src/app","engine":"openai","model":"gpt-4o-mini","permission_mode":"default","raider_version":"0.504","run":"r1","seq":1,"session":"20261025-091500-3f2a","time":1790000000.1,"tools":["list_files","read_file","bash"],"type":"run.started","version":1}
    {"seq":2,"state":"running","time":1790000000.1,"type":"run.state","version":1}
    {"arguments":{"command":"ls"},"call":"c1","name":"bash","seq":3,"status":"dispatched","time":1790000001.2,"type":"tool.call","version":1}
    {"call":"c1","content":"...","name":"bash","ok":true,"seq":4,"size":42,"status":"succeeded","time":1790000001.3,"truncated":false,"type":"tool.result","version":1}
    {"input_tokens":1200,"model":"gpt-4o-mini","output_tokens":40,"purpose":"chat","seq":5,"time":1790000002.4,"type":"model.usage","version":1}
    {"content":"There are ...","role":"assistant","seq":6,"time":1790000002.4,"type":"message","version":1}
    {"seq":7,"state":"completed","time":1790000002.4,"type":"run.state","version":1}
    {"elapsed":2.312,"metrics":{...},"response":"There are ...","seq":8,"session":{...},"status":"completed","time":1790000002.4,"type":"run.finished","version":1}

Token deltas are not part of version 1. New event types may appear within a
version, so a consumer must skip types it does not know.

=head2 Versions

C<--json=N> (C<--msgpack=N>, C<--yaml=N>, and the C<--stream-*> flags) asks for format version C<N>; the
version belongs to the model, so it means the same in every encoding and
for documents and streams alike. The
only version is C<1>, which is also what the plain flags write; any other
C<N> is a usage error. Only the C<=N> form carries a version -- in
C<raider --json 2 ...> the C<2> is part of the prompt.

Within a version, fields are only ever added, never renamed, removed or
changed in meaning, so a consumer must ignore fields it does not know. A
breaking change is a new version, chosen with C<--json=2>; the plain flag
stays on the old version for at least one release after the new one ships.

=head1 SESSIONS

Every run is recorded in a session journal (ADR 0015):
F<.raider/sessions/E<lt>idE<gt>.jsonl> under the working directory
(C<-r>), one JSON object per line. A one-shot run starts a new session and
names it on standard error (with a machine output flag, in the document's
C<session> instead); the REPL starts one with its first prompt and records
every prompt of it there. C<--no-session> records nothing.

Creating F<.raider/> also writes F<.raider/.gitignore> excluding
C<sessions/> and C<lib/>, unless that file exists: a journal holds the
whole output of every tool, so it is not committed by default. API keys
never enter it.

A run is recorded as C<run.started>, the input as C<message>, every
C<tool.call> and C<tool.result> (with the whole result text, where the
stream cuts it), the answer as C<message>, and C<run.finished> with how it
ended -- also when it failed or was interrupted. The permission policy's
decisions are recorded as C<permission.asked>, C<permission.granted>,
C<permission.denied> and C<permission.mode>. A C</clear> in the REPL
is recorded as C<history.cleared>. While a raider writes a
session it holds a lock on it; a second writer fails at once instead of
waiting. A journal that cannot be written in the middle of a run (a full
disk) is a warning on standard error; the run goes on.

Wherever a command takes a session C<ID>, a shorter form names it too: a
unique start of the id (C<20260925-08>) or its four hex digits at the end
(C<3f2a>). A form that fits more than one session is refused with the
candidates listed.

    raider session list                  # the project's sessions, newest first
    raider session show ID               # one session, event by event
    raider session show ID --json        # the whole journal as one document
    raider session resume ID             # the REPL, continuing session ID
    raider --session ID "and now ..."    # one more run in session ID
    raider --continue "and now ..."      # the same in the newest session
    raider -i --continue                 # the REPL on the newest session
    raider session fork ID               # a new session with ID's history
    raider session rm ID                 # delete session ID

C<session list> shows id, number of runs, the state of the last run and
the first prompt of each session; C<session show> prints every event, then
what the crash rules found. Both read without the lock, take C<-r> for the
project and a document format (C<--json>, C<--msgpack>, C<--yaml>), as do
C<session fork> and C<session rm>. As for
C<config explain>, the options may come first; there only
C<session list> and C<session show|resume|fork|rm> followed by a session id
are the subcommand, any other prompt starting with C<session> is sent as a
prompt.

C<session fork ID> starts a new session in the same project whose
C<session.created> names the original in C<forked_from>, and copies what a
resume would replay as the conversation into it, as C<message> events
outside any run. The original is only read, not locked or changed; the
fork has its own journal from then on and is continued like any session
(C<session resume>, C<--session>). C<session rm ID> deletes the journal
and its lock file; it takes the lock first, so a session another raider
has open is refused with exit status C<4>. Raider never deletes a session
on its own.

Continuing a session (C<--session ID>, C<--continue>, C<session resume>)
takes its lock, replays the conversation -- the input and answer of every
run that got an answer, and all recorded events into the full session
history; after a C<history.cleared> only what came later -- and records
the new runs in the same journal. Nothing recorded
is executed again. It reports what it found on standard error (in the REPL,
under the banner): lines that could not be read, runs that never ended
(they count as C<interrupted>), and tool calls without a result, whose
outcome is C<unknown>. The context (F<.raider.md>, packs, configuration) is
built fresh, not replayed. A session another raider has open ends F<raider>
with exit status C<4>.

=head1 PROVIDERS

    raider provider inspect HOST[:PORT] | https://HOST[:PORT] [--json] [--allow-internal]
    raider provider add ALIAS HOST[:PORT] | https://HOST[:PORT] [--credential-env NAME] [--allow-internal]
    raider provider list [--json]
    raider provider show ALIAS [--json]
    raider provider remove ALIAS [--yes]

C<provider inspect> fetches C<https://HOST[:PORT]/.well-known/langertha.json>, the provider
manifest of ADR 0007, validates it with L<Langertha::Manifest> and shows
what it declares: the provider id, the issuer, every endpoint (id,
dialect, base URL, auth reference), every auth entry (id, type) and every
model (id, endpoint, the capabilities it claims). Warnings follow for what
this raider cannot use -- an endpoint dialect it has no adapter for, an
auth type it cannot supply, a capability name Langertha does not know
(treated as absent) -- and for an issuer that is not the origin the
manifest came from or an endpoint on plain C<http> or an internal address.
C<extensions> are shown as inert: kept as published, never interpreted,
loaded or run. A manifest with a command-, code-, secret- or prompt-shaped
field (C<command>, C<exec>, C<api_key>, C<system_prompt>, ...), an unknown
field or another schema version is not valid.

Inspecting stores nothing, binds no credential and trusts nothing: the
manifest states what the provider claims. The fetch sends no API key, no
C<Authorization> header and no cookie, and is bounded:

=over

=item * C<https> only; a target with userinfo, a query, a fragment or
another path than the well-known one is a usage error. Without
C<https://> a host needs a dot (C<host.tld>), a port (C<host:PORT>) or
brackets (an IPv6 literal such as C<[::1]>, with or without a port); a
bare name such as C<acme> or C<localhost> is a provider alias, not a host
(C<localhost:8443> or C<https://localhost> is one).

=item * at most 1 MiB of body, 10 seconds from resolving the host to the
last byte, and 3 redirects, each within the origin (scheme, host and
port). A redirect to another origin is not followed; it is reported with
its target and the manifest counts as C<refused>.

=item * the host is resolved once and every address it resolves to is
checked; the request goes to a checked address, never to a second lookup
of the name, while TLS still verifies the certificate against the name.
Loopback, private (RFC 1918, shared address space, IPv6 unique local),
link-local and reserved addresses are refused, unless C<--allow-internal>
releases them. Cloud metadata addresses (C<169.254.169.254>,
C<fd00:ec2::254>, ...), the unspecified address, multicast and
C<240.0.0.0/4> are refused always. A host with one refused address among
its addresses is refused.

=back

C<--allow-internal> releases loopback, private, link-local and reserved
target addresses for this one inspection -- for a knarr or skeid you
deliberately run on an internal address. Nothing remembers it.

=head2 Using a provider

    raider --provider HOST[:PORT] | https://HOST[:PORT] [-m MODEL] [-k KEY] [--allow-internal] [options] [prompt...]

fetches and validates the manifest as C<provider inspect> does (same
limits and address rules) and runs on the endpoint of one of its models:
one-shot, machine output and the REPL work as with C<-e>. Nothing is
stored and nothing is remembered: naming the host on the command line is
the whole release, as with C<-o url=>. A name without dot, port or
C<https://> is an alias (see L</Stored aliases>), never a host. The rules are
provisional:

=over

=item * B<The model.> C<-m> (or C<-o model=>) must be a model id of the
manifest. Without it the manifest must list exactly one model id;
several are a usage error that lists them. C<model:> in F<.raider.yml>
does not apply. A model listed on several endpoints runs on the one whose
dialect raider has an adapter for; with more than one such endpoint raider
does not choose and the run is refused.

=item * B<The engine> follows from the endpoint's dialect:
C<openai-chat> runs as C<openai>, C<anthropic> as C<anthropic>, C<gemini>
as C<gemini>, C<ollama> as C<ollama> (native API), C<responses>,
C<perplexity-agent>, C<anthropic-compat> and C<aki> on their own
Langertha engines -- C<anthropic-compat> without native structured output,
through a synthetic tool and a forced tool choice instead (ADR 0007). The
engine's C<url> is the endpoint's C<base_url>, whatever F<.raider.yml>
says; the other engine options of F<.raider.yml> and C<-o> apply as with
C<-e>. A dialect this raider has no adapter for is an error, and so is
C<lmstudio>: Langertha's native LM Studio engine has no tool calling.

=item * B<The key> comes only from C<-k> or C<-o api_key=>. Neither
C<api_key:> in F<.raider.yml> nor any environment variable is used: a key
for another provider never reaches this one. An endpoint with an auth
reference needs a key (a usage error without one); an auth type other than
C<api_key> is an error. An endpoint without auth gets no key.

=item * B<The origin.> The endpoint's C<base_url> must be C<https> and of
the same origin (scheme, host, port) as the manifest; anything else is
refused, so the key goes nowhere but the origin named on the command line.
The endpoint's host is resolved and checked again before the run, under
the rules of C<provider inspect> (C<--allow-internal> releases it the same
way). The engine then connects to the first of the checked addresses,
never to a new lookup of the name (TLS still verifies the certificate
against the host name), so a DNS answer that changes after the check
cannot send the key elsewhere. A redirect to another host is never
followed; the model requests are POSTs, which are not redirected at all,
and the engine's synchronous requests (the REPL's C</model>) follow no
redirect.

=item * A model that does not declare C<tools_native> is a warning on
stderr, not an error: raider works through tool calls.

=back

=head2 Several endpoints and failures

A model that the manifest lists on several endpoints raider can use is
refused, not guessed: the error names the endpoints and C<--provider-endpoint
ID> chooses one (also for an alias).

Failures are reported on stderr before anything runs: a usage error exits
with C<2>, a provider that cannot be used as it is (refused, not fetched,
not valid, an unknown dialect or auth type) with C<1>. With a machine
output flag the same failure is also one document on stdout, C<status>
C<refused> (an address, redirect, origin or stored binding the rules do not
allow) or C<failed>, with C<error> and the C<provider> as given; the exit
status is the same with and without the flag.

=head2 Stored aliases

    raider provider add ALIAS HOST[:PORT] | https://HOST[:PORT] [--credential-env NAME] [--allow-internal]
    raider --provider ALIAS [-m MODEL] [-k KEY] [options] [prompt...]

C<provider add> fetches and validates the manifest as C<provider inspect>
does, shows its endpoints and auth and what will be pinned, and asks
C<[y/N]> on a terminal. Anything but C<y> stores nothing, and without a
terminal (stdin or stdout not a TTY) nothing is fetched or stored at all.
The binding goes under C<providers:> in F<~/.raider/config.yml> through the
one home-file writer (a C<providers:> in a project file is ignored):

    providers:
      acme:
        origin: https://provider.example:443
        issuer: https://provider.example
        auth: [api_key]
        credential_env: ACME_API_KEY
        allow_internal: 0
        pinned_at: 2026-10-15T09:30:00Z
        fingerprint: 3f2a...

C<fingerprint> is a field beyond the origin, issuer and auth types: the
digest of what was accepted (below), which is what C<--provider ALIAS>
compares, so an endpoint that moved is seen too.

=over

=item * B<The credential> is the I<name> of an environment variable
(C<--credential-env>), required when the endpoints ask for auth and never
guessed; its value is read at run time and goes to the pinned origin only.
C<-k> overrides it for one run. No other C<*_API_KEY> ever selects or
feeds an alias. The name is pinned too: another variable (edited by hand,
or given to C<provider add> again) is another secret sent to the same
origin, so it needs the renewed acceptance of C<provider add>; until then
C<--provider ALIAS> stops.

=item * B<What is pinned> is the origin, the issuer and the auth types, the
credential variable and C<allow_internal>, and through the C<fingerprint>
every endpoint's origin and dialect and the provider id (no TLS pin). A change of the model list,
capabilities or an endpoint path needs nothing. Anything pinned that changed
-- or an entry edited by hand, or without a fingerprint -- stops
C<--provider ALIAS> (exit C<1>, or C<3> for an entry that is not
well-formed), naming what differs, until C<provider add> is run again and
accepted.

=item * B<C<--allow-internal>> belongs to the alias: given to C<add> it is
stored and covered by the pin; it is a usage error next to a stored alias.

=item * B<The name> of an alias is lowercase letters, digits, C<-> and
C<_>, at most 32: it can never look like a host. A C<--provider> value
without dot, port or C<https://> is always an alias; one that is not stored
is a usage error (exit C<2>; with C<--json> the C<failed> document) that
points to C<provider add>, and nothing is fetched -- a typo in an alias
never turns into a network request. Any other C<--provider> value is a
HOST.

=item * B<C<provider list>> and B<C<provider show ALIAS>> print what is
stored (origin, issuer, auth types, the I<name> of the credential variable,
whether an internal address is allowed, when it was accepted; C<show> also
the fingerprint) and fetch nothing; an entry that cannot be used says why.
C<--json> gives one document (C<providers>, or C<provider>). The aliases
are also in C<raider config explain> and the REPL banner.

=item * B<C<provider remove ALIAS>> deletes the entry through the same
home-file writer, after asking on a terminal. Without a terminal it needs
C<--yes> and is an error otherwise; nothing is removed then.

=back

=head2 The provider document

C<--json>, C<--msgpack> and C<--yaml> write one document (no stream):

    {
       "address" : "93.184.216.34",
       "elapsed" : 0.214,
       "final_url" : "https://provider.example/.well-known/langertha.json",
       "manifest" : { "schema_version" : 1, "kind" : "langertha-provider", ... },
       "notes" : [],
       "redirects" : [],
       "status" : "completed",
       "url" : "https://provider.example/.well-known/langertha.json",
       "version" : 1,
       "warnings" : []
    }

C<status> is C<completed> for a valid manifest, C<refused> for a target
address or redirect the rules above do not allow, and C<failed> for
anything else: the network, TLS, an HTTP status, a limit, invalid JSON or
an invalid manifest. C<manifest> is the validated manifest as
L<Langertha::Manifest/to_hash> writes it; C<warnings> and C<notes> are
one sentence each; C<url> is the manifest URL, C<final_url> the one the
body came from after C<redirects>, C<address> the address connected to.
C<refused> and C<failed> carry C<error> instead of C<manifest>, and a
redirect that was not followed its target in C<location>. The version
and compatibility rules are those of L</Versions>.

=head1 EXIT STATUS

=over

=item C<0> -- success, including C<--help>, C<--version>, the exports, C<config explain>,
C<config migrate> (also with nothing to migrate, and C<--dry-run>), C<session list>, C<session show>, C<session fork>, C<session rm>,
C<provider inspect> of a valid manifest, C<provider add> that stored its
binding, C<provider list>, C<provider show> and C<provider remove> that
removed its alias, and leaving the REPL.

=item C<1> -- the run failed: the engine, a tool or the network raised an
error. With a machine output flag, the document says C<failed>. For
C<config migrate>: the migration was refused, also with C<--dry-run>, or
a step failed. For C<provider inspect>: the manifest was refused, could not be fetched or is
not valid (the document says C<refused> or C<failed>). For C<--provider>
also: its endpoint cannot be used (another origin, not C<https>, a refused
address, a dialect or auth type this raider has no adapter for, no
models, a model on several endpoints) or a stored alias no longer matches
what was accepted; see L</Using a provider> and L</Stored aliases>. For
C<provider add>: no terminal, an answer other than C<y>, a manifest that
was refused, not fetched or not valid. For C<provider remove>: no terminal
and no C<--yes>, or an answer other than C<y>.

=item C<2> -- usage error: unknown option, a C<-o> that is not
C<KEY=VALUE>, an unknown C<config> subcommand, no prompt, more than one
machine output flag, a machine output flag with C<-i>, or an unknown format
version; for C<config migrate> a word after it or a machine output flag;
for C<provider inspect> also no target or more than one, a
target that is no host or C<https> origin, or a C<--stream-*> flag; for
C<--provider> a target that is none, C<-e>, C<-o engine=>, C<-o url=> or
C<config explain> with it, a model the manifest does not list, no C<-m>
where it lists several, or no C<-k> (nor the alias's variable) where the
endpoint needs a key; for C<provider add> not two words, an alias or
variable name that is none, auth asked for without C<--credential-env> or
a document format; for C<provider show> and C<provider remove> not one
word or an alias that is not stored; and C<--allow-internal> or
C<--provider-endpoint> without C<--provider>, or C<--allow-internal> with a
stored alias.

=item C<3> -- configuration error: the config file (project or home; for
C<config migrate> the F<.raider.yml> to migrate) cannot be read, the
engine is unknown, or a pack detection rule is invalid.

=item C<4> -- the session to continue (C<--session>, C<--continue>,
C<session resume>) or to remove (C<session rm>) is in use by another
raider. A usage error (C<2>) is also an unknown or ambiguous session, a
C<--session> that is no session id, C<--continue> without any session, or
more than one of C<--session>, C<--continue> and C<--no-session>.

=item C<130>, C<143> -- a one-shot run was cancelled by C<SIGINT> or
interrupted by C<SIGTERM>. On C<SIGINT> (Ctrl-C) F<raider> cancels the run:
it ends the tool commands still running, abandons a model request in
flight, lets the run end at its next safe point and writes its document
-- C<cancelled>, or what the run reached if it ended anyway -- or its
events, or a note. A second C<SIGINT> while it cancels, a C<SIGTERM>, and
with a machine format either signal during startup end the tool commands
and write the C<interrupted> document at once. Either way F<raider> then
dies of that same signal, so a shell reports 128 plus the signal number
and a parent that waits for it sees a process killed by the signal. A
model or embedding request that blocks the process (not an asynchronous
one) holds a cancel up until it returns; the second C<SIGINT> does not
wait.

=back

The C<hall> and C<acp> subcommands keep their own exit statuses.

=head1 ENVIRONMENT

API keys come from the environment variables below only. Claude Code
subscription/OAuth credentials are not consumed by this CLI; Anthropic
usage goes through the API key path.

=head2 ANTHROPIC_API_KEY

The key of the C<anthropic> engine. Set, it also makes C<anthropic> the
engine when nothing names one; so do the next five, in this order.

=head2 OPENAI_API_KEY

The key of C<openai>.

=head2 DEEPSEEK_API_KEY

The key of C<deepseek>.

=head2 GROQ_API_KEY

The key of C<groq>.

=head2 MISTRAL_API_KEY

The key of C<mistral>.

=head2 GEMINI_API_KEY

The key of C<gemini>.

=head2 MINIMAX_API_KEY

The key of C<minimax>. This one and the next two are only read for their
engine; they never pick it.

=head2 CEREBRAS_API_KEY

The key of C<cerebras>.

=head2 OPENROUTER_API_KEY

The key of C<openrouter>.

=head2 BRAVE_API_KEY

Adds Brave to C<web_search>, next to the keyless DuckDuckGo.

=head2 SERPER_API_KEY

Adds Serper to C<web_search>.

=head2 GOOGLE_API_KEY

With L</GOOGLE_CSE_ID>, adds Google Custom Search to C<web_search>.

=head2 GOOGLE_CSE_ID

The Custom Search engine id for L</GOOGLE_API_KEY>.

=head2 RAIDER_PACK_DIRS

Colon-separated directories searched for packs after the project's
F<.raider/packs/>, F<~/.raider/packs/> and the bundled ones; see
L<Langertha::Raider::Packs/build_packs>.

=head2 RAIDER_ENABLE_TASKS

C<0> (or C<false>, C<no>, C<off>) mounts C<todo_write> instead of the task
tools C<task_create>, C<task_update>, C<task_list> and C<task_get>, like
Claude Code's C<CLAUDE_CODE_ENABLE_TASKS>; see
L<Langertha::Raider::Application/task_tools>.

=head2 HOME

Where F<~/.raider/packs/> and the REPL history F<~/.raider_history> are.

=head2 ANSI_COLORS_DISABLED

Set, the output and the trace have no colors (C<--no-color> and the
machine output flags set it themselves).

=head2 RAIDER_HALL_SOCKET

The control socket of the Hall that started this raider. When it names a
socket, the Hall tools C<telegram_reply>, C<hall_status> and C<hall_spawn>
are mounted; the Hall sets it, together with C<RAIDER_HALL_TELEGRAM_BOT>,
C<RAIDER_HALL_TELEGRAM_CHAT_ID> and C<RAIDER_HALL_TELEGRAM_THREAD_ID> for a
Telegram mission (see L<Langertha::Raider::HallTools>).

=head1 SEE ALSO

=over

=item * L<Langertha::Raider::CLI>

=item * L<Langertha::Raider>

=item * L<raider-hall> -- the Hall daemon, also reachable as C<raider hall>

=back

=head1 SUPPORT

=head2 Issues

Please report bugs and feature requests at
L<https://github.com/Getty/langertha-raider/issues>.

=head2 IRC

Join C<#langertha> on C<irc.perl.org> or message Getty directly.

=head1 CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

=head1 AUTHOR

Torsten Raudssus <torsten@raudssus.de> L<https://raudssus.de/>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

L<https://raudssus.de/>

=cut
