Wake your AI agent when something happens, not on a timer.
galvanize watches the real world (new mail in an inbox, a file landing in a folder, a git commit, a webhook call, an event from a script) and wakes a fresh AI agent session the moment one of those things occurs, with a prompt you wrote. Until now, the only event an agent could see was the clock: "trigger me when X" always became an hourly cron poll, because polling was the only surface the agent had. galvanize puts real event triggers directly into the agent's own tool list (native plugin for Hermes, MCP server for Claude Code and Codex), so when you say "wake me when resumes land", the agent wires an actual push trigger instead. Passwords go to your OS keyring, each trigger starts watching in seconds, and results arrive wherever you asked (Telegram, Discord, or just the log).
Install
Works on Windows, macOS, and Linux (Python 3.10+). No cloning needed — one command installs straight from GitHub:
pipx install "git+https://github.com/jarvis959/galvanize.git"
# or: uv tool install --from "git+https://github.com/jarvis959/galvanize.git" galvanize
# or: pip install git+https://github.com/jarvis959/galvanize.git
Then run setup once:
galvanize init
init enables the Hermes webhook platform (backs up your config first), installs the agent plugin, registers the daemon to start at login, and auto-registers the MCP tool surface into Claude Code / Codex if it finds them on this machine. Everything is confirmed on screen and reversible; --yes accepts defaults.
After that, the whole surface is five verbs:
galvanize add folder ~/watch --wake hermes # + a live test fire
galvanize add imap you@gmail.com --wake hermes # app-password to keyring
galvanize status # watching? last fire, errors, health
galvanize doctor # deep health check
galvanize test cad-drops # inject a synthetic event through the real path
Why
Every "trigger me when X" conversation defaults to an hourly cron poller, because polling is the only surface the agent can see. galvanize puts event triggers in the agent's own tool list, so when you say "wake me when resumes land", the agent wires a real push trigger instead of a poll job.
- Fresh sessions, not thread injections. Each event spawns a clean one-shot run: no context pollution, results delivered where you asked.
- Management everywhere cron is managed. Dashboard
/triggerstab,/triggersslash command,hermes triggersCLI, agent tools, plusgalvanize status/doctor/daemon: all surfaces, one ops core. - Push email that survives real life. IDLE with 25-min re-arm, UID dedupe, reconnect catch-up, keyring-held credentials, and a
migrate hermes-croncommand that converts your existing email pollers. - Zero-inbound-port webhooks. Optional user-owned Cloudflare Worker relay (
relay/worker.js): services POST to your URL, your laptop pulls the queue.
Working with each harness
Hermes
galvanize init does the setup: enables platforms.webhook in your config.yaml (backup saved first), pip-installs the package into the interpreter Hermes runs in, copies the plugin into ~/.hermes/plugins and enables it. Restart the Hermes gateway once to load the webhook platform; the trigger_* tools appear in new sessions. From then on, "wake me when a file lands in ~/cad-drops" creates a real trigger in conversation.
Claude Code / Codex
init writes the MCP server entry into ~/.claude.json / ~/.codex/config.toml automatically when it finds them (Codex gets default_tools_approval_mode = "approve" pre-declared, as newer Codex builds otherwise hide the tools). New session: trigger_add and friends are in the tool list. Wake presets:
galvanize add folder ~/inbox --wake claude # claude -p "{prompt}"
galvanize add folder ~/inbox --wake codex # codex exec (sandbox pre-declared)
galvanize add git-hook ~/code/myrepo --wake codex # wake Codex on commits
Any other CLI agent
galvanize add folder ~/watch --wake shell --command 'myagent run "{prompt}"'
Daily use
Triggers the agent creates itself with its trigger_add tool are the primary path; the CLI is the no-agent fallback. Both write the same ~/.galvanize/triggers.yaml, and the dashboard tab manages what either creates (creation stays conversation-first by design).
Development
git clone https://github.com/jarvis959/galvanize && cd galvanize
python -m venv .venv && .venv/bin/pip install -e ".[dev]" # Scripts/ on Windows
.venv/bin/pytest # unit suite; live-lane + docker tests skip cleanly
The live Hermes-lane test runs inside a Hermes checkout's venv (pytest tests/test_live_hermes_lane.py); the IMAP suite drives a GreenMail container and skips when Docker is unavailable.
MIT licensed. Built for the Hermes ecosystem; architecture is harness-neutral.
Metadata
Release files for galvanize 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| galvanize-0.2.0.tar.gz | 1.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| galvanize-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.8 MB
Release files / galvanize-0.2.0.tar.gz
| Download URL | galvanize-0.2.0.tar.gz |
|---|---|
| Size | 1.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2a88b9ef7f1eb91f88434290ea6f89711fdf16cec266737c563b038c812569f6
|
|
BLAKE2b-256 checksum How to use checksums |
dddc7d7834eae68bfaae1f98223b88a988cc6074705dff2198a11e76c531c802
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / galvanize-0.2.0-py3-none-any.whl
| Download URL | galvanize-0.2.0-py3-none-any.whl |
|---|---|
| Size | 68.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cb41c2ba1db800b102de1fb3cde5aa749c520e4832a598965835f9a53db5930a
|
|
BLAKE2b-256 checksum How to use checksums |
bb656b96cc421d4ae9a4383217ec52fb27e7c3c2d56c70e8022d96b78a4304d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|