Your friendly neighborhood OpenProject agent
A standalone "OpenProject copilot" that uses the pi agent harness + LLM of your choice to automate various tasks.
Chat with it throughout the OpenProject interface and let it work in the background!
AI tools help us design and implement faster. Nevertheless, outside that creative process, there is still some inevitable legwork: checking out branches, running local AI coding assistants by hand, and managing pull requests.
Use OPilot to remove that friction from your development workflow. It can automate anything from simple bug-fixing to entire E2E product delivery. Just chat with @OPilot in OpenProject and GitHub PRs!
- Prototype solutions: Generate auto-correcting code prototypes via
@OPilot build. Refine the code by chatting with OPilot Bot within the PR. - Take ownership: Run
gh adopt <pr-id>to make the prototypes your own, thus cleanly transferring ownership. - General PR assistance: Tag
@OPilotin any upstream PR with questions. These can relate to code as well as the product context.
- Automate product development: Use the experimental
pdproduct development pipeline to deliver entire features end-to-end, using OpenProject as the work tracking backend.
- Refine work packages: Discuss work packages in the chat via free-form chatting or preset commands like
@OPilot grill. - Create work packages: Turn a suggestion made in a comment into its own work package(s) with
@OPilot create wp <instructions>. OPilot builds the package content, creates them in the same project, and relates them back. - [Enterprise] Run project-wide discovery: Ask
@OPilotanything about the reachable projects' data -- it will leverage the instance's MCP server to give you a fresh answer.
- Requirements
- Quick start
- Bird's-eye view
- Interface
- Reviewing & pushing
- Configuration
- Development
- TODO
- Docker
- LLM (inference) URL & key: OpenRouter or your own OpenAI-compatible server (see Models)
- GitHub auth token for a permission-less contributor account (e.g. opilot-agent)
- OpenProject API token for a sufficiently restricted bot account (e.g. Chomper Agent on Community)
- At least "read work package" and "write comment" is required; more permissions (e.g. "create work package") may also unlock optional features (e.g. work package creator)
git clone https://github.com/opf/opilot
cd opilot
docker compose build
# Create .env based on .env.example OR just leave it to the first startup wizardScans for new activity on OpenProject WPs + its own GitHub PRs, and acts on @OPilot mentions
./opilot agentFallback interface: Same functionality as the agent, but runnable via the CLI & adding some extras.
Some examples:
# Use the built-in OpenProject mini-SDK
./opilot op doc list
./opilot op wp create --project "COMMS" --subject "Hi"
# Use the software development commands
./opilot dev build COMMS-123
# Invoke the opilot chatting interface locally
./opilot chat
# See configured models and their token usage stats
./opilot usage ┌─────────────────┐
│ ./opilot │
│ (shell wrapper) │
└────────┬────────┘
│
│ docker compose run
│
┌── Docker ─────────┼────────────────────────────────────────────────────────────────────────────────┐
│ ▼ │
│ ┌─ runner container ────────────────┐ ┌─ harness container ───────────────┐ │
│ │ │ │ │ │
│ │ Ruby 4.0 script │ json │ Node.js server (:47291) │ │
│ │ * Pulls WP content from OP API │◀──────▶│ POST / -> `pi --mode json` │ │
│ │ * Manages metadata in .opilot/ │ │ volumes: .opilot/ │ │
│ │ * WP metadata mirror │ │ │ │
│ │ * Plan files │ │ │ │
│ │ * Draft PR data │ └──────────────┬────────────────────┘ │
│ │ * Pushes branches, opens PRs │ │ │
│ │ * Delegates chat to the LLM │ │ │
│ └───────┬──────────────────┬────────┘ │ │
│ │ │ ┌──────┴───────────────────┐ │
│ │ │ │ │ │
│ │ │ ▼ ▼ │
│ │ │ ┌─ opgw ─────────────┐ ┌─ authgw ───────────────┐ │
│ │ │ │ * attach the OP │ │ * attach real API key │ │
│ │ │ │ API token │ │ * limit access to │ │
│ │ │ │ * read-only ops │ │ inference API-only │ │
│ │ │ │ │ │ │ │
│ │ │ └───────┬────────────┘ └────────────┬───────────┘ │
│ │ │ │ │ │
└─────────┼──────────────────┼─────────────────────────┼──────────────────────────────┼──────────────┘
▼ ▼ │ ▼
┌──────────────┐ ┌─────────────────┐────┐ │ ┌─────────────────────────┐
│ GitHub API │ │ OpenProject API │/mcp│ ◀─────────┘ │ Inference API: │
└──────────────┘ └─────────────────┘────┘ │ OpenRouter, or your │
│ own OpenAI-compatible │
│ endpoint │
└─────────────────────────┘
The harness container processes untrusted text (work package descriptions and comments), so it is locked down: no host/LAN exposure, no network egress at all except authgw and opgw, an isolated API key, writes confined to /repos (and
never into a .git/ directory), Bash confined to read-only git, resource caps,
a hardened container, and everything ships only as a draft PR for human
review.
Happens via a dedicated unprivileged bot account (such as opilot-agent), with no access to the canonical repo. It forks, pushes to the fork, and opens cross-repo draft PRs.
You may easily transfer ownership of a prototype PR generated by the agent.
Set up the following alias:
gh alias set adopt …
gh alias set adopt '!set -e
v() { gh pr view "$1" --json "$2" -q ".$2"; }
branch="$(v "$1" headRefName)"; base="$(v "$1" baseRefName)"; num="$(v "$1" number)"
fork="$(gh pr view "$1" --json headRepositoryOwner,headRepository --template "{{.headRepositoryOwner.login}}/{{.headRepository.name}}")"
git fetch origin "$base"
git fetch "https://github.com/$fork.git" "$branch"
git checkout -B "$branch" FETCH_HEAD
git rebase "origin/$base" -x "git commit --amend --no-edit --reset-author"
git push origin "$branch"
body="$(v "$1" body | sed "/<!-- opilot:banner -->/,/<!-- \/opilot:banner -->/d; s#hxxp://#http://#g; s#hxxps://#https://#g")"
body="Adapted from #$num.
$body"
url="$(gh pr create --draft --head "$branch" --base "$base" --title "$(v "$1" title)" --body "$body")"
gh pr comment "$1" --body "Adopted in $url."
echo "New PR (yours): $url"'Then, from inside your OpenProject repo, run this:
gh adopt 42 # or paste the PR URLIt does the following:
- Refreshes the code branch from upstream
- Rewrites commit ownership to you
- Pushes a new branch to the upstream repo
- Opens a draft PR for the new branch.
- Closes the original PR.
Everything lives in .env, which the first run writes interactively. See .env.example for the full documentation of variables.
OPilot harness reaches the inference API through authgw, a small gateway container that is the harness's only route out. Configure the taget API & auth via environment variables.
Point OPILOT_INFERENCE_URL at any OpenAI-compatible server — vLLM, Ollama,
llama.cpp, TGI, LM Studio — and give the models a provider prefix of your
choosing:
OPILOT_INFERENCE_URL=http://10.0.0.5:8000/v1
OPILOT_INFERENCE_KEY= # empty if the server needs none
OPILOT_MODEL_HEAVY=local/qwen2.5-coder:32b
OPILOT_MODEL_LIGHT=local/qwen2.5-coder:7b
The test suite runs inside the runner container so the Ruby version and gem environment match production exactly.
Run all tests:
docker compose run --no-deps --rm runner bundle exec rakeRun a single file:
docker compose run --no-deps --rm runner bundle exec ruby -Itest test/opilot/agent_test.rbThe suite uses Minitest (ships with Ruby) and WebMock for HTTP stubs. No network calls are made during the test run.
The roadmap lives in TODO.md.