An autonomous task orchestrator that delegates work to AI coding agents (Claude, Codex, OpenCode, Kimi, MiniMax). Runs as a background service, manages isolated worktrees, syncs with GitHub Issues, and handles the full task lifecycle from routing to PR creation.
- Multi-agent support — Route tasks to Claude, Codex, OpenCode, Kimi, or MiniMax based on task complexity
- Multi-project — Manage multiple repositories from a single service
- GitHub Issues integration — Two-way sync with GitHub Issues as the source of truth
- GitHub Projects V2 — Automatic project board column sync on status changes
- Isolated worktrees — Each task runs in its own git worktree, never touching the main repo
- Live session streaming — Watch agents work in real-time via
orch stream(all sessions) ororch stream <task_id> - Control session — Conversational ops assistant via
orch chat— ask about tasks, create new ones, check status in natural language - Internal tasks — SQLite-backed tasks for cron jobs and maintenance (no GitHub issue clutter)
- Job scheduler — Cron-like scheduled tasks with native Rust cron matching
- Automatic PR creation — Branches pushed, PRs created, and comments posted automatically
- Complexity-based routing — Router assigns
simple|medium|complexand config maps to models - Agent memory — Learnings persist across retries so agents don't repeat mistakes
- Per-task artifacts — Organized per-repo, per-task, per-attempt directory structure
brew tap gabrielkoerich/homebrew-tap brew install orchcd /path/to/your/project orch initThis creates:
~/.orch/config.yml— global configuration (shared defaults + project registry).orch.yml— project-specific configuration (in your project root)
orch service startOr use Homebrew services:
brew services start orch# Create an internal task (SQLite only, no GitHub issue) orch task add "Fix authentication bug" --body "Users can't login with OAuth" # Create a GitHub issue task orch task add "Update README" --labels "documentation,good-first-issue"The service automatically:
- Routes tasks to the best agent based on content
- Creates isolated worktrees
- Runs the agent in a tmux session
- Pushes branches and creates PRs
- Updates GitHub issue status and project board
Orch requires GitHub authentication to sync with issues and create PRs. Three methods are supported:
# Set as environment variable export GH_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx" # Or configure in ~/.orch/config.yml gh: auth: token: "ghp_xxxxxxxxxxxxxxxxxxxx"Better audit trails and scoped permissions for team automation:
github: token_mode: github_app app_id: "123456" private_key_path: "/path/to/app-private-key.pem"The orchestrator automatically generates JWTs and refreshes installation tokens before expiry.
The simplest setup — just authenticate once and everything works:
gh auth loginOrch calls gh auth token automatically when GH_TOKEN/GITHUB_TOKEN are not set. This fallback is enabled by default (gh.allow_gh_fallback: true).
To disable it (enforce explicit token configuration):
# ~/.orch/config.yml gh: allow_gh_fallback: falseSee Configuration for details and run orch auth check to verify your setup.
When running as a background service (e.g., brew services start orch), the service process does not inherit your shell environment. Pass the token securely:
Option A — ~/.private file (sourced automatically by runner scripts):
# ~/.private (chmod 600) export GH_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"Option B — launchd EnvironmentVariables in the plist:
<key>EnvironmentVariables</key> <dict> <key>GH_TOKEN</key> <string>ghp_xxxxxxxxxxxxxxxxxxxx</string> </dict>Option C — GitHub App (recommended for teams): no long-lived token needed; the service exchanges a short-lived JWT for an installation token automatically.
Important: Orch never writes
GH_TOKENinto runner scripts on disk. Tokens are injected into the tmux session environment at spawn time and exist only in process memory.
orch serve # Run the orchestrator service (foreground) orch service start # Start background service orch service stop # Stop background service orch service restart # Restart service orch service status # Check service statusorch task list # List all tasks orch task list --status new # Filter by status orch task get <id> # Get task details orch task add "Title" # Create internal task orch task add "Title" --labels "bug,urgent" # Create GitHub issue orch task route <id> # Manually route a task to an agent orch task run <id> # Manually run a task orch task run # Run next available routed task orch task retry <id> # Reset task to new for re-routing orch task unblock <id> # Unblock a blocked task orch task unblock all # Unblock all blocked/needs_review tasks orch task publish <id> # Promote internal task to GitHub issue orch task attach <id> # Attach to running tmux session orch task live # List active agent sessions orch task kill <id> # Kill a running agent session orch task cost <id> # Show token cost breakdown orch task close <id> # Manually mark a task as done orch task close <id> --note "msg" # Mark done with a commentorch project list # List all registered projects orch project add # Register current directory as a project orch project add /path/to/dir # Register a specific project path orch project remove /path # Unregister a projectorch board list # List accessible GitHub Projects V2 boards orch board link <id> # Link current repo to a board by ID orch board sync # Re-discover field IDs and update config orch board info # Show current board configorch job list # List scheduled jobs orch job add "0 9 * * *" "Morning review" # Daily at 9am orch job add "*/30 * * * *" "Check CI" --type bash --command "scripts/ci-check.sh" orch job remove <id> # Remove a job orch job enable <id> # Enable a job orch job disable <id> # Disable a job orch job tick # Run one scheduler tick (for testing)orch init # Initialize orch for current project orch init --repo owner/repo # Initialize with specific repo orch agents # List installed agent CLIs orch metrics # Show task metrics summary (24h) orch stream # Stream ALL running agent sessions orch stream <task_id> # Stream a single task orch log # Show last 50 log lines orch log 100 # Show last N lines orch log watch # Tail logs live orch version # Show version orch config <key> # Read config value (e.g., orch config gh.repo) orch completions <shell> # Generate shell completions (bash, zsh, fish)orch chat # Interactive REPL orch chat "what's running?" # Single message mode orch chat --session ops # Use a named session profile orch chat history # Show recent messages orch chat history --search "bean" # Search past conversationsIn the REPL, use /model to switch models:
orch> /model sonnet # Infer agent (claude) orch> /model minimax:sonnet # Explicit agent:model orch> /model opencode:minimax-m2.5-free # OpenCode with specific model orch> /model # Show current agent:model Shared defaults and project registry. All settings here apply to every project unless overridden.
# Project registry — list of local paths # Each path must contain a .orch.yml with gh.repo projects: - /Users/me/Projects/my-app - /Users/me/Projects/my-lib workflow: auto_close: true review_owner: "@owner" enable_review_agent: false max_attempts: 10 timeout_seconds: 1800 router: mode: "llm" # "llm" (default) or "round_robin" agent: "claude" # which LLM performs routing model: "haiku" # fast/cheap model for classification timeout_seconds: 60 fallback_executor: "codex" model_map: simple: claude: haiku codex: gpt-5.1-codex-mini medium: claude: sonnet codex: gpt-5.2 complex: claude: opus codex: gpt-5.3-codex review: claude: sonnet codex: gpt-5.2 agents: claude: allowed_tools: [...] # Claude Code tool allowlist opencode: permission: { ... } # OpenCode permission config models: [...] # Available models git: name: "orch[bot]" email: "orch@orch.bot"Place in your project root. Values here override global defaults.
# Required — identifies this project on GitHub gh: repo: "owner/repo" project_id: "PVT_..." # optional: GitHub Projects V2 project_status_field_id: "..." # optional project_status_map: # optional backlog: "option-id-1" in_progress: "option-id-2" review: "option-id-3" done: "option-id-4" # Optional overrides workflow: max_attempts: 5 router: fallback_executor: "codex" required_tools: - cargo - bun # Per-project scheduled jobs jobs: - id: code-review schedule: "0 4,17 * * *" task: title: "Code review" body: "Review the codebase for bugs and improvements" labels: [review] enabled: true| Status | Description |
|---|---|
new | Task created, awaiting routing |
routed | Agent assigned, awaiting dispatch |
in_progress | Agent actively working in tmux session |
needs_review | Agent completed, awaiting review agent dispatch (automatic) |
in_review | Review agent actively reviewing the PR |
done | PR merged, worktree cleaned up |
blocked | Requires human attention (max review cycles, agent failures) |
Override the router by adding labels to GitHub issues:
| Label | Effect |
|---|---|
agent:claude | Force Claude executor |
agent:codex | Force Codex executor |
agent:opencode | Force OpenCode executor |
agent:kimi | Force Kimi executor |
agent:minimax | Force MiniMax executor |
complexity:simple | Use simple model tier |
complexity:medium | Use medium model tier |
complexity:complex | Use complex model tier |
no-agent | Skip agent routing (manual task) |
Orch is built in Rust with a modular architecture:
src/ ├── main.rs # CLI entrypoint (clap) ├── control.rs # Control session (orch chat) — context assembly, agent invocation ├── config/ │ └── mod.rs # Config loading, hot-reload, multi-project ├── store.rs # Unified SQLite task store (tasks, metrics, KV, rate limits) ├── parser.rs # Agent response normalization ├── cron.rs # Cron expression matching ├── template.rs # Template rendering ├── tmux.rs # tmux session management ├── security.rs # Secret scanning + redaction ├── home.rs # Home directory (~/.orch/) + per-repo state paths ├── cmd.rs # Command execution helpers with error context ├── cmd_cache.rs # Cached command results ├── repo_context.rs # Per-repo task-local context ├── webhook_status.rs # Webhook health tracking ├── backends/ # External task backends │ ├── mod.rs # ExternalBackend trait │ └── github.rs # GitHub Issues + Projects V2 sync ├── channels/ # Communication channels │ ├── transport.rs # Output broadcasting │ ├── capture.rs # tmux output capture │ ├── notification.rs # Unified notifications │ ├── stream.rs # Live output streaming │ ├── tmux.rs # tmux bridge │ ├── github.rs # GitHub webhooks │ ├── slack.rs # Slack integration │ ├── telegram.rs # Telegram bot │ ├── discord.rs # Discord registration + REST helpers │ └── discord_ws.rs # Discord Gateway websocket (real-time events) ├── cli/ # CLI command implementations │ ├── mod.rs # Init, agents, board, project, metrics, stream │ ├── chat.rs # Control session (REPL, single-message, history) │ ├── task.rs # Task CRUD │ ├── job.rs # Job management │ └── service.rs # Service lifecycle ├── github/ # GitHub API helpers │ ├── cli_wrapper.rs # gh CLI wrapper │ ├── http.rs # Native HTTP client (reqwest, connection pooling) │ ├── token.rs # Token resolution (env, config, gh CLI, GitHub App) │ ├── types.rs # Issue, Comment, Label, PR review types │ └── projects.rs # Projects V2 GraphQL operations └── engine/ # Core orchestration ├── mod.rs # Main event loop, project init, struct defs ├── tick.rs # Core tick phases (sessions, routing, dispatch, unblock) ├── sync.rs # Periodic sync (cleanup, PR review, mentions, skills) ├── review.rs # PR review pipeline (review agent, auto-merge, re-route) ├── cleanup.rs # Worktree cleanup, merged-PR detection, store helpers ├── commands.rs # Owner /slash commands in issue comments ├── tasks.rs # Task manager (internal + external, unified store) ├── router/ # Agent routing (label, round-robin, LLM) │ ├── mod.rs # Router logic and RouteResult │ ├── config.rs # Router configuration │ └── weights.rs # Routing weight signals ├── jobs.rs # Job scheduler + self-review └── runner/ # Task execution ├── mod.rs # Full task lifecycle ├── task_init.rs # Guard checks, worktree setup, invocation building ├── session.rs # tmux session lifecycle and output collection ├── context.rs # Prompt context building ├── worktree.rs # Git worktree management ├── agent.rs # Agent invocation + prompt building ├── agents/ # Per-agent runners (Claude, Codex, OpenCode) ├── response.rs # Response parsing, weight signals ├── response_handler.rs # Success path: commit, push, PR, budget ├── fallback.rs # Error classification and recovery strategies └── git_ops.rs # Auto-commit, push, PR creation Task artifacts are organized per-repo, per-task, per-attempt:
~/.orch/state/{owner}/{repo}/tasks/{id}/ attempts/ 1/ prompt-sys.md # System prompt prompt-msg.md # Task prompt runner.sh # Runner script exit.txt # Exit code stderr.txt # Agent stderr output.json # Agent response result.json # Parsed result (status, summary, etc.) 2/ # Retry attempt ... Task metadata (branch, worktree, agent, model, attempts, pr_number, memory, etc.) is stored in the unified SQLite database at ~/.orch/orch.db, not in per-task JSON files.
git clone https://github.com/gabrielkoerich/orch.git cd orch cargo build --releasecargo nextest run # preferred (matches CI) cargo test # fallback if nextest is not installedInstall nextest: cargo binstall cargo-nextest (requires cargo-binstall).
- Service log:
~/.orch/state/orch.log - Homebrew stdout:
/opt/homebrew/var/log/orch.log - Homebrew stderr:
/opt/homebrew/var/log/orch.error.log
- AGENTS.md — Agent and developer notes
- docs/architecture.md — System architecture and diagrams
MIT