Event system for AI CLI agents
Project description
trigr
AI CLI coding agents (Claude Code, Codex, Gemini CLI) are reactive — they only work when a human types a message. They have no native event system, no triggers, no way to react to external events autonomously.
trigr fixes that. It's a lightweight event system that delivers events into a running agent session. The agent calls trigr watch, goes to sleep, and wakes up when something happens.
Agent runs trigr watch (background task or blocking)
→ blocks until event
→ event arrives → prints JSON → exits
→ agent wakes up, processes event
→ agent runs trigr watch again
→ "asleep" until next event
One tool. One pattern. Works with any agent.
Install
uv tool install trigr
Quick Start
# Initialize trigr in your project
trigr init
# Terminal 1: watch for events (auto-starts server)
trigr watch
# Terminal 2: send an event
trigr emit hello --data '{"msg": "world"}'
# → Terminal 1 prints the event JSON and exits
Agent Integration
Claude Code
trigr watch runs as a background task — the agent can chat with the user while waiting for events. When an event arrives, the background task exits and the agent gets notified.
# In a Claude Code skill or agent instruction:
# "Run trigr watch as a background task. When it completes, process the event and restart."
trigr watch --timeout 3600
Codex CLI / Gemini CLI
trigr watch runs as a blocking call. The agent waits for the next event, processes it, and restarts the loop.
| Agent | How trigr watch runs | Background chatting? |
|---|---|---|
| Claude Code | Background task | Yes |
| Codex CLI | Blocking call | No (not needed) |
| Gemini CLI | Blocking call | No |
Event Sources
trigr supports three ways to produce events:
1. Manual / Programmatic
Anything can POST events — other terminals, scripts, webhooks, other agents:
trigr emit code_review --data '{"pr": 42, "repo": "myapp"}'
2. Pollers
Shell commands that run on an interval. If stdout is non-empty, it becomes an event:
[pollers.check-inbox]
interval = 60
command = "python check_mail.py"
3. Cron Jobs
Shell commands on a cron schedule:
[crons.daily-summary]
cron = "0 9 * * *"
command = "python daily_report.py"
Pollers and cron commands are language-agnostic. Write a script in any language that prints JSON to stdout. trigr runs it on schedule.
Delayed Events
The agent can schedule its own future events — perfect for follow-ups:
# "Follow up in 48 hours if no response"
trigr emit followup --data '{"candidate": "C-4821"}' --delay 48h
# Supported units: s (seconds), m (minutes), h (hours), d (days)
trigr.toml
Project-local configuration. Created by trigr init.
[server]
host = "127.0.0.1"
port = 9374
[pollers.check-inbox]
interval = 60
command = "python check_mail.py"
[crons.morning-sync]
cron = "0 9 * * *"
command = "echo '{\"type\": \"morning\"}'"
Add jobs from the CLI:
trigr add inbox-check --interval 60 --command "python check_mail.py"
trigr add daily-report --cron "0 9 * * *" --command "python report.py"
Commands
| Command | Description |
|---|---|
trigr init |
Create trigr.toml in the current directory |
trigr serve [-f] |
Start server (detached by default, -f foreground) |
trigr watch [--timeout 300] |
Block until event, print JSON, exit |
trigr emit <type> [--data '{}'] [--delay 10s] |
Push event to queue |
trigr add <name> -c "cmd" (--interval N | --cron "...") |
Add poller/cron to config |
trigr status |
Show server state |
How It Works
trigr serve → FastAPI server + APScheduler
├── POST /emit (push events to priority queue)
├── GET /next (long-poll, blocks until event ready)
└── GET /status (queue depth, registered jobs)
Events sit in an in-memory priority queue sorted by delivery time. GET /next blocks until an event whose fire_at has passed, then returns it as JSON.
The server auto-starts when you run trigr watch or trigr emit. A .trigr.pid file lives next to trigr.toml, so multiple projects can run independent servers on different ports.
Poller Output Format
Poller commands should print JSON to stdout. If the JSON has a type field, it's used as the event type. Otherwise it defaults to poller.<name>.
# Poller script example
echo '{"type": "new_email", "data": {"from": "jane@example.com", "subject": "Re: Role"}}'
If stdout is empty, no event is created. This lets pollers silently skip cycles when nothing is new.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file trigr-1.0.1.tar.gz.
File metadata
- Download URL: trigr-1.0.1.tar.gz
- Upload date:
- Size: 28.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a27c5ce0a518784295cac44c8b8b345bdb10cb147df3621d55b007a6efa83a4
|
|
| MD5 |
359a1c11fb2b894d582dafd4d2447f45
|
|
| BLAKE2b-256 |
fad0dd5a26c3df231a4e24d850d73c929531f64e01b6e8f9776b323407332af8
|
File details
Details for the file trigr-1.0.1-py3-none-any.whl.
File metadata
- Download URL: trigr-1.0.1-py3-none-any.whl
- Upload date:
- Size: 10.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d33fe1cdc774bb0248b2277d9379c632adb0bbcee7b16abae6148266b4e5610
|
|
| MD5 |
0351bc115cffcf28abc553bb94cd29f4
|
|
| BLAKE2b-256 |
60e501917ca24cd418dc0c9395ea18c99b3704f502db03a0c18e468333a90eeb
|