For pseudocode conventions, see the README.

BashTool::execute (src/tools/bash.rs)

Purpose: Execute a shell command, capture output, enforce safety. Preconditions: params.command is present. Postconditions: Returns Ok(ToolResult) even for non-zero exit codes (LLM needs the error to self-correct).

FUNCTION BashTool::execute(params: JSON, ctx: ToolContext) -> Result<ToolResult, ToolError>

  command ← params["command"] as String  // InvalidArgs if missing
  cancel ← ctx.cancel

  // Safety: check deny patterns (substring match)
  FOR EACH pattern IN self.deny_patterns
    IF command contains pattern THEN
      RETURN Err(Failed("Command blocked by safety policy: contains '{pattern}'"))
    END IF
  END FOR

  // Optional confirmation callback
  IF self.confirm_fn defined AND NOT self.confirm_fn(command) THEN
    RETURN Err(Failed("Command was not confirmed by the user."))
  END IF

  // Build subprocess: bash -c "{command}"
  cmd ← Command("bash", ["-c", command])
  IF self.cwd defined THEN cmd.current_dir(self.cwd) END IF
  cmd.stdout(piped), cmd.stderr(piped)

  // Race: cancellation vs timeout vs command completion
  result ← SELECT {
    cancel.cancelled()          → RETURN Err(Cancelled)
    sleep(self.timeout)         → RETURN Err(Failed("Command timed out after {N}s"))
    cmd.output()                → result  // may be Err if spawn failed
  }

  output ← result  // Err(io) → Err(Failed("Failed to execute: {e}"))

  stdout ← output.stdout as utf8 (lossy)
  stderr ← output.stderr as utf8 (lossy)

  // Truncate at limit
  IF stdout.len > self.max_output_bytes THEN
    stdout ← stdout[0..max_output_bytes] + "\n... (output truncated)"
  END IF
  IF stderr.len > self.max_output_bytes THEN
    stderr ← stderr[0..max_output_bytes] + "\n... (output truncated)"
  END IF

  exit_code ← output.exit_code OR -1

  text ←
    IF stderr is empty THEN
      "Exit code: {exit_code}\n{stdout}"
    ELSE
      "Exit code: {exit_code}\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}"
    END IF

  // Always Ok — non-zero exit is NOT a ToolError
  RETURN Ok(ToolResult {
    content: [Text(text)],
    details: { exit_code, success: exit_code == 0 }
  })

END FUNCTION