Overview
Thanks for your interest in contributing to Magpie! This guide will help you get started with the codebase, understand the architecture, and make your first contribution.Prerequisites
Before you begin, ensure you have:- Rust 1.75+ — Install via rustup
- Claude CLI — Required for both Tier 1 (text generation) and Tier 2 (Goose agent). See Claude Code docs for setup
- Git and GitHub CLI (
gh) — For version control, PR workflow, and repo cloning
Getting Started
1. Fork and Clone
2. Set Up Environment
Copy from.env.example or set manually:
3. Build the Project
First build takes ~4-5 minutes due to Goose transitive dependencies (candle, llama-cpp, tree-sitter). Subsequent builds are much faster.
4. Run Tests
5. Check Formatting and Lints
Project Structure
magpie-core, which is the main library. The adapter crates (magpie-cli, magpie-discord, magpie-teams) are thin wrappers that implement the ChatPlatform trait.
Key Modules in magpie-core
| Module | Purpose |
|---|---|
pipeline.rs | Full pipeline orchestrator — classify task, choose blueprint, CI loop, PR, Plane |
agent.rs | MagpieAgent wrapper around Goose’s agent loop (Tier 2) |
blueprint/ | Blueprint engine — step definitions, runner, shell/agent step kinds |
git.rs | GitOps helper for branch, commit, push, PR operations |
plane.rs | PlaneClient for self-hosted Plane issue tracking |
platform.rs | ChatPlatform trait — the adapter interface |
repo.rs | Org-scoped dynamic repo resolution |
trace.rs | Observability — TraceBuilder, AgentCallTrace, JSONL output |
sandbox/ | Sandbox abstraction — local, mock, Daytona implementations |
Two-Tier Architecture
Understanding the two-tier architecture is critical for contributing:Tier 1: claude_call (pipeline.rs)
- Direct
claude -pCLI call for clean, single-response text - Used for: branch slugs, task classification, commit messages
- When to use: Steps that need simple text output (summarization, classification)
Tier 2: MagpieAgent (agent.rs)
- Full Goose agent loop with streaming and tool access
- Used for: actual coding work (file edits, test writing, implementation)
- When to use: Steps that need file/shell tool access
Coding Conventions
Error Handling
- Use
anyhow::Result<T>everywhere - Use
.context("description")?to add error context - Use
bail!()for early returns with custom errors - Never
unwrap()in library code — only in tests or after guaranteed checks
Async Code
- Runtime:
tokio - All async traits use
#[async_trait] - Shared ownership:
Arc::clone(&x)(notx.clone())
Logging
- Use
tracingmacros:info!(),warn!(),error!()with structured fields - Never
println!()in library code (only in CLImain.rs)
Types & Style
- Derive
Debug, Cloneon most structs - Add
Serialize, Deserializefor types that cross boundaries (API responses, config files) - Builder pattern:
with_*methods that returnSelf - Default
rustfmt— runcargo fmtbefore committing cargo clippy -- -D warnings(all warnings are errors)
Configuration
Making Changes
1. Create a Branch
2. Make Your Changes
Keep commits focused and atomic. Follow the coding conventions above.3. Test Your Changes
4. Push and Create PR
main on GitHub.
Pull Request Guidelines
- Keep PRs small and focused — One logical change per PR is ideal
- Write a clear description of what the PR does and why
- Link related issues (e.g., Plane issues or GitHub issues) in the PR description
- All CI checks must pass before merge
- Address review feedback promptly
Common Patterns
Adding a New Pipeline Step
- Simple text → Tier 1 (
claude_call()inpipeline.rs) - File/shell access → Tier 2 (add
AgentStepto a blueprint) - Deterministic →
ShellStep
Adding a New Chat Adapter
- Create new crate:
crates/magpie-<platform>/ - Implement
ChatPlatformtrait (4 methods:name,fetch_history,send_message,close_thread) - Build
PipelineConfigfrom env vars - Call
run_pipeline()with your platform implementation
crates/magpie-discord/src/handler.rs for a complete example.
Adding a New Sandbox Type
-
Implement
trait Sandboxinsandbox/mod.rs:name()— sandbox type identifierworking_dir()— current working directoryexec()— execute commandread_file()— read file contentswrite_file()— write file contentsdestroy()— cleanup resources
-
Wire into
pipeline.rssandbox creation logic
sandbox/mock.rs for a simple example.
Running Specific Adapters
CLI (no external services needed)
Discord Bot
Teams Webhook
TEAMS_LISTEN_ADDR (default 0.0.0.0:3978):
POST /api/messages— Bot Framework webhookGET /health— health check
Reporting Issues
If you find a bug or have a feature request, open a GitHub issue with:- A clear title and description
- Steps to reproduce (for bugs)
- Expected vs. actual behavior
- Relevant logs or error messages