A high-performance Rust implementation of the Hermes-Agent orchestration loop for LLM-driven tool execution.
- Streaming-First Architecture: Detect and execute tool calls incrementally from partial LLM outputs
- Tolerant XML Parser: Handle malformed tags and unclosed JSON with state-machine parsing
- Early Tool Detection: Initiate tool execution as soon as
</tool_call>is detected - Self-Healing: Automatically re-prompt LLM with error context on failures
- Dynamic Schema Generation: Automatically generate JSON Schema from Rust structs
- Shared TOML Configuration: One runtime config model across
hermes-cliandhermes-core - Ratatui TUI: Prompt-first landing view, responsive workspace panes, constrained-terminal fallback, blockquote-style reasoning, block-style tool activity, MCP/Skills/Behavior management
- Autonomous Coding Mode: 24/7 workspace-driven loop that reads
TODO.md, validates with local tests, and only pushes after success - Structured Logging: Comprehensive observability via the
tracingcrate
┌─────────────────────────────────────────────────────────────┐
│ Hermes-RS │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ OpenAI │ │ XMLParser │ │ ToolRegistry │ │
│ │ Client │ │ (Tolerant) │ │ & Execution │ │
│ └─────────────┘ └──────────────┘ └────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Orchestration Loop (ReAct) ││
│ │ Think → Plan → Execute Tools → Observe → Respond ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
# Build from source
cargo build --release
# Or install the CLI crate directly
cargo install --path crates/hermes-cliTagged releases publish per-platform binaries automatically in the repository's GitHub Releases tab.
# Set your API key
export OPENAI_API_KEY=your_api_key_here # PowerShell: $env:OPENAI_API_KEY="..."
# Start the prompt-first TUI
hermes chat
# Run a one-shot query
hermes run --query "What is 2 + 2?"
# Start 24/7 autonomous workspace mode
hermes autonomous
# List available tools
hermes tools
# Test a specific tool
hermes test echo --args '{"message": "Hello, World!"}'Prompt-first landing screen:
Workspace session with conversation, reasoning, and activity panes:
Hermes reads configuration in this order:
--config <path>./hermes.toml./.hermes.toml- OS config directory (for example
~/.config/hermes/config.tomlon Linux) - Environment variables
- CLI flags
Start from the checked-in example file:
cp hermes.example.toml hermes.tomlConfiguration is TOML, not YAML. Example:
[client]
base_url = "https://api.openai.com/v1"
timeout_secs = 60
# api_key = "set me or use OPENAI_API_KEY"
[agent]
model = "gpt-4"
max_iterations = 20
tool_timeout_secs = 30
request_timeout_secs = 120
stream = true
show_reasoning = true
[autonomous]
interval_secs = 300
todo_path = "TODO.md"
status_path = "autonomous-status.toml"
test_command = "cargo test --workspace"
git_remote = "origin"
git_branch = "agent-dev"
commit_message = "Auto-commit by hermes-rs"
[tui]
rich_output = true
landing_title = "HERMES"
prompt_placeholder = "Ask anything... \"Fix a TODO in the codebase\""Or use environment variables:
export OPENAI_API_KEY=your_api_key_here
export OPENAI_BASE_URL=https://api.openai.com/v1
export HERMES_MODEL=gpt-4See hermes.example.toml for the full schema, including MCP, Skills, gateway, and tool/runtime defaults.
- Hermes automatically loads the nearest workspace guidance file from
AGENTS.md,CLAUDE.md,.hermes.md,HERMES.md, or.cursorrulesand injects it into the system prompt as<workspace_context> - Global
.md/.txtcontext files under the user Hermes context directory are also included when present - Oversized context files are truncated and obvious prompt-injection patterns are blocked before injection
hermes autonomousruns a continuous loop against the current workspacehermes run --autonomousis kept as a compatibility alias for the same mode- Autonomous mode reads repo-root
TODO.mdon every tick and skips work when## Pendingis empty - The loop uses the existing Hermes agent and tools to inspect the repo, implement the next pending task, and update
TODO.md - Hermes writes a repo-local
autonomous-status.tomlreport on each tick with the current state, failure summary, validation result, and last push target - After each iteration Hermes runs the configured validation command, which defaults to
cargo test --workspace - Git operations are strict:
- tests must pass before any push is attempted
- successful runs stage workspace changes while excluding the status report, then execute
git commit -m "Auto-commit by hermes-rs"andgit push origin agent-dev - repeated failures on the same workspace state pause the loop until
TODO.mdor git state changes, and that pause survives process restarts throughautonomous-status.toml
TODO.md is the autonomous source of truth and should keep this structure:
## Implemented
- completed work
## Pending
- next tasks for autonomous modehermes chatstarts on a prompt-first landing screenienters prompt editing, and typing on landing also bootstraps prompt entry immediatelyEnterruns the current promptUp/Downin prompt mode replay recent prompts from historyTabcycles workspace panelsUp/Downscroll the chat in command modePageUp,PageDown,Home, andEndscroll the conversation even while prompt mode is activeCtrl+Lstarts a fresh session when you want to discard the current conversation history- The workspace uses a split desktop layout at 120 columns and above, stacks panels below that, and collapses secondary panels into popups below 65 columns or 20 rows
- The Reasoning pane renders model thinking with quote rails, while tool calls in Activity render as compact blocks for easier scanning
- After a run completes or fails, the workspace returns to prompt mode so you can send a follow-up in the same session
stream = falsenow uses the non-streaming response path instead of the streaming parser
Use a throwaway repository first to validate your autonomous setup end to end:
mkdir hermes-autonomous-sample
cd hermes-autonomous-sample
git init
git checkout -b agent-dev
cp ../hermes-rs/hermes.example.toml ./hermes.tomlCreate a minimal TODO.md:
## Implemented
- bootstrap sample repo
## Pending
- add one safe autonomous taskThen tune [autonomous] in hermes.toml for the sample repo:
[autonomous]
todo_path = "TODO.md"
status_path = "autonomous-status.toml"
test_command = "git diff --check"
git_remote = "origin"
git_branch = "agent-dev"Run Hermes:
hermes autonomousWhile it runs, inspect:
TODO.mdto confirm completed items move fromPendingtoImplementedautonomous-status.tomlto seestate, timing fields, failure or pause metadata, the last validation summary, and the last push targetgit log --onelineto confirm only validated work is committed
If the loop pauses after repeated failures, edit TODO.md or otherwise change the workspace state, then start or continue hermes autonomous again. Hermes reloads the persisted pause state from autonomous-status.toml and resumes only after the workspace fingerprint changes.
use hermes_core::{
agent::{HermesAgent, AgentConfig},
client::{OpenAIClient, ClientConfig},
tools::{HermesTool, ToolRegistry, ToolContext},
schema::ToolSchema,
};
use async_trait::async_trait;
use serde_json::Value;
// Define a custom tool
struct MyTool;
#[async_trait]
impl HermesTool for MyTool {
fn name(&self) -> &str { "my_tool" }
fn description(&self) -> &str { "My custom tool" }
fn schema(&self) -> ToolSchema { /* ... */ }
async fn execute(&self, args: Value, context: ToolContext) -> ToolResult {
// Your tool logic here
}
}
// Create the agent
let client = OpenAIClient::new(ClientConfig::default());
let registry = ToolRegistry::new(std::time::Duration::from_secs(30));
registry.register(MyTool).await.unwrap();
let agent = HermesAgent::new(
AgentConfig::default(),
client,
registry,
);
// Run the agent
let response = agent.run("Hello!").await?;
println!("{}", response.content);hermes [OPTIONS] <COMMAND>
Commands:
autonomous Run the autonomous coding loop
run Run the agent with a query
tools List available tools
chat Interactive chat mode
test Test a specific tool
help Print this message or the help of the given subcommand(s)
Options:
-v, --verbose Enable verbose output
-l, --log-level <LOG> Log level (debug, info, warn, error) [default: info]
-c, --config <FILE> Configuration file path
--api-key <KEY> OpenAI API key
--base-url <URL> OpenAI base URL
-m, --model <MODEL> Model to use [default: gpt-4]
-i, --max-iterations <N> Maximum iterations [default: 20]
--tool-timeout <SECS> Tool timeout in seconds [default: 30]
--request-timeout <SECS> Request timeout in seconds
--context-window <TOKENS> Context window size
--max-healing-attempts <N> Maximum self-healing retries
--stream / --no-stream Force streaming on or off
Tools are defined via the HermesTool trait. The framework automatically generates JSON Schema from your Rust structs:
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase")]
struct WeatherArgs {
city: String,
country: Option<String>,
}
struct WeatherTool;
#[async_trait]
impl HermesTool for WeatherTool {
fn name(&self) -> &str { "get_weather" }
fn description(&self) -> &str { "Get weather information for a city" }
fn schema(&self) -> ToolSchema {
ToolSchema::from_type::<WeatherArgs>("get_weather", "Get weather information")
}
async fn execute(&self, args: Value, context: ToolContext) -> ToolResult {
// Parse and execute
}
}Hermes can also register delegate_to_sub_agent, an opt-in built-in tool that lets the parent ReAct agent delegate focused deep-analysis tasks to an isolated child HermesAgent with a fresh conversation.
The library provides structured error types with self-healing capabilities:
use hermes_core::error::Error;
match result {
Ok(response) => { /* handle success */ }
Err(Error::ToolNotFound { name }) => {
// Tool doesn't exist - self-healing will re-prompt LLM
}
Err(Error::ToolTimeout { name, timeout }) => {
// Tool timed out - retry logic available
}
Err(e) => {
// Other errors
}
}Dual-licensed under MIT or Apache 2.0.
See CONTRIBUTING.md for coding conventions, testing requirements, and the PR process.
Documentation and release hygiene for maintainers:
-
keep
hermes.example.tomlin sync with runtime config changes -
add every user-facing change to
CHANGELOG.mdbefore cutting a tag -
update README screenshots or keybinding docs when TUI behavior changes
-
update
AGENTS.md/CLAUDE.mdwhen the project context changes enough that an agent would otherwise rediscover it from scratch
This project is a Rust implementation of the Hermes-Agent originally developed by Nous Research.
While this is a "pure Rust" rewrite, the orchestration logic, system prompts, and architecture are based on the original work. This project is an unofficial community port and is not affiliated with or endorsed by Nous Research.


