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
Metadata
Release files for polycoding 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polycoding-1.0.1.tar.gz | 406.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| polycoding-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 463.8 kB
Release files / polycoding-1.0.1.tar.gz
| Download URL | polycoding-1.0.1.tar.gz |
|---|---|
| Size | 406.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ae3bb43cf5395c392bcfb3f769ca8d961bf7b5012c968c499ea95ded6f7356db
|
|
BLAKE2b-256 checksum How to use checksums |
249f162d36d5e65060f750db40b4fe1ce058b868faafbbdbe43c6b483b1111ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 2, 2026.
Transparency logRelease files / polycoding-1.0.1-py3-none-any.whl
| Download URL | polycoding-1.0.1-py3-none-any.whl |
|---|---|
| Size | 56.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
759d6f5ca76309f3e6f6cfe3cbfb573f72ac5d33fcf9a67f3a6b7f3a55a1924f
|
|
BLAKE2b-256 checksum How to use checksums |
142ca6b505ed0cfc5211e03c0c9112723d9a8c091cb13cddd6e9a336cf14e29c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 2, 2026.
Transparency log