NAME
Langertha::Raider::BashTools - Internal shell tools of raider: bash, bash_output, kill_shell and monitor
VERSION
version 0.504
SYNOPSIS
my $shell = Langertha::Raider::BashTools->new(
loop => $app->loop,
working_directory => $root,
validator => $loadguard->validator,
);
my $server = $shell->server; # MCP::Server with bash, bash_output, kill_shell
DESCRIPTION
Internal module. Its interface may change without notice.
The shell tools of raider, shaped after Claude Code's Bash, BashOutput and KillShell (Langertha::Raider::ToolNames, ADR 0024).
bash runs a command with bash -c in "working_directory" (or the working_directory argument), in a process group of its own, with standard input from /dev/null and standard output and standard error into one file, so the result is the combined output in the order it was written, as in Claude Code. A command that exits with 0 answers with its output, or (Bash completed with no output); any other exit status is an error result that starts with Exit code N. Output over "output_limit" characters keeps its start and its end around ... [N characters truncated] ....
A command still running after its timeout (seconds, default "default_timeout", at most "max_timeout") is not killed: it is moved to the background, as Claude Code does, and the result says so with its ID. run_in_background starts it there at once. A background shell is a child of raider supervised by the application's event loop ("watch_process" in IO::Async::Loop, ADR 0016); bash_output reads what it wrote since the last call, with its status, and kill_shell stops it. The background shells belong to this object, so to the application: they live across the runs of a session and are stopped when it goes away ("stop_all", DEMOLISH).
allowed_commands and validator (the loadguard gate, ADR 0018) are asked before any command starts, in the foreground or the background, with the texts MCP::Run used.
With "tasks" -- the application's registry of background work (ADR 0027) -- a background shell is registered there as bash_N, kind bash, and when it ends, on its own or stopped, a <task-notification> goes to the run that started it ("async_tools"); the texts of bash then say that it will be notified. bash_output and kill_shell also take the ID (or the name given at spawn) of any other entry of the registry -- a subagent: bash_output answers with its status and, once it has ended, its report; kill_shell asks it to stop ("stop" in Langertha::Raider::Tasks). This module does not know what a subagent is; the registry does.
With "tasks" it also has monitor (ADR 0031): a command in the background whose stdout lines are delivered as notifications (Langertha::Raider::Monitor), stopped with kill_shell like a shell. monitor is a shell tool: the gates of bash apply to its command.
loop
Required. The event loop that supervises the background shells -- the application's, the one the raid drives (ADR 0016).
working_directory
The directory commands run in unless a call names another. Without it, raider's own working directory.
allowed_commands
Optional. The first words a command may start with; any other command is refused with Command not allowed: WORD.
validator
Optional. Called as $validator->($command, $working_directory) before a command starts; 1 lets it run, anything else refuses it with Command REASON ("validator" in Langertha::Raider::Loadguard).
tasks
Optional. The application's Langertha::Raider::Tasks: background shells are registered and notify there, and bash_output and kill_shell reach its other entries (see "DESCRIPTION"). Without it a background shell is known only here and notifies nobody.
default_timeout
Seconds a command runs in the foreground when the call gives no timeout: 120, as in Claude Code.
max_timeout
The largest timeout of bash and bash_output, in seconds: 600, as in Claude Code. A larger one is cut down to it.
output_limit
Characters of output a result carries: 30000, as in Claude Code. Longer output keeps its first and its last half of it.
kill_grace
Seconds between SIGTERM and SIGKILL when a shell is stopped.
block_timeout
Seconds bash_output with block waits when the call gives no timeout.
monitor_timeout
Seconds a monitor watches when the call gives no timeout_ms: 300.
monitor_max_timeout
The longest a monitor may watch, in seconds: 1800 (30 minutes). A larger timeout_ms is cut down to it.
monitor_unattended_max_timeout
The same limit when "unattended" says there is nobody to talk to (a run with a single prompt, raider -p): 600 (10 minutes).
unattended
Optional. Called without arguments when a monitor starts: true when the run has no person at the keyboard (the application has no one to ask).
monitor_min_timeout
The shortest watch in seconds: 1, as the timeout_ms of Claude Code has a minimum of 1000. Tests lower it.
monitor_options
Extra arguments for every Langertha::Raider::Monitor (batch_window, burst_limit, ...). Tests use it to make the limits small.
server
The MCP::Server with the tools bash, bash_output and kill_shell, built once.
bash
my ( $text, $is_error ) = $shell->bash({ command => 'make test', timeout => 300 });
The bash tool: runs the command and returns the result text and whether it is an error result.
my ( $text, $is_error ) = $shell->bash( $in, { recipient => 'agent_2', call => 'c4', detached => 1 } );
The optional caller (see "async_tools") says who runs it: the recipient of the notification when the command ends in the background (default main), the call id the notification names as tool-use-id, and whether the run is detached -- its foreground command then belongs to the session ("background_pids"). Its working_directory is where a command without one runs instead of "working_directory" (a subagent in a worktree of its own, ADR 0032).
notification
my ( $text, $label ) = $shell->notification( $shell_hash, 'completed' );
The <task-notification> of a background shell that ended (ADR 0027 §4, in Claude Code's form for shells) and the line a surface shows for it. Status completed (exit code 0), failed (any other) or killed; summary Background command "DESCRIPTION" completed (exit code N), ... failed with exit code N or ... was stopped -- the command stands in for a missing description.
bash_output
my ( $text, $is_error ) = $shell->bash_output({ bash_id => 'bash_1', filter => 'FAIL', block => 1 });
The bash_output tool: the new output of a background shell, with its status and exit code:
<status>completed</status>
<exit_code>0</exit_code>
<stdout>
...
</stdout>
stdout carries both outputs and is left out when there is no new output. An unknown ID is an error result No task found with ID: ID.
Another entry of "tasks" (an agent, by ID or by the name given at spawn) answers with its status (running, completed, failed, killed) and, once it has ended, its report (its error, when it failed):
<status>completed</status>
<output>
REPORT
</output>
This synchronous method does not wait for an agent: an agent runs on the loop, which a blocking wait would stop. "bash_output_f" waits.
bash_output_f
my ( $text, $is_error ) = await $shell->bash_output_f({ bash_id => 'agent_1', block => 1 });
"bash_output" as a Future: block waits on the loop, so other work -- an agent, the shells -- goes on meanwhile, for a shell and for an agent. What "async_tools" runs.
kill_shell
my ( $text, $is_error ) = $shell->kill_shell({ shell_id => 'bash_1' });
The kill_shell tool: stops a running background shell ("kill_grace") and answers Successfully stopped task: ID (COMMAND). An unknown ID, or a shell that is not running, is an error result.
Another running entry of "tasks" (an agent, by ID or by the name given at spawn) is asked to stop ("stop" in Langertha::Raider::Tasks), and the answer is Successfully stopped task: agent_1 (DESCRIPTION); its run ends on the loop, and a background agent's notification then says it was stopped. One that is not running is the same error as a shell's.
monitor
my ( $text, $is_error ) = $shell->monitor(
{ description => 'errors in the log', command => 'tail -f app.log | grep --line-buffered ERROR', timeout_ms => 600000 },
{ recipient => 'agent_2', call => 'c4' },
);
The monitor tool: starts the command as a Langertha::Raider::Monitor (an entry of "tasks", monitor_N) and answers at once. The gates of bash ("allowed_commands", the "validator") are asked first and refuse with the same texts. timeout_ms is held to "monitor_min_timeout" and "monitor_max_timeout" ("monitor_unattended_max_timeout" without a person); the caller is as for "bash".
background_pids
The process IDs of the commands that belong to the session, not to the run in progress: the background shells still running, and the foreground command of a run that goes on without the person -- a background subagent, the detached caller of "bash" (ADR 0027 §3). A surface that stops the children of a cancelled run leaves these alone.
async_tools
my $tools = $shell->async_tools( recipient => 'main' );
my $tools = $shell->async_tools(
recipient => sub { $subagent->task_id },
detached => sub { $subagent->detached },
working_directory => sub { $subagent->working_root }, # optional
applies => sub { my ( $mcp ) = @_; ... }, # is $mcp a client of this server?
);
The three tools as "async_tools" in Langertha::Raider for the raider of one run. bash runs with that run as its caller ("bash"): the recipient of its background shells' notifications, whether it is detached and the working_directory its commands run in by default -- each a value or a code reference asked at the call -- and the call id from the call's context (call, else id). bash_output goes through "bash_output_f", so block lets the loop run; kill_shell is "kill_shell". Each handler returns a Future of an MCP result. applies goes into each tool as the applies of "async_tools" in Langertha::Raider, so a tool of these names from another source is left alone.
stop_all
$shell->stop_all;
Stops every shell still running ("kill_grace"). DEMOLISH does the same, so no shell outlives the application.
SEE ALSO
SUPPORT
Issues
Please report bugs and feature requests at https://github.com/Getty/langertha-raider/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/
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.