Plane Compose
Project as Code framework for Plane — Define projects, schemas, and work items in YAML. Sync bidirectionally. Version control everything.
Plane Compose brings infrastructure-as-code workflows to project management. Declare your Plane projects in YAML, push them to Plane, pull changes back, and version-control everything in Git.
Features
- Bidirectional sync — push local YAML to Plane, pull remote state back
- Schema management — work item types, states, labels, workflows, and custom properties
- Workspace management — members, releases, initiative labels, relation definitions, project states
- Template-based init & upgrade — bootstrap or upgrade projects from local or remote (Git) templates
- Project cloning — clone any existing Plane project to local YAML
- Intelligent diff — see exactly what would change before pushing
- Collaborative mode — additive-only push, safe for shared teams
- CI/CD ready —
--dry-run,--exit-code,--json, and non-interactive flags throughout - Self-hosted support — works with any Plane instance, not just Plane Cloud
- Rate limiting — built-in throttling with live stats
Installation
pipx install plane-compose
Upgrade
pipx upgrade plane-compose
Verify
plane --version
Quick Start
Step 1 — Authenticate
plane auth login
# Interactive: enter your API key and workspace slug
# API keys: https://app.plane.so/<workspace>/settings/account/api-tokens/
For CI/CD (non-interactive):
plane auth login \
--server-url https://api.plane.so \
--auth-type pat \
--token YOUR_TOKEN \
--workspace myteam
Path A — Start from Scratch
# Initialize a new project (default built-in template)
plane init MYPROJ -w myteam
cd myproj
# Edit schema to match your workflow
vim schema/types.yaml
vim schema/workflows.yaml
# Push schema (creates the project in Plane)
plane schema push
# Add work items
vim work/workitems.yaml
# Push work items
plane push
Or bootstrap from a template (local path or Git URL):
# From a local template
plane init MYPROJ -w myteam -t ./templates/engineering
# From a remote Git template (with optional version pin)
plane init MYPROJ -w myteam -t https://github.com/org/repo/templates/standard@v1.2.0
cd myproj
# Push everything (schema + any template work items)
plane push
Path B — Clone an Existing Project
# Clone by UUID, project key, or workspace/KEY
plane clone myteam/MYPROJ
cd myproj
# See what was pulled
cat .plane/remote/items.yaml
# Make changes and push
vim work/inbox.yaml
plane push
Commands
For the full flag reference, see the Command Reference.
Auth
| Command | Purpose |
|---|---|
plane auth login |
Authenticate with Plane (interactive or CI flags) |
plane auth logout CONNECTION_ID |
Remove a connection |
plane auth list-connections |
List all saved connections and workspaces |
plane auth connect-workspace CONNECTION_ID WORKSPACE |
Link a workspace to a connection |
plane auth disconnect-workspace WORKSPACE |
Unlink a workspace |
Project
| Command | Purpose |
|---|---|
plane init [PROJECT] -w WORKSPACE [-t TEMPLATE] |
Initialize a new project (optionally from a template) |
plane clone PROJECT [--workspace WS] |
Clone an existing Plane project to local YAML |
plane upgrade [PROJECT] -t TEMPLATE [--include-data] [--skip-pull] [--dry-run] |
Upgrade schema (and optionally data) from a template |
plane schema validate [PROJECT] |
Validate local schema files without connecting to Plane |
plane schema push [PROJECT] [--dry-run] [--force] |
Push schema to Plane (creates project if needed) |
plane schema import [PROJECT] [--merge] [--force] |
Import remote schema into local state |
plane schema sync [PROJECT] [--dry-run] |
Declarative schema sync — make remote match local exactly |
plane schema diff [PROJECT] [--exit-code] |
Compare local schema with remote |
plane push [PROJECT] [--dry-run] [--schema-only] [--skip SECTION] [--resume] |
Push schema and/or work items to Plane |
plane pull [PROJECT] [--schema-only] [--skip SECTION] |
Pull schema and/or work items from Plane |
plane diff [PROJECT] [--exit-code] |
Show bidirectional diff between local and remote work items |
plane status [--all] |
Show project sync status |
plane validate [--offline] |
Validate work items against schema |
plane upgrade — Template-based upgrades
Pulls the latest state from Plane, diffs it against a template, shows a plan, and applies changes. Non-destructive — local-only items are always preserved.
plane upgrade -t ./my-template # Schema only, current dir
plane upgrade MYPROJ -t https://github.com/org/repo/tmpl # Remote template
plane upgrade MYPROJ -t ./my-template --include-data # Schema + cycles, modules, work items, pages
plane upgrade MYPROJ -t ./my-template --dry-run # Preview only
Pin a specific template version:
plane upgrade MYPROJ -t https://github.com/org/repo@v1.2.0
Workspace (plane ws)
Manage workspace-level configuration: work item types, members, releases, initiative labels, relation definitions, and project states.
| Command | Purpose |
|---|---|
plane ws clone [WORKSPACE] |
Clone workspace configuration to local YAML |
plane ws pull [--merge] [--force] |
Pull latest workspace config from Plane |
plane ws push [--dry-run] [--prune] [--skip SECTION] |
Push local workspace changes to Plane |
plane ws diff |
Show differences between local and remote workspace config |
plane ws upgrade [WORKSPACE] [--template TEMPLATE] [--dry-run] [--schema-only] |
Apply a versioned workspace template — diff, plan, and push changes |
plane ws state show |
Display workspace sync state |
plane ws state reset [--force] |
Reset workspace sync state |
plane ws state remove PATH_TO_ITEM |
Remove a specific item from state tracking |
Sync State (plane state)
| Command | Purpose |
|---|---|
plane state show PROJECT_KEY |
Show project sync state |
plane state reset PROJECT_KEY [--force] |
Reset sync state (next push treats all items as new) |
plane state clear-items PROJECT_KEY |
Clear work item tracking, keep schema mappings |
plane state remove PROJECT_KEY TRACKING_KEY |
Remove a specific item from tracking |
Rate Limiting (plane rate)
| Command | Purpose |
|---|---|
plane rate stats |
Show current rate limit statistics |
plane rate reset |
Reset rate limit statistics |
Project Structure
Created by plane init or plane clone:
myproj/
├── plane.yaml # Project config — workspace, connection, template source
├── workspace.yaml # Workspace-level config (features, work item types) — auto-managed
├── schema/
│ ├── features.yaml # Project feature flags
│ ├── labels.yaml # Label definitions
│ ├── members.yaml # Project members
│ ├── states.yaml # Workflow states
│ ├── types.yaml # Work item types and custom properties
│ └── workflows.yaml # State transitions
├── work/
│ ├── cycles.yaml # Cycles
│ ├── milestones.yaml # Milestones
│ ├── modules.yaml # Modules
│ ├── pages.yaml # Project pages (requires pages feature flag)
│ └── workitems.yaml # Work items (any *.yaml except reserved files is treated as work items)
└── .plane/
└── state.json # Sync state (auto-managed)
plane.yaml
workspace: myteam
connection: default # Connection to use (defaults to "default")
template: default # Template source — set by init/upgrade, used by next upgrade
project:
key: MYPROJ
name: My Project
uuid: abc-123-def # Auto-populated after first schema push
defaults:
type: task
workflow: standard
work/workitems.yaml
workitems:
- id: "auth-oauth" # Stable ID — prevents duplicates across pushes
title: Implement OAuth2 authentication
type: task
priority: high
labels: [backend, feature]
state: todo
description: Add OAuth2 authentication flow
- id: "bug-login-css"
title: Fix login button alignment
type: bug
priority: medium
state: backlog
work/cycles.yaml
cycles:
- name: Sprint 1
description: First sprint — core setup and scaffolding
start_date: '2024-01-01'
end_date: '2024-01-14'
- name: Sprint 2
description: Second sprint — feature development
start_date: '2024-01-15'
end_date: '2024-01-28'
work/modules.yaml
modules:
- name: Authentication
description: User login, OAuth, and session management
status: backlog
- name: API Integration
description: Third-party API integrations
status: backlog
Workspace Structure
Created by plane ws clone:
myteam/
├── plane.yaml # Workspace config (slug, connection)
├── schema/
│ ├── features.yaml # Workspace feature flags
│ ├── members.yaml # Members with roles
│ ├── workitemtypes.yaml # Work item types, properties, hierarchy
│ ├── project.yaml # Project-level states and labels
│ ├── initiatives.yaml # Initiative labels
│ ├── releases.yaml # Release tags and labels
│ ├── relations.yaml # Custom relation type definitions
│ ├── templates_project.yaml # Project templates (push/pull/prune)
│ ├── templates_page.yaml # Page templates (push/pull/prune)
│ └── webhooks.yaml # Webhook configurations (read-only stub)
└── work/
└── releases.yaml # Release items (operational data)
Configuration
Self-Hosted Plane
Point to your instance when authenticating:
plane auth login --server-url https://plane.yourcompany.com
The server URL is stored in the connection and used automatically for all subsequent commands. No changes to plane.yaml needed.
Alternatively, override via environment variable:
export PLANE_API_URL="https://plane.yourcompany.com"
API Key
- Plane Cloud:
https://app.plane.so/<workspace>/settings/account/api-tokens/ - Self-hosted:
https://your-plane-url/<workspace>/settings/account/api-tokens/
Credentials are stored at ~/.config/plane-compose/credentials.
Environment Variables
PLANE_API_URL=https://api.plane.so # API base URL
PLANE_API_TIMEOUT=30 # Request timeout (seconds)
PLANE_RATE_LIMIT_PER_MINUTE=50 # API rate limit
PLANE_DEBUG=true # Enable debug logging
PLANE_LOG_TO_FILE=true # Log to ~/.config/plane-compose/plane.log
Troubleshooting
Authentication failed (401)
plane auth logout <connection-id>
plane auth login
Permission denied (403)
plane auth list-connections # Check available workspaces
plane auth whoami # Verify current user
Project not found (404)
# Remove stale UUID from plane.yaml, then re-push
plane schema push
Rate limit exceeded (429)
plane rate stats
export PLANE_RATE_LIMIT_PER_MINUTE=30
plane push
Duplicate work items
# Always use stable IDs in work/*.yaml
- id: "unique-identifier"
title: My task
State out of sync
cp .plane/state.json .plane/state.json.backup
plane state reset MYPROJ
plane pull
Debug logging
plane --debug push
tail -f ~/.config/plane-compose/plane.log
Development
Install plane-compose into your own Python environment:
pip install plane-compose
# Or pin to a specific version
pip install plane-compose==0.4.0
Architecture
src/planecompose/
├── cli/ # CLI commands (thin layer — delegates to sync/backend)
├── backend/ # Backend abstraction (PlaneApiClient, rate limiting)
├── core/ # Domain models (Pydantic)
├── sync/ # Sync orchestration (planner + executor)
├── diff/ # Change detection
├── parser/ # YAML parsing
├── templates/ # Template loader (local + Git, @tag pinning)
├── utils/ # Rate limiting, logging
├── config/ # Configuration management
└── exceptions.py # Custom exception hierarchy
Documentation
Full documentation is available at developers.plane.so/dev-tools/plane-compose, covering:
- Getting started — authentication, init, schema push, work items
- Cloning an existing project
- Project structure reference
- Understanding sync modes (collaborative vs declarative)
- Working with schemas — types, workflows, labels
- Working with work items
- Self-hosting configuration
Links
- Plane — Main website
- Plane API Documentation — API reference
- Plane Python SDK — Official SDK
License
AGPLv3 — see LICENSE.txt for details.
Metadata
Release files for plane-compose 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| plane_compose-0.5.2.tar.gz | 517.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| plane_compose-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 927.2 kB
Release files / plane_compose-0.5.2.tar.gz
| Download URL | plane_compose-0.5.2.tar.gz |
|---|---|
| Size | 517.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3935eb28b6e6ff75fc08e9a8881ebb565b82905fbef4652755bc16a65c79888d
|
|
BLAKE2b-256 checksum How to use checksums |
e3b99ce73313a90f4a25202a77b66761ac996b892c6aaf5713e457289d5a2499
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / plane_compose-0.5.2-py3-none-any.whl
| Download URL | plane_compose-0.5.2-py3-none-any.whl |
|---|---|
| Size | 409.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bee9915eee35c9948f1c5637e336376528ab28732a0a53a29c117fc8b572002d
|
|
BLAKE2b-256 checksum How to use checksums |
50e0b65029d54d9b91ea5e0d479ea859c78ff50dd9e6cc1769f3a5b8fa7a2381
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|