Local-first orchestrator for headless coding agents across multiple GitHub repos
Project description
ctrlrelay
Local-first orchestrator for headless coding agents across your GitHub repos. Watches for assigned issues, runs a dev pipeline in an isolated git worktree, opens a PR, and asks you on Telegram when it gets stuck.
Table of Contents
About
Headless coding agents are great interactively. Running them across half a dozen repos without staring at a terminal is a different problem: who schedules the runs, who watches for new work, who hands you the "I'm blocked, what do you want?" question, who tracks the PR until it merges.
ctrlrelay is a small daemon that does all of that on your laptop. It
is local-first on purpose: no server, no queue, no multi-tenant anything.
Your agent credentials, your GitHub credentials, your repos, your
machine.
Today ctrlrelay ships with a Claude Code (claude -p) backend. The
orchestrator layer — worktrees, state DB, scheduler, Telegram bridge —
is agent-agnostic, and plug-in backends for other headless coding agents
are on the roadmap (see Roadmap).
Features
- Issue poller. Detects issues assigned to you across every configured repo, spawns a dev session in a dedicated git worktree, and opens a PR.
- Telegram bridge. When a session hits a blocking question, the bridge relays it to you as a DM and resumes the session once you reply.
- PR watcher. Tracks the opened PR to merge and closes the loop with a Telegram notification.
- In-process scheduler (APScheduler). Runs periodic jobs inside
the poller daemon. Ships with a
secopsjob that reviews Dependabot alerts and PRs daily at 6am; cron expressions follow standard Vixie 5-field semantics (Sun=0, DOM-OR-DOW). - Checkpoint protocol. The agent writes a structured state file at the end of every session so the orchestrator knows whether it succeeded, failed, or is blocked on input. Agent backends implement this protocol to integrate.
- Cross-platform supervision. launchd (macOS) and systemd (Linux)
examples in
docs/operations.md. One codebase, identical behavior.
How it works
┌───────────────┐
│ GitHub API │
└───────┬───────┘
│ (poll: issues assigned to me)
┌───────▼───────┐ ┌──────────────┐
│ poller daemon │◄────────┤ APScheduler│
│ (launchd / │ │ (secops cron)│
│ systemd) │ └──────────────┘
└───┬───────┬───┘
new issue │ │ blocked session
│ │
┌──────────▼──┐ ┌─▼───────────────────┐
│ dev pipeline│ │ Telegram bridge │
│ in worktree │ │ (socket ↔ bot API) │
│ agent CLI │ └──────────┬──────────┘
└──────┬──────┘ │
│ PR opened │ DM you
▼ ▼
┌────────────┐ ┌─────────┐
│ PR watcher │ │ You │
└────────────┘ └─────────┘
Under the hood it's Python + asyncio + sqlite for state. The agent
is invoked as a subprocess (today: claude -p), and GitHub is accessed
via the gh CLI. No web server, no queue, no database dependency — just
a launchd/systemd-supervised daemon.
Getting started
Prerequisites
- Python 3.12+
- git 2.20+
- The
ghCLI, authenticated (gh auth login) — used for all GitHub API calls. - A headless coding agent backend. Today that means the
claudeCLI, authenticated (claude auth login). Future backends will document their own setup. - (Optional, for the
secopspipeline) thecodexCLI, authenticated. The secops pipeline invokescodex reviewas an independent reviewer for the agent's output; you can disable it by settingcode_review.method: "none"in your config if you prefer to skip the review step. - (Optional) a Telegram bot token if you want the bridge — see Telegram bridge docs.
Installation
From PyPI (once published):
pip install ctrlrelay
# or: uv pip install ctrlrelay
From source (current path while in alpha):
git clone https://github.com/AInvirion/ctrlrelay.git
cd ctrlrelay
uv pip install -e . # or: pip install -e .
Quick start
# Copy and edit the example config:
cp config/orchestrator.yaml.example config/orchestrator.yaml
# Validate it:
ctrlrelay config validate
# Run the dev pipeline against an issue you're assigned:
ctrlrelay run dev --issue 42 --repo your-org/your-repo
# Or start the poller to auto-process newly assigned issues + run the
# scheduled secops sweep daily at 6am:
ctrlrelay poller start # daemonizes; returns the terminal
ctrlrelay poller status # verify it's running
Run as a supervised daemon (launchd on macOS / systemd on Linux) — see Operations.
Documentation
- Getting started
- Configuration
- Telegram bridge
- Feedback loop
- CLI reference
- Operations (launchd / systemd / scheduled jobs)
- Architecture
- Development
Roadmap
- Multi-agent backend support. The agent dispatcher is the seam we
intend to widen so
ctrlrelaycan drive alternative headless coding agents (e.g. OpenAI Codex CLI, OpenCode, Hermes) alongside Claude Code. Each backend will implement the same checkpoint protocol and be selectable per repo via config. - Additional scheduled jobs. The in-process scheduler already has
secops; follow-ups include a weekly activity summary and a stale- session reaper. - Dashboard mode. An optional, opt-in heartbeat push to a hosted dashboard for operators running the daemon across many machines.
Contributing
We welcome contributions from the community! Please read our Contributing Guidelines before submitting a pull request, and abide by our Code of Conduct.
First-time contributors will be prompted by the CLA Assistant bot to sign the Contributor Assignment Agreement in-PR — it's a one-time, one-comment step.
Security
If you discover a security vulnerability, please follow our Security Policy. Please do not file public GitHub issues for security reports — open a private advisory instead.
License
This project is licensed under the Apache License 2.0 — see the LICENSE file for details.
Copyright (c) 2026 AInvirion LLC. All Rights Reserved.
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 ctrlrelay-0.2.0.tar.gz.
File metadata
- Download URL: ctrlrelay-0.2.0.tar.gz
- Upload date:
- Size: 239.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d8b4d1b42db43f490756c35dc1b9aa87ebc60373e6c002cace62e74f5a37487
|
|
| MD5 |
dfb13d7118e99ca8f5fd364af1d5a29c
|
|
| BLAKE2b-256 |
4f41e135abfc15c3608f2a948a607e36f60434c7ef6e8d279a6e681abed21e50
|
Provenance
The following attestation bundles were made for ctrlrelay-0.2.0.tar.gz:
Publisher:
publish.yml on AInvirion/ctrlrelay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctrlrelay-0.2.0.tar.gz -
Subject digest:
9d8b4d1b42db43f490756c35dc1b9aa87ebc60373e6c002cace62e74f5a37487 - Sigstore transparency entry: 1396285953
- Sigstore integration time:
-
Permalink:
AInvirion/ctrlrelay@1936851ba7671c4e23b02939df282661c8cfa76b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/AInvirion
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1936851ba7671c4e23b02939df282661c8cfa76b -
Trigger Event:
release
-
Statement type:
File details
Details for the file ctrlrelay-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ctrlrelay-0.2.0-py3-none-any.whl
- Upload date:
- Size: 125.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d08864d49c53ce8d0403d292d88121e357ecbc2622a93aa14f9c31c35b6cf36
|
|
| MD5 |
dc488fb921c3e5ad7fa2abd4663f024f
|
|
| BLAKE2b-256 |
06447707b5eec46c4b2a927cdf16166b818121de74b26abccc39180734990287
|
Provenance
The following attestation bundles were made for ctrlrelay-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on AInvirion/ctrlrelay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctrlrelay-0.2.0-py3-none-any.whl -
Subject digest:
9d08864d49c53ce8d0403d292d88121e357ecbc2622a93aa14f9c31c35b6cf36 - Sigstore transparency entry: 1396285961
- Sigstore integration time:
-
Permalink:
AInvirion/ctrlrelay@1936851ba7671c4e23b02939df282661c8cfa76b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/AInvirion
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1936851ba7671c4e23b02939df282661c8cfa76b -
Trigger Event:
release
-
Statement type: