Skip to content

galderic/charles

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Charles Orchestrator (MVP)

Event-driven orchestrator that pulls tickets from a file, runs Agent X (dev), opens a GitHub PR, runs Agent Y (review), merges on success, and advances to the next ticket.

Features

  • File-backed ticket source (.charles/tickets.json)
  • Persistent orchestrator state machine (.charles/state.json)
  • GitHub webhook subscription endpoint (/github/webhook)
  • Pluggable Dev/Review agents (default model config: gpt-5.3-codex)
  • One-ticket-at-a-time processing
  • Startup warning when no PR-triggered GitHub Actions workflow is detected
  • Merge gate supports reviewer APPROVE or review comment marker (PR reviewed by ...)

Quickstart

1) Install dependencies

export PYTHONPATH=src
python -m pip install -e ".[dev]"

2) Configure settings files

For multi-project usage, keep shared defaults and secrets in a global config directory:

mkdir -p ~/.config/charles
cp config/default.toml ~/.config/charles/default.toml
cp .env.example ~/.config/charles/.env

Then add per-project overrides only where needed:

mkdir -p .charles
cp config/local.example.toml .charles/local.toml

Then edit:

  • ~/.config/charles/default.toml for shared defaults across repositories
  • ~/.config/charles/.env for shared secrets (GITHUB_TOKEN, GITHUB_WEBHOOK_SECRET)
  • .charles/local.toml only for project-specific overrides (for example github.base_branch, custom commands)

Charles always infers GitHub owner/repo from git remote get-url origin (must point to github.com). Base branch defaults to origin/HEAD (for example main/master), with fallback to main. Default agent commands use package-installed entrypoints, so they work across repositories.

Config precedence is:

  1. Environment variables
  2. Project .env
  3. Global ~/.config/charles/.env
  4. Project .charles/local.toml
  5. Project config/default.toml
  6. Global ~/.config/charles/local.toml
  7. Global ~/.config/charles/default.toml
  8. Built-in defaults

3) Start server

python -m orchestrator

You can also run:

charles-orchestrator

If your repo is missing PR-triggered CI workflow files, you can scaffold one:

charles-orchestrator --create

This creates .github/workflows/charles-pr-checks.yml when no PR-triggered workflow is found locally. Commit and push that file so GitHub starts sending successful check_run/check_suite events for PRs.

4) Trigger next ticket

curl http://localhost:8080/tick

5) Configure GitHub webhook

Subscribe repository webhook to:

  • URL: http://<host>:8080/github/webhook
  • Events: pull_request, pull_request_review, pull_request_review_comment, issue_comment, check_suite, check_run
  • Secret: same as GITHUB_WEBHOOK_SECRET

6) Start ngrok tunnel to the orchestrator server

Run ngrok against the orchestrator app port (default 8080):

ngrok http 8080

Important:

  • Use 8080 (or your configured ORCH_PORT) as the ngrok target.
  • Do not run ngrok http 4040. Port 4040 is ngrok's local inspector/API, not your app.

7) Auto-update webhook URL for local tunnels

When your local tunnel URL changes, run:

charles-update-github-webhook --from-ngrok

This reads the current HTTPS URL from ngrok's local API (http://127.0.0.1:4040/api/tunnels) and updates the GitHub webhook to:

https://<current-tunnel-domain>/github/webhook

If using cloudflared (or any other tunnel), pass URL explicitly:

charles-update-github-webhook --public-url https://your-subdomain.trycloudflare.com

8) Reset failed tickets back to queued

Instead of editing JSON manually, reset failed tickets in the default runtime file (.charles/tickets.json):

charles-reset-failed-tickets

You can still pass --tickets-file explicitly for non-default locations.

Configuration

Global defaults file (~/.config/charles/default.toml) and optional project files (config/default.toml, .charles/local.toml) use this structure:

[orchestrator]
model = "gpt-5.3-codex"
host = "0.0.0.0"
port = 8080
log_level = "INFO"

[github]
# Optional override. Defaults to origin/HEAD (fallback "main").
# base_branch = "main"

[agents]
dev_command = "charles-dev-agent-codex"
review_command = "charles-review-agent-claude"
proposal_eval_command = "charles-proposal-eval-codex"
# Optional: only accept review events/comments from this login.
# reviewer_login = "your-claude-bot-login"

When reviewer identity matches PR author, GitHub rejects APPROVE. Charles falls back to COMMENT and also posts a PR conversation comment PR reviewed by <reviewer>. Merge gating accepts either an APPROVE review or that marker comment.

GitHub owner/repo are inferred from git remote get-url origin when origin points to github.com. Base branch is inferred from refs/remotes/origin/HEAD unless overridden.

Runtime files are always repo-local:

  • .charles/tickets.json
  • .charles/state.json

Supported env vars (GITHUB_TOKEN, GITHUB_BASE_BRANCH, GITHUB_WEBHOOK_SECRET, ORCH_MODEL, ORCH_HOST, ORCH_PORT, LOG_LEVEL, DEV_AGENT_COMMAND, REVIEW_AGENT_COMMAND, PROPOSAL_EVAL_COMMAND, REVIEWER_LOGIN).

Optional path override env vars:

  • ORCH_GLOBAL_CONFIG_FILE, ORCH_GLOBAL_LOCAL_CONFIG_FILE
  • ORCH_CONFIG_FILE, ORCH_LOCAL_CONFIG_FILE
  • ORCH_GLOBAL_ENV_FILE
  • ORCH_ENV_FILE

API

  • GET /healthz -> health check
  • GET /tick -> trigger orchestrator progress from idle
  • POST /github/webhook -> GitHub event ingestion

Notes

  • This is an MVP with explicit interfaces for swapping TicketSource (e.g., Jira/Trello) and agent adapters.
  • In production, use GitHub App auth, stronger retry policies, and robust deduplication for webhook deliveries.

About

My own event driven AI agents orchestrator

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages