Coding agent that lives in your project management tool
Project description
Polycode
Multi-agent software development automation using CrewAI. GitHub App integration for webhook-driven workflows across multiple repositories.
Table of Contents
- Overview
- Tech Stack
- Prerequisites
- Getting Started
- Architecture
- Environment Variables
- Available Scripts
- CLI Commands
- Testing
- Workflows
- GitHub App Integration
- Docker Deployment
- Troubleshooting
Overview
Polycode automates software development tasks using AI-powered multi-agent workflows. It integrates as a GitHub App to provide seamless automation triggered by issue labels.
Key Features
- Multi-agent CrewAI workflows - Plan, implement, review, test, and verify code
- GitHub App integration - Webhook-driven automation for multi-repo support
- Label-to-flow mapping - Trigger workflows by adding labels to issues
- Iterative development - Ralph Loop with automatic verification and retry
- Context+ integration - Semantic code intelligence via MCP/Ollama
Available Workflows
| Workflow | Label | Description |
|---|---|---|
| Ralph | ralph |
Fast iterative implementation with automated verification |
| Specify | specify |
Feature specification and planning workflow |
Tech Stack
- Language: Python 3.13+
- Framework: CrewAI 1.10+ (multi-agent orchestration)
- Web Server: FastAPI with Uvicorn
- Database: PostgreSQL 16+ with SQLAlchemy/SQLModel
- Queue: Celery with Redis
- Package Manager: uv
- CLI: Typer with Rich
- Real-time: Socket.IO for streaming events
- Deployment: Docker (includes Node.js 22, Bun, Context+)
Prerequisites
- Python 3.13+ (via pyenv, mise, or asdf)
- uv package manager -
curl -LsSf https://astral.sh/uv/install.sh | sh - PostgreSQL 16+ (or Docker)
- Redis (for Celery background jobs)
- GitHub App credentials (for webhook integration)
- Ollama (optional, for Context+ semantic search)
Getting Started
1. Clone and Install
git clone https://github.com/your-org/polycode.git
cd polycode
uv sync
2. Environment Setup
Copy the example environment file:
cp .env.example .env
Configure required variables (see Environment Variables).
3. Database Setup
Using Docker:
docker run --name postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=polycode \
-p 5432:5432 \
-d postgres:16
Using local PostgreSQL:
# macOS
brew install postgresql@16
brew services start postgresql@16
createdb polycode
# Ubuntu
sudo apt install postgresql-16
sudo -u postgres createdb polycode
The database tables are auto-created on first run via SQLAlchemy.
4. Redis Setup
docker run --name redis -p 6379:6379 -d redis:7
Or locally:
# macOS
brew install redis
brew services start redis
# Ubuntu
sudo apt install redis-server
sudo systemctl start redis
5. Start Services
Development (all services):
# Terminal 1: FastAPI webhook server
uv run polycode server webhook --reload
# Terminal 2: Socket.IO streaming server
uv run polycode server socketio --reload
# Terminal 3: Celery worker
uv run polycode worker start --queues celery,default --loglevel=info
# Terminal 4: Flower (Celery monitoring, optional)
uv run polycode worker flower
Quick test:
uv run polycode --help
Architecture
Directory Structure
polycode/
├── src/
│ ├── cli/ # Typer CLI commands
│ │ ├── main.py # Entry point
│ │ ├── server.py # Server management
│ │ ├── worker.py # Celery worker commands
│ │ ├── flow.py # Flow execution
│ │ ├── project.py # GitHub project management
│ │ └── db.py # Database utilities
│ ├── crews/ # CrewAI crew definitions
│ │ ├── base.py # PolycodeCrewMixin with hooks
│ │ ├── plan_crew/ # Story planning
│ │ ├── implement_crew/ # Code implementation
│ │ ├── review_crew/ # Code review
│ │ ├── test_crew/ # Test generation
│ │ ├── verify_crew/ # Build verification
│ │ ├── ralph_crew/ # Ralph loop implementation
│ │ ├── conversation_crew/ # Conversational agent
│ │ └── streaming/ # Real-time event publishing
│ ├── flows/ # CrewAI Flow orchestration
│ │ ├── base.py # FlowIssueManagement base
│ │ ├── protocol.py # Flow protocol definitions
│ │ ├── ralph/ # Ralph Loop flow
│ │ └── specify/ # Specify flow
│ ├── channels/ # Real-time communication
│ │ ├── base.py # Channel base class
│ │ ├── dispatcher.py # Multi-channel dispatcher
│ │ ├── redis/ # Redis pub/sub channel
│ │ └── stream/ # Socket.IO streaming
│ ├── github_app/ # GitHub App integration
│ │ ├── app.py # FastAPI webhook server
│ │ ├── auth.py # JWT authentication
│ │ ├── webhook_handler.py # Event processing
│ │ ├── installation_manager.py
│ │ └── label_mapper.py # Label-to-flow mapping
│ ├── project_manager/ # GitHub project management
│ │ ├── github.py # GitHub API client
│ │ ├── github_projects_client.py
│ │ ├── git_utils.py # Git operations
│ │ ├── flow_runner.py # Flow execution
│ │ └── types.py # Pydantic models
│ ├── persistence/ # Database layer
│ │ ├── postgres.py # SQLAlchemy models
│ │ └── config.py # Database settings
│ ├── gitcore/ # Git worktree operations
│ ├── modules/ # Plugin hook system
│ ├── tools/ # Custom CrewAI tools
│ │ ├── file_read_tool.py
│ │ ├── directory_read_tool.py
│ │ ├── exec_tool.py
│ │ └── agents_md_loader.py
│ ├── tasks/ # Task templates
│ ├── glm.py # GLM JSON LLM wrapper
│ └── bootstrap.py # Application bootstrap
├── task_templates/ # Custom task templates
│ ├── custom_implement.md
│ └── custom_generate_result.md
├── tests/ # Test files
├── docs/ # Documentation
├── Dockerfile
├── entrypoint.sh
├── Makefile
└── pyproject.toml
Request Lifecycle
GitHub Webhook → FastAPI /webhook/github → LabelMapper → Celery Task
↓
FlowRunner.kickoff()
↓
RalphLoopFlow (CrewAI Flow)
↓
PlanCrew → RalphCrew → VerifyCrew
↓
Hook Events (git commit, PR create)
Flow Event System
The hook system enables modular event handling:
| Event | Emitted By | Hook Handler |
|---|---|---|
FLOW_STARTED |
Flow setup | Initialize resources |
STORIES_PLANNED |
PlanCrew | Create checklist items |
STORY_COMPLETED |
Implement step | Commit + push code |
CREW_FINISHED |
Any crew | Log, metrics |
FLOW_FINISHED |
Flow complete | Create PR, cleanup |
CLEANUP |
Flow end | Remove worktree |
Database Schema
-- Flow state persistence
flow_states (
id SERIAL PRIMARY KEY,
flow_uuid VARCHAR(255) NOT NULL,
method_name VARCHAR(255) NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
state_json JSONB NOT NULL
)
-- Async human-in-the-loop
pending_feedback (
id SERIAL PRIMARY KEY,
flow_uuid VARCHAR(255) UNIQUE NOT NULL,
context_json JSONB NOT NULL,
state_json JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL
)
-- Issue tracking
requests (
id SERIAL PRIMARY KEY,
issue_number INTEGER NOT NULL,
request_text TEXT NOT NULL,
status VARCHAR(50) NOT NULL,
commit VARCHAR(255),
created_at TIMESTAMPTZ
)
-- Payment tracking (manual mode)
payments (
id SERIAL PRIMARY KEY,
issue_number INTEGER NOT NULL,
payment_id VARCHAR(255),
amount INTEGER,
currency VARCHAR(10),
payment_method VARCHAR(50),
status VARCHAR(50),
created_at TIMESTAMPTZ,
verified_at TIMESTAMPTZ
)
Environment Variables
Required
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://user:pass@localhost:5432/polycode |
REDIS_HOST |
Redis hostname | localhost |
REDIS_PORT |
Redis port | 6379 |
GITHUB_APP_ID |
GitHub App ID | 123456 |
GITHUB_APP_PRIVATE_KEY |
GitHub App private key (with \n) |
-----BEGIN RSA PRIVATE KEY-----\n... |
GITHUB_APP_WEBHOOK_SECRET |
Webhook secret for signature validation | your-webhook-secret |
LLM Configuration
| Variable | Description | Default |
|---|---|---|
OPENAI_API_KEY |
OpenAI API key | - |
OPENAI_URL_BASE |
Custom OpenAI-compatible URL | - |
MODEL |
Default model identifier | gpt-4o |
Context+ (Optional)
| Variable | Description | Default |
|---|---|---|
OLLAMA_HOST |
Ollama server URL | http://localhost:11434 |
OLLAMA_EMBED_MODEL |
Embedding model | nomic-embed-text |
OLLAMA_CHAT_MODEL |
Chat model for Context+ | gemma2:27b |
Application
| Variable | Description | Default |
|---|---|---|
APP_RUN |
Default run mode | api |
APP_HOST |
Server host | 0.0.0.0 |
APP_PORT |
Server port | 5000 |
LOG_LEVEL |
Logging verbosity | INFO |
DATA_PATH |
Worktree storage path | /data |
SSH_KEY |
SSH private key for git push | - |
Available Scripts
Package Management
uv sync # Install all dependencies
uv add <package> # Add a new dependency
uv add --dev <package> # Add development dependency
uv lock # Update lock file
Linting & Formatting
uv run ruff check . # Check lint errors
uv run ruff check --fix . # Auto-fix lint errors
uv run ruff format . # Format code
uv run pyright # Type checking
Database
uv run polycode db init # Initialize database tables
uv run polycode db reset # Reset database (warning: destructive)
CLI Commands
# Show help
uv run polycode --help
uv run polycode <command> --help
# Server management
uv run polycode server webhook --host 0.0.0.0 --port 5000 --reload
uv run polycode server socketio --host 0.0.0.0 --port 5001
# Celery worker
uv run polycode worker start --queues celery,default --loglevel=info
uv run polycode worker flower --port=5555
# Flow execution
uv run polycode flow run ralph --issue 42 --repo owner/repo
# GitHub project management
uv run polycode project list --installation 12345
uv run polycode project status --issue 42
# Database
uv run polycode db init
uv run polycode db status
Testing
Run Tests
# Run all tests
uv run pytest
# Run specific test file
uv run pytest tests/test_flows.py
# Run single test
uv run pytest tests/test_flows.py::test_ralph_flow_setup
# Run tests matching pattern
uv run pytest -k "ralph"
# Verbose output
uv run pytest -v
# Stop after first failure
uv run pytest --maxfail=1
# With coverage
uv run pytest --cov=src
Test Structure
tests/
├── __init__.py
├── test_flows.py # Flow tests
├── test_gitcore.py # Git operations tests
├── test_github_manager.py # GitHub API tests
├── test_agents_md_loader.py # AGENTS.md loader tests
├── test_project_manager_plugin.py
└── test_retro.py # Retrospective tests
Writing Tests
"""Test module docstring."""
from unittest.mock import MagicMock, patch
from project_manager.github import GitHubProjectManager
from project_manager.types import ProjectConfig
def test_has_label_returns_true_when_label_exists():
"""Test that has_label returns True when the label is present."""
# Arrange
config = ProjectConfig(
repo_owner="testowner",
repo_name="testrepo",
token="fake_token",
)
manager = GitHubProjectManager(config)
mock_repo = MagicMock()
mock_pr = MagicMock()
mock_pr.labels = [MagicMock(name="approved")]
mock_repo.get_pull.return_value = mock_pr
manager.github_client.get_repo = MagicMock(return_value=mock_repo)
# Act
result = manager.has_label(123, "approved")
# Assert
assert result is True
mock_repo.get_pull.assert_called_once_with(123)
Workflows
Ralph Flow (ralph)
Iterative development with automatic verification loops.
Flow Steps:
- Setup - Initialize git worktree, load issue context
- Plan - Decompose task into ordered user stories via
PlanCrew - Implement - Execute each story via
RalphCrew:- Implement code changes
- Run tests
- If tests fail: retry with error context (max 3 attempts)
- If tests pass: commit and proceed to next story
- Verify - Final build and test verification
- Cleanup - Push changes, create PR, remove worktree
Tools Available to Ralph Agents:
| Tool | Description |
|---|---|
FileReadTool |
Read file contents |
FileWriterTool |
Write/modify files |
DirectoryReadTool |
Explore directory structure |
ExecTool |
Execute shell commands (build, test) |
AgentsMDLoaderTool |
Load AGENTS.md context files |
Safety Features:
- Per-story commits (atomic changes)
- Maximum 3 retry iterations per story
- Error context included in retries
- Automatic worktree cleanup
Specify Flow (specify)
Feature specification and planning workflow for larger features.
GitHub App Integration
Setup
-
Create GitHub App
Go to https://github.com/settings/apps/new
Configure:
- App name: Your app name
- Webhook URL: Your server URL +
/webhook/github - Webhook secret: Generate a secure secret
- Permissions:
- Issues: Read & Write
- Pull requests: Read & Write
- Contents: Read & Write
- Projects: Read & Write
- Metadata: Read-only
-
Generate Private Key
After creating the app, generate a private key and add to
.env:GITHUB_APP_PRIVATE_KEY=$(cat private-key.pem | awk '{printf "%s\\n", $0}' | tr -d '\n')
-
Install App
Install the app on target repositories from the app settings page.
-
Create Label Mappings
curl -X POST http://localhost:5000/mappings \ -H "Content-Type: application/json" \ -d '{ "installation_id": 12345, "label_name": "ralph", "flow_name": "ralph_flow", "priority": 0 }'
Available Endpoints
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | App info and status |
/health |
GET | Health check |
/webhook/github |
POST | GitHub webhook handler |
/installations |
GET | List active installations |
/mappings |
GET | List label mappings |
/mappings |
POST | Create label mapping |
Triggering Flows
- Create an issue in a connected repository
- Add the mapped label (e.g.,
ralph) - Webhook triggers flow execution automatically
Docker Deployment
Build Image
make docker_build
# or
docker build -t ghcr.io/xeroc/polycode:latest .
Run Container
# API server
docker run -p 5000:5000 \
-e DATABASE_URL=postgresql://... \
-e REDIS_HOST=redis \
-e GITHUB_APP_ID=123456 \
-e GITHUB_APP_PRIVATE_KEY="-----BEGIN..." \
-e GITHUB_APP_WEBHOOK_SECRET=secret \
-v /data:/data \
ghcr.io/xeroc/polycode:latest api
# Celery worker
docker run \
-e DATABASE_URL=postgresql://... \
-e REDIS_HOST=redis \
-e OLLAMA_HOST=http://ollama:11434 \
-e SSH_KEY="$(cat ~/.ssh/id_rsa)" \
ghcr.io/xeroc/polycode:latest worker
# Socket.IO server
docker run -p 5001:5000 \
ghcr.io/xeroc/polycode:latest socketio
Docker Compose
services:
api:
image: ghcr.io/xeroc/polycode:latest
command: api
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/polycode
- REDIS_HOST=redis
- GITHUB_APP_ID=${GITHUB_APP_ID}
- GITHUB_APP_PRIVATE_KEY=${GITHUB_APP_PRIVATE_KEY}
- GITHUB_APP_WEBHOOK_SECRET=${GITHUB_APP_WEBHOOK_SECRET}
depends_on:
- db
- redis
worker:
image: ghcr.io/xeroc/polycode:latest
command: worker
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/polycode
- REDIS_HOST=redis
- OLLAMA_HOST=http://ollama:11434
- SSH_KEY=${SSH_KEY}
volumes:
- data:/data
depends_on:
- db
- redis
- ollama
socketio:
image: ghcr.io/xeroc/polycode:latest
command: socketio
ports:
- "5001:5000"
environment:
- REDIS_HOST=redis
depends_on:
- redis
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=polycode
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7
volumes:
- redisdata:/data
ollama:
image: ollama/ollama
volumes:
- ollamadata:/root/.ollama
volumes:
pgdata:
redisdata:
ollamadata:
data:
Troubleshooting
Database Connection Issues
Error: could not connect to server: Connection refused
Solution:
- Verify PostgreSQL is running:
docker psorpg_isready - Check
DATABASE_URLformat:postgresql://USER:PASSWORD@HOST:PORT/DATABASE - Ensure database exists:
createdb polycode
Redis Connection Issues
Error: Error connecting to Redis
Solution:
- Verify Redis is running:
redis-cli ping - Check
REDIS_HOSTandREDIS_PORTvalues - For Docker: ensure containers are on same network
Celery Worker Not Processing Tasks
Error: Tasks queued but not executing
Solution:
# Check worker is running
uv run polycode worker start --queues celery,default --loglevel=debug
# Check Flower dashboard
uv run polycode worker flower
# Open http://localhost:5555
GitHub App Authentication Failures
Error: JWT authentication failed
Solution:
-
Verify
GITHUB_APP_IDmatches your app -
Ensure private key has proper newlines:
echo "$GITHUB_APP_PRIVATE_KEY" | head -1 # Should show: -----BEGIN RSA PRIVATE KEY-----
-
Check app is installed on the repository
LLM API Errors
Error: OpenAI API error
Solution:
- Verify
OPENAI_API_KEYis set - Check rate limits
- For custom endpoints, verify
OPENAI_URL_BASE
Context+ Not Working
Error: Context+ MCP connection failed
Solution:
-
Ensure Ollama is running:
ollama serve -
Pull required models:
ollama pull nomic-embed-text ollama pull gemma2:27b
-
Verify Context+ is installed:
bun install -g contextplus -
Test MCP server:
bunx contextplus skeleton .
Git Operations Failing
Error: Permission denied (publickey)
Solution:
-
For Docker: ensure
SSH_KEYenvironment variable is set -
For local: verify SSH key has access to repository
-
Add GitHub to known hosts:
ssh-keyscan github.com >> ~/.ssh/known_hosts
Import Errors
Error: ModuleNotFoundError: No module named 'xxx'
Solution:
# Ensure virtual environment is active
uv sync
# Run with uv
uv run polycode --help
Resources
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 polycoding-1.0.1.tar.gz.
File metadata
- Download URL: polycoding-1.0.1.tar.gz
- Upload date:
- Size: 406.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae3bb43cf5395c392bcfb3f769ca8d961bf7b5012c968c499ea95ded6f7356db
|
|
| MD5 |
adc56ec88395e33248cb7783464d3e7e
|
|
| BLAKE2b-256 |
249f162d36d5e65060f750db40b4fe1ce058b868faafbbdbe43c6b483b1111ec
|
Provenance
The following attestation bundles were made for polycoding-1.0.1.tar.gz:
Publisher:
main.yaml on xeroc/polycode
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polycoding-1.0.1.tar.gz -
Subject digest:
ae3bb43cf5395c392bcfb3f769ca8d961bf7b5012c968c499ea95ded6f7356db - Sigstore transparency entry: 1217979033
- Sigstore integration time:
-
Permalink:
xeroc/polycode@f7bb7a443d58ff6b7f14ebaea6f05488cb7ce8b8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/xeroc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yaml@f7bb7a443d58ff6b7f14ebaea6f05488cb7ce8b8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polycoding-1.0.1-py3-none-any.whl.
File metadata
- Download URL: polycoding-1.0.1-py3-none-any.whl
- Upload date:
- Size: 56.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
759d6f5ca76309f3e6f6cfe3cbfb573f72ac5d33fcf9a67f3a6b7f3a55a1924f
|
|
| MD5 |
762427ab5bd17578e71523a9eeea2647
|
|
| BLAKE2b-256 |
142ca6b505ed0cfc5211e03c0c9112723d9a8c091cb13cddd6e9a336cf14e29c
|
Provenance
The following attestation bundles were made for polycoding-1.0.1-py3-none-any.whl:
Publisher:
main.yaml on xeroc/polycode
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polycoding-1.0.1-py3-none-any.whl -
Subject digest:
759d6f5ca76309f3e6f6cfe3cbfb573f72ac5d33fcf9a67f3a6b7f3a55a1924f - Sigstore transparency entry: 1217979044
- Sigstore integration time:
-
Permalink:
xeroc/polycode@f7bb7a443d58ff6b7f14ebaea6f05488cb7ce8b8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/xeroc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yaml@f7bb7a443d58ff6b7f14ebaea6f05488cb7ce8b8 -
Trigger Event:
push
-
Statement type: