Skip to main content

A Claude Code clone built with Agno agents

Project description

aru

An intelligent coding assistant for the terminal, powered by LLMs and Agno agents.

๐Ÿ“– Full documentation: https://estevaofon.github.io/aru/

0329(3)

Highlights

  • Catalog-Driven Multi-Agent Architecture โ€” build, plan, executor, and explorer (subagent) specs resolved from a single source of truth (aru/agents/catalog.py)
  • Autonomous Plan Mode โ€” Agents self-trigger planning via enter_plan_mode(task); plan steps are persisted in the session and surfaced each turn as a PLAN ACTIVE reminder
  • Structured Subtask Tracking โ€” create_task_list / update_task / update_plan_step force the executor to plan, execute, and mark subtasks as it goes
  • Interactive CLI โ€” Streaming responses, multi-line paste, session management
  • Image Support โ€” Attach images via @ mentions for multimodal analysis (Claude, GPT-4o, Gemini)
  • 17 Integrated Tools โ€” File I/O (single + batched), code search, shell, web, delegation, plan/task tracking
  • Multi-Provider โ€” Anthropic, OpenAI, Ollama, Groq, OpenRouter, DeepSeek, and others via custom configuration
  • Custom Commands, Skills, and Agents โ€” Extend aru via the .agents/ directory
  • Custom Tools โ€” Add your own Python tools with a simple @tool decorator
  • Plugin System โ€” OpenCode-compatible hooks for tool lifecycle, chat, permissions, and more
  • MCP Support โ€” Integration with Model Context Protocol servers

Quick Start

1. Install

pip install aru-code

Requirements: Python 3.11+

2. Configure the API Key

Aru uses Claude Sonnet 4.6 from Anthropic as the default model. You need an Anthropic API key to get started.

Set your API key as an environment variable or create a .env file in your project directory:

ANTHROPIC_API_KEY=sk-ant-your-key-here

Using another provider? See the Models and Providers section to configure OpenAI, Ollama, Groq, etc.

3. Run

aru

That's it โ€” aru is available globally after install.

Usage

Commands

Command Description
Natural language Just type โ€” aru handles the rest
/plan <task> Creates a detailed implementation plan
/model [provider/model] Switch models and providers
/mcp List available MCP servers and tools
/commands List custom commands
/skills List available skills
/agents List custom agents
/sessions List recent sessions
/help Show all commands
! <command> Execute shell commands
/quit or /exit Exit aru

CLI Options

aru                                    # Start new session
aru --resume <id>                      # Resume session
aru --resume last                      # Resume last session
aru --list                             # List sessions
aru --dangerously-skip-permissions     # Skip permission prompts

Examples

aru> /plan create a REST API with FastAPI to manage users

aru> refactor the authentication module to use JWT tokens

aru> ! pytest tests/ -v

aru> /model ollama/codellama

Image Support

Attach images to your messages using the same @ mention syntax used for files. Aru detects image files by extension and sends them to the LLM as visual content for multimodal analysis.

Supported formats: .png, .jpg, .jpeg, .gif, .webp, .bmp

aru> describe @screenshot.png
aru> compare @before.png and @after.png
aru> review @code.py and explain the diagram in @architecture.png
aru> analyze @D:/full/path/to/image.jpg

Images are sent natively to the model via the provider's multimodal API โ€” no base64 text is injected into the conversation. Works with any multimodal model (Claude Opus/Sonnet, GPT-4o, Gemini, etc.). The autocomplete shows an [image] label for image files.

Note: Images require a multimodal model. Local models via Ollama may not support image input. Maximum file size: 20MB.

Configuration

Models and Providers

By default, aru uses Claude Sonnet 4.6 (Anthropic). You can switch to any supported provider during a session with /model:

Provider Command API Key (.env) Extra Installation
Anthropic /model anthropic/claude-sonnet-4-6 ANTHROPIC_API_KEY โ€” (included)
Ollama /model ollama/llama3.1 โ€” (local) pip install "aru-code[ollama]"
OpenAI /model openai/gpt-4o OPENAI_API_KEY pip install "aru-code[openai]"
Groq /model groq/llama-3.3-70b-versatile GROQ_API_KEY pip install "aru-code[groq]"
OpenRouter /model openrouter/deepseek/deepseek-chat-v3-0324 OPENROUTER_API_KEY pip install "aru-code[openai]"
MiniMax /model openrouter/minimax/minimax-m2.7 OPENROUTER_API_KEY pip install "aru-code[openai]"

To install all providers at once:

pip install "aru-code[all-providers]"

Ollama (local models)

To run models locally without an API key, install Ollama, start the server, and use any installed model:

ollama serve                    # Start the Ollama server
ollama pull codellama           # Download a model
aru                             # Start aru
# Inside aru:
/model ollama/codellama

Configuring the default model

You can set the default provider/model in aru.json so you don't need to switch manually every session:

{
  "default_model": "openrouter/minimax/minimax-m2.7",
  "model_aliases": {
    "minimax": "openrouter/minimax/minimax-m2.5",
    "minimax-m2.7": "openrouter/minimax/minimax-m2.7",
    "deepseek-v3": "openrouter/deepseek/deepseek-chat-v3-0324",
    "sonnet-4-6": "anthropic/claude-sonnet-4-6",
    "opus-4-6": "anthropic/claude-opus-4-6"
  }
}

The default_model field sets the main model. The model_aliases are shortcuts that can be used with /model <alias>.

Custom providers

You can configure custom providers with specific token limits:

{
  "providers": {
    "deepseek": {
      "models": {
        "deepseek-chat-v3-0324": { "max_tokens": 16384 }
      }
    },
    "openrouter": {
      "models": {
        "minimax/minimax-m2.5": { "max_tokens": 65536 },
        "minimax/minimax-m2.7": { "max_tokens": 131072 }
      }
    }
  }
}

Permissions (aru.json)

Aru uses a granular permission system where each tool action resolves to one of three outcomes:

  • allow โ€” executes without asking
  • ask โ€” prompts for confirmation (once / always / no)
  • deny โ€” blocks the action silently

Configure permissions per tool category with glob patterns:

{
  "permission": {
    "*": "ask",
    "read": "allow",
    "glob": "allow",
    "grep": "allow",
    "list": "allow",
    "edit": {
      "*": "allow",
      "*.env": "deny"
    },
    "write": {
      "*": "allow",
      "*.env": "deny"
    },
    "bash": {
      "*": "ask",
      "git *": "allow",
      "npm *": "allow",
      "pytest *": "allow",
      "rm -rf *": "deny"
    },
    "web_search": "allow",
    "web_fetch": "allow",
    "delegate_task": "allow"
  }
}

Available categories

Category Matched against Default
read file path allow
edit file path ask
write file path ask
bash command string safe prefixes = allow, rest = ask
glob โ€” allow
grep โ€” allow
list โ€” allow
web_search โ€” allow
web_fetch URL allow
delegate_task โ€” allow

Rule precedence

Rules use last-match-wins ordering. Place catch-all "*" first, then specific patterns:

{
  "edit": {
    "*": "allow",
    "*.env": "deny",
    "*.env.example": "allow"
  }
}

Shorthands

"permission": "allow"

Allows everything (equivalent to --dangerously-skip-permissions).

"permission": { "read": "allow", "edit": "ask" }

String value applies to all patterns in that category.

Defaults

Without any aru.json config, aru applies safe defaults:

  • Read-only tools (read, glob, grep, list) โ†’ allow
  • Mutating tools (edit, write) โ†’ ask
  • Bash โ†’ ~40 safe command prefixes auto-allowed (ls, git status, grep, etc.), rest โ†’ ask
  • Sensitive files (*.env, *.env.*) โ†’ deny for read/edit/write (except *.env.example)

Config file locations

Aru loads configuration from two levels, with project settings overriding global ones:

Level Path Purpose
Global (user) ~/.aru/config.json Defaults that apply to all projects (model, aliases, permissions, providers)
Project aru.json or .aru/config.json Project-specific overrides

Global config is loaded first, then the project config is deep-merged on top โ€” scalar values and lists are replaced, nested objects (like permission, providers, model_aliases) are merged recursively. This means you can set your preferred model and aliases globally and only override what's different per project.

Example ~/.aru/config.json:

{
  "default_model": "anthropic/claude-sonnet-4-6",
  "model_aliases": {
    "sonnet": "anthropic/claude-sonnet-4-6",
    "opus": "anthropic/claude-opus-4-6"
  },
  "permission": {
    "read": "allow",
    "glob": "allow",
    "grep": "allow"
  }
}

Then a project aru.json only needs project-specific settings:

{
  "default_model": "ollama/codellama",
  "permission": {
    "bash": { "pytest *": "allow" }
  }
}

The result: default_model becomes ollama/codellama, model_aliases come from global, and permission merges both levels (read, glob, grep from global + bash from project).

A full aru.json config reference here: aru.json

AGENTS.md

Place an AGENTS.md file in your project root with custom instructions that will be appended to all agent system prompts.

Instructions (Rules)

You can load additional instructions from local files, glob patterns, or remote URLs via the instructions field in aru.json:

{
  "instructions": [
    "CONTRIBUTING.md",
    "docs/coding-standards.md",
    "packages/*/AGENTS.md",
    "https://raw.githubusercontent.com/my-org/shared-rules/main/style.md"
  ]
}

Each entry is resolved as follows:

Format Example Behavior
Local file "CONTRIBUTING.md" Reads the file relative to the project root
Glob pattern "docs/**/*.md" Expands the pattern, respects .gitignore
Remote URL "https://example.com/rules.md" Fetches via HTTP (5s timeout, cached per session)

All resolved content is combined and appended to the agent's system prompt alongside AGENTS.md. Individual files are capped at 10KB, and the total combined size is capped at 50KB to prevent context bloat. Missing files and failed URL fetches are skipped with a warning.

.agents/ Directory

.agents/
โ”œโ”€โ”€ agents/         # Custom agents with their own model, tools, and prompt
โ”‚   โ””โ”€โ”€ reviewer.md # Usage: /reviewer <args>
โ”œโ”€โ”€ commands/       # Custom slash commands (filename = command name)
โ”‚   โ””โ”€โ”€ deploy.md   # Usage: /deploy <args>
โ””โ”€โ”€ skills/         # Custom skills/personas
    โ””โ”€โ”€ review/
        โ””โ”€โ”€ SKILL.md

Command files support frontmatter with description, agent, and model fields, plus OpenCode-style argument placeholders: $ARGUMENTS (full string), $1/$2 (positional), and $ARGUMENTS[N] (0-indexed).

Custom Agents

Custom agents are Markdown files with YAML frontmatter stored in .agents/agents/. Each agent runs with its own system prompt, model, and tool set โ€” unlike commands and skills, which reuse the General Agent.

---
name: Code Reviewer
description: Review code for quality, bugs, and best practices
model: anthropic/claude-sonnet-4-5
tools: read_file, grep_search, glob_search
max_turns: 15
mode: primary
---

You are an expert code reviewer. Analyze code for bugs, security,
performance, and readability. Do NOT modify files.

Frontmatter fields

Field Required Description
name Yes Display name of the agent
description Yes When to use this agent (shown in /agents and tab completion)
model No Provider/model reference (e.g., anthropic/claude-sonnet-4-5). Defaults to session model
tools No Comma-separated tool names (allowlist) or JSON object for granular control (e.g., {"bash": false}). Defaults to all general tools
max_turns No Max tool calls before the agent stops. Default: 20
mode No primary (invocable via /name) or subagent (only via delegate_task). Default: primary
permission No Permission overrides (same format as aru.json permission section). Replaces global rules for specified categories while the agent runs

Invocation

There are three ways to invoke a custom agent:

Method Syntax When to use
Slash command /reviewer src/auth.py Directly invoke a primary agent by name
@mention @reviewer check this function Mention an agent anywhere in your message
delegate_task Automatic (subagents only) Subagent names and descriptions are injected into the delegate_task tool description, so the LLM sees them and can call delegate_task(task="...", agent="name") on its own when it judges the task fits
aru> /reviewer src/auth.py           # slash command (primary agents)
aru> @reviewer check the auth module  # @mention (primary or subagent)
aru> /agents                          # list all custom agents

Note: Slash commands (/name) are only available for primary agents โ€” subagents are blocked with a warning. @mention works for any agent regardless of mode. Subagents can be invoked in two ways: automatically by the LLM via delegate_task, or manually by the user via @name.

Discovery paths

Agents are discovered from multiple locations (later overrides earlier):

  1. ~/.agents/agents/ โ€” global (available in all projects)
  2. ~/.claude/agents/ โ€” global (Claude Code compatible path)
  3. .agents/agents/ โ€” project-local
  4. .claude/agents/ โ€” project-local

Agent-level permissions

Agents can override global permission rules. Overrides replace the entire category โ€” unspecified categories inherit from global config.

---
name: Code Reviewer
description: Read-only code reviewer
permission:
  edit: deny
  write: deny
  bash:
    git diff *: allow
    grep *: allow
---

You can also set agent permissions in aru.json (overrides frontmatter):

{
  "agent": {
    "reviewer": {
      "permission": { "edit": "deny", "write": "deny" }
    }
  }
}

Each agent gets its own isolated "always" memory โ€” approvals during an agent's run don't carry over to the global scope.

Subagent mode

Agents with mode: subagent can be referenced by the LLM via delegate_task(task, agent="name") but are not directly invocable from the CLI.

Custom Tools

You can extend aru with your own Python tools. Drop a .py file in .aru/tools/ (project) or ~/.aru/tools/ (global) โ€” aru auto-discovers and registers every function found.

# .aru/tools/deploy.py
from aru.plugins import tool

@tool(description="Deploy the current branch to an environment")
def deploy(environment: str = "staging") -> str:
    """Runs the deploy script and returns the output."""
    import subprocess
    result = subprocess.run(
        ["./scripts/deploy.sh", environment],
        capture_output=True, text=True,
    )
    return result.stdout or result.stderr

The LLM sees each tool as a first-class function โ€” name, description, and typed parameters are inferred from the signature.

Rules

  • Decorator is optional. A bare def fn(...) -> str with a docstring works too. Use @tool(...) when you want a custom description or to override a built-in.
  • Parameters are read from type hints; defaults become optional params.
  • Return type should be str (or something stringifiable) โ€” the result is sent back to the LLM as tool output.
  • Override built-ins with @tool(override=True) if you want to replace, say, bash with your own implementation.
  • Discovery paths (later roots override earlier ones):
    1. ~/.aru/tools/
    2. .aru/tools/
    3. ~/.agents/tools/
    4. .agents/tools/

Both sync and async def functions are supported.

Plugins

For more control than custom tools โ€” e.g. intercepting tool calls, mutating chat messages, injecting env vars into shell commands, or blocking permissions โ€” use the plugin system. Plugins are Python files that return a Hooks object, mirroring OpenCode's hook pattern.

# .aru/plugins/audit.py
from aru.plugins import Hooks, PluginInput

async def plugin(ctx: PluginInput, options: dict | None = None) -> Hooks:
    hooks = Hooks()

    @hooks.on("tool.execute.before")
    async def before_tool(event):
        print(f"[audit] running {event.tool_name} with {event.args}")

    @hooks.on("tool.execute.after")
    async def after_tool(event):
        print(f"[audit] {event.tool_name} โ†’ ok")

    @hooks.on("shell.env")
    async def inject_env(event):
        event.env["DEPLOY_TOKEN"] = "โ€ขโ€ขโ€ขโ€ข"

    # You can also register tools directly from a plugin:
    def greet(name: str) -> str:
        """Say hello."""
        return f"hello, {name}"
    hooks.tools["greet"] = greet

    return hooks

Save the file as .aru/plugins/<name>.py and aru will load it automatically at startup.

Available hooks

Hook When it fires Typical use
config After config is loaded Read/adjust config
tool.execute.before Before any tool runs Audit, block, mutate args
tool.execute.after After any tool runs Log, post-process results
tool.definition When tool list is resolved Modify tool descriptions/params
chat.message Before a user message is sent to the LLM Rewrite the message
chat.params Before the LLM call Adjust temperature, max_tokens
chat.system.transform Before the LLM call Modify the system prompt
chat.messages.transform Before the LLM call Modify the full message history
command.execute.before Before a slash command runs Block or rewrite commands
permission.ask Before a permission prompt Auto-allow/deny
shell.env Before bash runs Inject env vars
session.compact Before context compaction React to compaction
event Any published event Generic subscription

Handlers can be sync or async. They run sequentially so each can mutate the event before the next handler sees it. Raise PermissionError to block an action.

Loading plugins

Plugins come from three sources:

  1. Auto-discovery โ€” .aru/plugins/*.py, .agents/plugins/*.py, and the same paths under ~/

  2. Config โ€” explicit list in aru.json:

    {
      "plugins": [
        "my-package-plugin",
        ["./.aru/plugins/audit.py", { "verbose": true }]
      ]
    }
    

    The second form passes options to the plugin as the options argument.

  3. Entry points โ€” installed packages can register via the aru.plugins entry point group

Every plugin file must export a plugin(ctx, options) function (sync or async) that returns a Hooks instance.

MCP Support (Model Context Protocol)

Aru can load tools from MCP servers. Configure in .aru/mcp_config.json:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"]
    }
  }
}

Agents

Built-in agents are declared as specs in aru/agents/catalog.py and instantiated on demand by agent_factory.create_agent_from_spec. A single construction path resolves the model, tool list, prompt role, and plugin hooks for all native agents.

Agent Mode Role Tools
build (General) primary Conversational coding assistant. Self-triggers enter_plan_mode for 3+ file changes Full tool set including delegate_task
plan (Planner) primary Read-only analysis โ†’ ## Summary + ## Steps markdown plan Read/search only (read_file, read_files, glob_search, grep_search, list_directory)
executor primary Step-by-step execution of a stored plan with mandatory task list tracking Full tool set
explorer subagent Fast, read-only codebase research. Invoked only via delegate_task(task, agent_name="explorer") Read/search + read-only bash + rank_files

Scope reviewer: aru/agents/planner.py also exposes review_plan(request, plan), a one-shot, no-tool reviewer that runs on the small model to trim scope creep from generated plans. Enabled via plan_reviewer: true in aru.json.

Plan mode flow

The plan agent runs in two ways:

  1. Manual: the user types /plan <task> โ€” the planner produces a plan, the reviewer optionally trims it, and the result is stored in the session.
  2. Autonomous: the build agent calls enter_plan_mode(task) when it detects a multi-file task. This invokes the planner, stores the plan, and returns a summary.

Once a plan is stored, every following turn prepends a <system-reminder> listing all plan steps with their status icons. The build/executor agent works through them in order, calling update_plan_step(index, "completed") after each. Within a step, it calls create_task_list([...]) to break the step into 1โ€“10 concrete subtasks, then update_task(i, "completed") as they finish.

Tools

File Operations

  • read_file โ€” Reads files with line range support and binary detection
  • read_files โ€” Reads multiple files in parallel (batched)
  • write_file โ€” Writes content to files, creating directories as needed
  • write_files โ€” Writes multiple files in one call
  • edit_file โ€” Find-and-replace edits on files
  • edit_files โ€” Batched find-and-replace across multiple files

Search & Discovery

  • glob_search โ€” Find files by pattern (respects .gitignore)
  • grep_search โ€” Content search with regex and file filtering
  • list_directory โ€” Directory listing with gitignore filtering
  • rank_files โ€” Multi-factor file relevance ranking (explorer subagent only)

Shell & Web

  • bash โ€” Executes shell commands with permission gates
  • web_search โ€” Web search via DuckDuckGo
  • web_fetch โ€” Fetches URLs and converts HTML to readable text

Planning & Delegation

  • enter_plan_mode โ€” Generate a structured plan via the planner agent and store it in the session
  • update_plan_step โ€” Mark a macro plan step as in_progress / completed / failed / skipped
  • create_task_list โ€” Declare 1โ€“10 subtasks for the current step (mandatory first executor call)
  • update_task โ€” Mark a subtask as in_progress / completed / failed
  • delegate_task โ€” Spawn an autonomous subagent (defaults to explorer) for parallel research or execution

Architecture

aru-code/
โ”œโ”€โ”€ aru/
โ”‚   โ”œโ”€โ”€ cli.py              # Main REPL loop, argument parsing, and entry point
โ”‚   โ”œโ”€โ”€ agent_factory.py    # Single factory โ€” builds Agno Agents from catalog specs
โ”‚   โ”œโ”€โ”€ commands.py         # Slash commands, help display, shell execution
โ”‚   โ”œโ”€โ”€ completers.py       # Input completions, paste detection, @file mentions
โ”‚   โ”œโ”€โ”€ context.py          # Token optimization (pruning, truncation, compaction)
โ”‚   โ”œโ”€โ”€ display.py          # Terminal display (logo, status bar, streaming output)
โ”‚   โ”œโ”€โ”€ runner.py           # Agent execution, streaming, PLAN ACTIVE reminder injection
โ”‚   โ”œโ”€โ”€ session.py          # Session state, persistence, plan steps tracking
โ”‚   โ”œโ”€โ”€ runtime.py          # Request context (TaskStore, session, display handles)
โ”‚   โ”œโ”€โ”€ config.py           # Configuration loader (AGENTS.md, .agents/)
โ”‚   โ”œโ”€โ”€ providers.py        # Multi-provider LLM abstraction
โ”‚   โ”œโ”€โ”€ permissions.py      # Granular permission system (allow/ask/deny)
โ”‚   โ”œโ”€โ”€ agents/
โ”‚   โ”‚   โ”œโ”€โ”€ base.py         # Shared prompt templates + build_instructions(role)
โ”‚   โ”‚   โ”œโ”€โ”€ catalog.py      # AgentSpec registry โ€” build / plan / executor / explorer
โ”‚   โ”‚   โ””โ”€โ”€ planner.py      # review_plan() โ€” small-model scope reviewer
โ”‚   โ””โ”€โ”€ tools/
โ”‚       โ”œโ”€โ”€ codebase.py     # Core tool implementations + GENERAL/EXECUTOR/PLANNER/EXPLORER sets
โ”‚       โ”œโ”€โ”€ plan_mode.py    # enter_plan_mode tool (agent-invokable planner entry)
โ”‚       โ”œโ”€โ”€ tasklist.py     # create_task_list / update_task / update_plan_step
โ”‚       โ”œโ”€โ”€ ast_tools.py    # Tree-sitter code analysis
โ”‚       โ”œโ”€โ”€ ranker.py       # File relevance ranking
โ”‚       โ”œโ”€โ”€ mcp_client.py   # MCP client
โ”‚       โ””โ”€โ”€ gitignore.py    # Gitignore-aware filtering
โ”œโ”€โ”€ aru.json                # Permissions and model configuration
โ”œโ”€โ”€ .env                    # API keys (not committed)
โ”œโ”€โ”€ .aru/                   # Local data (sessions)
โ””โ”€โ”€ pyproject.toml

Built With

Development

# Clone and install in editable mode with dev dependencies
git clone https://github.com/estevaofon/aru.git
cd aru
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=aru --cov-report=term-missing

Built with Claude and Agno

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aru_code-0.26.1.tar.gz (213.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aru_code-0.26.1-py3-none-any.whl (148.6 kB view details)

Uploaded Python 3

File details

Details for the file aru_code-0.26.1.tar.gz.

File metadata

  • Download URL: aru_code-0.26.1.tar.gz
  • Upload date:
  • Size: 213.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.8

File hashes

Hashes for aru_code-0.26.1.tar.gz
Algorithm Hash digest
SHA256 aa8c0246483ed1e07e05df3c057b825c6d0110ab32965a059c7e121023c48a85
MD5 ddc0724fef29ff0c003aa7367f2872cb
BLAKE2b-256 d21ef81377a47b157aae68fab92d47270b429ee1f8a8db3a823ff577ab9f168b

See more details on using hashes here.

File details

Details for the file aru_code-0.26.1-py3-none-any.whl.

File metadata

  • Download URL: aru_code-0.26.1-py3-none-any.whl
  • Upload date:
  • Size: 148.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.8

File hashes

Hashes for aru_code-0.26.1-py3-none-any.whl
Algorithm Hash digest
SHA256 339ecdf6178b8cee7562a1ed291d5709c89be4853391ee0c0f84df0f221b590a
MD5 9ddd0d2a9def328432e639c392857f4a
BLAKE2b-256 41585492089c69806d30e2ca2a4eb0dd5ad20e3edd4e335cf25a5bba54681e21

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page