Hermes Bot Forge
Say "make me a social media manager" — your Hermes agent builds that Bot.
hermes plugins install bot-forge
Bot Forge is a Hermes Agent plugin that lets any agent spawn a complete, working Bot Mode Bot from one sentence — no New Agent dialog, no setup:
| The new Bot gets | |
|---|---|
| 🪪 Identity | a unique, Proper Case name and a blob face in the Bots roster |
| 📜 SOUL.md | written by your agent for that one job |
| 🧠 Memory | starter facts, plus what Hermes already knows about you |
| 🛠️ Tools & skills | file, web and browser, plus what the role needs |
| ⏰ Routines | optional cron jobs that post into its Bot Chat |
| 💬 Bot Chat | opened with the Bot introducing itself |
| 🔌 Gateway | a background service, started and enabled on login |
Every step is checked, and the whole Bot is rolled back if one fails.
Demo
1. Ask any Bot for a new teammate. It checks the roster, designs the Bot and calls create_agent — the new Bot (Inkwell) appears in the roster while it works:

2. The new Bot is already alive. Its Bot Chat opens with it introducing itself:

Onboarding
1. Requirements
- Hermes Agent 0.21+ with the
hermesCLI on yourPATH - Hermes Desktop for Bot Mode (the CLI works too — Bots are profiles)
- Linux and macOS are tested. Windows is not: Bots are still created and work from the CLI, but gateway services and sandboxes are skipped there
2. Install
hermes plugins install BkashJEE/hermes-bot-forge
3. Enable it where you chat
Plugins are opt-in per profile. Enable Bot Forge on each profile that should be able to create Bots — your main profile, and any "manager" Bot:
hermes plugins enable bot-forge # the default profile
hermes -p ceo plugins enable bot-forge # a Bot named "ceo"
If enable asks to grant tool overrides, answer no — Bot Forge doesn't override anything.
4. Restart Hermes
hermes gateway restart
hermes -p ceo gateway restart # for each profile you enabled
Then quit and reopen Hermes Desktop.
5. Check it's loaded
hermes bot-forge-doctor
It tells you exactly what is missing — a profile you forgot to enable, a gateway still running the old code (the usual reason the tools never appear), a model that needs a per-Bot sign-in, and which sandbox backends this machine can run. Or, the long way:
hermes plugins list | grep bot-forge
Or ask your agent: "what tools do you have for creating agents?" — it should name create_agent, update_agent, list_agents and the rest.
6. Make your first Bot
Open Bot Mode, click the Bot you enabled, and type:
make me a bot that writes x.com posts and threads
Wait 1–3 minutes. The reply looks like:
🧪 Inkwell is alive — find it in Bot Mode.
job: turns ideas, links and demo notes into X posts and threads
brain: gpt-6-astra tools: browser, file, web, …
Click the new Bot — its Bot Chat already has its introduction.
7. Choose how new Bots sign in
New Bots inherit the model of the Bot that created them. Whether they can use it right away depends on the provider:
| Your model's provider | What happens | What to do |
|---|---|---|
| API key (OpenRouter, OpenAI API, Anthropic API, …) | Works immediately — keys are copied to new profiles | Nothing |
| Local model (llama.cpp, Ollama, LM Studio) | Works immediately | Nothing |
| OAuth sign-in (ChatGPT/Codex, Claude subscription, …) | The Bot is created; the result tells you it needs a sign-in | Run the command it gives you once per Bot, or set a fallback_model, or see share one login below |
Settings
Set per profile under plugins.entries.bot-forge.settings in that profile's config.yaml:
plugins:
entries:
bot-forge:
settings:
inherit_model: true
fallback_model: {}
probe_local_models: false
install_gateway: true
journal_enabled: true
suggest_connectors: true
allow_delete: false
| Key | Default | Meaning |
|---|---|---|
inherit_model |
true |
New Bots use the model of the profile that asked for them, like Bot Mode's New Agent. |
fallback_model |
{} |
Model to switch a Bot to when its inherited model can't sign in, e.g. {default: qwen3, provider: custom, base_url: http://127.0.0.1:8080/v1}. |
probe_local_models |
false |
With no fallback_model, look for a local llama.cpp / Ollama / LM Studio server to fall back to. |
install_gateway |
true |
Install and start a gateway service per Bot (skipped on Windows). |
journal_enabled |
true |
Give new Bots a private, append-only work journal for outcomes, evidence and next steps. |
allow_delete |
false |
Let delete_agent work at all. Off by default — an agent should not be able to destroy a Bot on its own. |
backup_before_delete |
true |
Export the Bot to a .tar.gz before deleting it, so it can be restored. If the backup fails, the delete is refused. |
allow_secrets |
false |
Let share_agent write / import_agent accept a template the secret scanner marked BLOCK. Operator-only; the model cannot pass it as an argument. |
suggest_connectors |
true |
After building a Bot, suggest matching servers from Hermes' MCP catalog. Suggestion only — connecting an account always needs you. |
Optional: share one login across Bots
The plugin itself never touches credentials. If you want OAuth models to work in new Bots with no per-Bot sign-in, there is an unsupported helper you run yourself:
python extras/share_login.py <bot-name>
It copies the root profile's auth.json into an independent file with permissions 0600. It never copies or links auth.lock, and removes a destination lock symlink left by the older helper. Existing destination credential links are replaced without writing through them; linked profile directories are refused. Each selected Bot receives every provider login in the copied file. OAuth refresh tokens can still compete at the provider, so signing each Bot in independently is recommended. POSIX only. Remove the copied <profile>/auth.json to discard that Bot's copy.
Tools
Thirteen tools, all driven by plain requests in chat:
| Tool | Say this | What it does |
|---|---|---|
create_team |
"set me up a content team" | Builds a whole team at once: a lead plus up to 6 specialists, each reporting to it. The lead learns the roster and delegates. |
teach_agent |
"remember how I write my weekly report" | Saves a procedure as a skill the Bot keeps and loads when the job comes up. |
create_agent |
"make me a bot that writes X posts" | Builds a Bot: name, face, SOUL.md, memory, tools, skills, routines, approvals, Bot Chat intro, gateway. |
update_agent |
"make Inkwell funnier", "give Atlas the browser" | Edits a Bot in place — persona, name, description, memory, tools, skills, model, face, routines. Backs up what it replaces. |
copy_agent |
"make another one like Inkwell, for LinkedIn" | Duplicates a Bot under a new name (no chat history, no routines). |
list_agents |
"what bots do I have?" | Roster with description, model, routine count and hidden state. |
check_install |
"the tools aren't showing up" | Checks the install itself: enabled profiles, whether each gateway runs the current code, Bot Mode, model sign-in, sandbox backends. |
check_agents |
"anything waiting on me?" | Leads with waiting_on_you — every Bot blocked on something only you can do, with its age. Then the read-only health check: routines that run too often (and their cost in runs/day), paused or never-run routines, unused Bots, stopped gateways, a persona missing its name or approvals. |
agent_journal |
"what did Inkwell work on this week?" | Enables, appends to, and reads a Bot's dated work journal. Entries capture outcomes and evidence, never credentials or private reasoning. |
ask_agent |
"ask Inkwell for 3 post ideas" | Sends a task to another Bot and returns its reply. |
share_agent |
"share Inkwell with a friend" | Writes a readable .botforge.json template — persona, its own memory, tools, skills, routines. Never chat history, facts about you, or keys, and secret-scanned (CLEAN / WARN / BLOCK). mode: backup makes a full private backup instead. |
import_agent |
"import this bot" | Builds a Bot from a .botforge.json template (scanned again), or restores a backup. |
hide_agent |
"hide Inkwell from the list" | Hides or unhides it in the roster. It keeps running. |
delete_agent |
"delete Inkwell" | Permanent. Off unless you enable it, and it must repeat the Bot's exact name. |
Start from a proven template
Five starters, modelled on the most-used Grok Bot patterns:
| Template | Bot | What it does | Routine |
|---|---|---|---|
chief-of-staff |
Marshal | Routes work to specialists, keeps open loops, pings you only when you must act | 7:00 brief, 18:00 handoff (weekdays), Friday 16:00 review |
morning-brief |
Dawn | Calendar, what's waiting on you, 3 headlines — drafts only | 7:30 weekdays |
research-digest |
Scout | What changed on your topics in the last 24 hours, sourced | 8:00 daily |
competitor-watcher |
Lookout | Reports real changes on competitors' pricing, product and hiring pages | 9:00 weekdays |
engineering-outer-loop |
Foreman | Failing checks, stuck PRs, new issues → small tasks. Never writes or merges code | 9:30 weekdays |
make me a chief of staff from the template
Any field you give overrides the template's, so "a morning brief bot called Sol that also covers crypto" works.
A team in one sentence
set me up a content team
builds a lead plus its specialists, each with one job, each reporting to the lead, each introduced in its own Bot Chat. A member that fails is rolled back on its own — the rest of the team stands.
A Bot that tells you where your request stands
Every new Bot starts its reply with one emoji for the state of your request:
| 👀 | picked it up, working on it |
| 💬 | answering now |
| ✅ | done |
| ✋ | needs your approval before going further |
| ⚠️ | blocked, or something failed |
| ⏳ | scheduled for later |
In Hermes Desktop a reaction lands on your own message as a tapback, the moment you send it — and it fits what you asked: 🔧 for a fix, 🔎 for research, ✍️ for writing, 📊 for numbers, ⏳ for something scheduled, 📋 for a review, 🛠️ for something to build, 💬 for a question, 👀 when it can't tell. Then it becomes ✅ / ✋ / ⚠️ for how the turn ended. The reaction is placed by a hook, not by the model — and because a Hermes hook only runs in the profile running the turn, that hook ships inside each Bot as a tiny companion plugin (bot-forge-marks: two hooks, no tools, so a Bot never gains the power to create or delete Bots). New Bots get it automatically; for a Bot made before it, ask an agent to "turn on reactions for ". It needs Settings → Appearance → Message Reactions on, and switches off with ack_tapback: false. check_install reports which Bots can react and which cannot.
Everywhere else, the state rides on the reply: one emoji, at the very start, then the answer as normal — it is never the whole reply. Because it rides on the reply itself, it works the same in Hermes Desktop, an editor client, the CLI, a cron run or a messaging platform.
This deliberately does not use Hermes' emoji tapbacks:
react_to_messageis documented as a human touch, "never as a status signal", and a Bot told to do both follows neither.
Turn it off for a Bot with create_agent(ack_reactions=false), or on for an older Bot:
turn on acknowledgements for Inkwell
Give a Bot its own computer
A Bot with terminal or code_execution runs commands on your machine by default — the same weakness people hit with other bot platforms, where every Bot shares one computer and one set of logins. Ask for a sandbox instead:
make me a coding bot, sandboxed
create_agent(sandbox="docker") puts that Bot's shell in its own container: it cannot read your files and cannot block the other Bots. singularity and apptainer work too. If the backend is not usable on this machine, the call is refused before any Bot is created, with the reason. check_agents reports each Bot's sandbox and flags shell-capable Bots that run on the real machine.
A journal for work that survives the chat
Every new Bot gets a private journal/YYYY-MM-DD.md log. After meaningful work it records a short factual entry:
- what it tried and the observable outcome
- evidence such as a file, command, link or measurement
- blockers and the next step
Routine conversation is skipped. Credentials, facts unrelated to the Bot's job, private reasoning and hidden chain-of-thought are refused. Journals stay local, are excluded from shareable .botforge.json templates, and are included only in explicit private backups.
For a Bot created before this feature:
enable journaling for Inkwell
Then ask "what did Inkwell work on this week?" to read recent entries.
What's waiting on you
A blocked Bot writes the blocker in its journal and then goes quiet — so blockers pile up unseen, one Bot at a time. Ask once:
anything waiting on me?
check_agents answers with waiting_on_you: every unresolved blocked or failed entry across every Bot, newest first, each with the Bot's name, what it needs and how many days it has sat there.
2 waiting on you — Marlow: Need the Stripe API key to pull invoices; Nova: Weekly post draft could not publish
The item closes itself when the Bot records the same piece of work as completed — nothing to tick off by hand.
It knows where it landed
A new Bot normally arrives knowing its job and nothing about your machine, so the first thing you do is explain your own workspace to it. Bot Forge reads that once, at birth:
make me a social media manager
🧪 Quill is alive — find it in Bot Mode.
fits: x-content — X Content Studio (~/work/x-content)
already here: social-media, media-use
heads up: Nova already works in this territory
It scores the Bot's own SOUL.md and one-job against the directory Hermes is running in, the repos under it, and this install's Bots, skills and plugins — then writes the answer into the Bot's memory, so it knows on turn one and never researches your machine again. Deterministic word matching, not a model call: no tokens, no waiting, no questions.
It also refuses to build a Bot you already have. If an existing Bot's job covers the new one, create_agent stops and names it, so a roster of twenty Bots doesn't quietly become a roster of twenty overlapping ones.
Read-only and shallow — directory names, git remotes, and the head of a README / AGENTS.md / CLAUDE.md. Never your source files, never your home directory unless you point workspace_roots at it, and anything that looks like a credential never reaches a Bot's memory. Off with workspace_survey: false.
A blocked Bot tells you
The waiting queue answers when you ask. This is the other half: when a Bot records a blocker, it emails you.
[Bot Forge] Marlow is blocked — Need the Stripe API key
Marlow stopped and needs you.
Need the Stripe API key
Invoice sync cannot run without a read-only key.
Next step: add the key, then rerun
Reply to Marlow in Hermes — this address does not take replies.
It uses the email Hermes already has (EMAIL_SMTP_HOST / EMAIL_ADDRESS / EMAIL_PASSWORD), so there is nothing new to configure and no new credential anywhere. Set notify_email to send somewhere other than your own mailbox, or false to switch it off.
Outbound only, on purpose. The recipient is resolved from your config and nothing else — never from a tool call, a persona, or anything a model wrote. A Bot can write to your address and no other, so it cannot be talked into mailing a stranger and two Bots cannot reply to each other forever. Nothing here reads a mailbox. Messages are rate limited per Bot so a stuck routine can't become a mail storm, secret-scanned before they leave, and skipped in silence when mail isn't set up — a mail server being down never breaks a turn.
For the whole queue on a schedule rather than one blocker at a time:
python3 ~/.hermes/plugins/bot-forge/notify.py digest
Cost and safety built in
- Draft-first by default. Every Bot is born with approval checkpoints — sending/publishing, spending money, deleting data — unless you explicitly ask for none.
- No runaway routines. Schedules faster than every 30 minutes are refused unless you explicitly ask (every run is a model call; every 15 minutes is 96 runs a day).
- Nothing private leaves in a share. Templates carry the Bot's design, never your chats, your facts or your keys — and they're scanned for secrets on the way out and on the way in.
- One identity per Bot. Renaming, copying or importing rewrites the persona's name instead of stacking a second one, so a Bot never introduces itself as another.
Guardrails a new Bot is born with
create_agent takes an approvals list and a reports_to Bot, written into the new Bot's SOUL.md and memory:
Ask first — never do these without the user saying yes: publish or send anything; spend money; delete files. Escalate to — @ceo for scope, priorities and final calls.
So a Bot that drafts posts never publishes one, and knows who to escalate to.
Bundled skill: bot-forge:bot-forge — role defaults, naming rules and a SOUL.md template your agent follows.
How it works
create_agent runs forge.py in a subprocess with a clean environment (Hermes agent terminals point HOME/HERMES_HOME at the calling profile):
- Name check — against live profiles, display names and Bot titles; generic or taken names are refused with suggestions before anything is created
hermes profile create <id> --clone-from default— messaging channels are left behind- Bot Mode metadata in
profile.yaml(ui_meta.hermes-bots: title, description, blob face) — the same shape Desktop's New Agent dialog saves - SOUL.md (guaranteed to state the Bot's own name),
memories/MEMORY.md, andmemories/USER.mdminus entries that name the assistant — otherwise the new Bot adopts another Bot's name - Journal — private, dated Markdown entries plus a concise factual journaling policy
- Config —
platform_toolsets.cli, unrelated skill categories disabled (not deleted), inherited model - Routines —
hermes cron create … --deliver bot-chat:<id> - Smoke test — the kickoff message in its
Bot Chat - Gateway —
hermes -p <id> gateway install --start-now --start-on-login
Any failure after step 2 deletes the profile.
Note: step 3 writes Desktop's Bot Mode metadata directly. It isn't a public API and may change between Hermes releases.
What makes it different
| Grok Bots | OpenMausBot | OpenClaw | Bot Forge | |
|---|---|---|---|---|
| Build a Bot from one sentence, no dialog | partial | roster UI | config/CLI | ✅ |
| Tested before you get it, rolled back on failure | — | — | — | ✅ |
| Persona file you can read and edit | instructions | SOUL.md |
SOUL.md |
✅ SOUL.md |
| Edit by chatting | UI | UI | delegation tool | ✅ update_agent |
| Share / import a Bot | templates | markdown teams | ClawHub | ✅ share_agent |
| Approval checkpoints written in at birth | set later | permission cards | policy | ✅ approvals |
| Whole team from one sentence | — | markdown file | — | ✅ create_team |
| Teach a skill by chatting | ✅ | playbooks | ClawHub | ✅ teach_agent |
| Connector suggestions for the job | ✅ | ✅ | — | ✅ from Hermes' MCP catalog |
| Routine cost health check | community bots | — | — | ✅ check_agents |
| Secret scan on shared templates | community bots (Bouncer, Vet) | — | — | ✅ built in |
| Chat history excluded from shares | ✅ | — | — | ✅ |
| Runs entirely on your machine | — | ✅ | ✅ | ✅ |
| Per-Bot sandbox chosen at creation | — (one shared computer) | — | — | ✅ sandbox |
| Every reply says where the request stands | — | — | — | ✅ built into every Bot |
| Install doctor | — | — | — | ✅ hermes bot-forge-doctor |
Troubleshooting
| Symptom | Fix |
|---|---|
| Agent asks questions instead of building | Say "bot" or "agent" in the request, e.g. "make me a bot that…" |
Agent doesn't know create_agent |
Run hermes bot-forge-doctor — usually the profile isn't enabled or the gateway still runs the old code |
| "name … is taken" | Expected — your agent picks another name automatically |
| New Bot says it needs a sign-in | See step 7 |
| New Bot doesn't appear in the roster | Click another Bot and back, or reopen Hermes Desktop |
| Gateway "not started" | Run hermes -p <bot> gateway install --start-now and read its output |
| An edit didn't take | Changes apply on the Bot's next turn; send it a message. Previous files are in <profile>/backups/bot-forge/ |
delete_agent refuses |
By design: set allow_delete: true, or run hermes profile delete <name> yourself |
Uninstall
hermes plugins disable bot-forge
hermes plugins remove bot-forge
Bots you created stay. Remove one with hermes profile delete <name>.
Development
python -m unittest discover -s tests
hermes plugins validate .
hermes plugins doctor .
Thanks
People outside this repo who found things in it, and what they found:
- @samideckers-cmd — that
ask_agentforwarded the caller's whole environment to a different Bot, so a gateway holding the main profile's.envhanded those secrets to a Bot with its own credentials (#22, shipped in v0.15.2). Also reported, in #21 on 26 September, that bare in-package imports break when Hermes loads the plugin as a package — five days before the same bug shipped in v0.15.0 and dropped all 14 tools. Both fixes reachedmainthrough rebuilt branches, so git records them under the maintainer's name; the work is theirs. - @teknium1 — the catalog review that caught the v0.15.0 load failure before it was listed, an SMTP path that could send a password in cleartext, mail switches that silently did nothing, adoption writing into every profile on load, and a credential store shared between profiles by symlink (#28 and the fixes in #29).
- @sealca — reuse-first provisioning and multiplex readiness (#24).
License
MIT © Bikash Joshi
