Skip to main content

Project as Code framework for Plane - Define and sync projects declaratively with YAML

Project description

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
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 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
│   ├── modules.yaml        # Modules
│   └── workitems.yaml      # Work items (any *.yaml except cycles/modules 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
│   └── 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


License

AGPLv3 — see LICENSE.txt for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plane_compose-0.4.0rc2.tar.gz (396.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plane_compose-0.4.0rc2-py3-none-any.whl (296.5 kB view details)

Uploaded Python 3

File details

Details for the file plane_compose-0.4.0rc2.tar.gz.

File metadata

  • Download URL: plane_compose-0.4.0rc2.tar.gz
  • Upload date:
  • Size: 396.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for plane_compose-0.4.0rc2.tar.gz
Algorithm Hash digest
SHA256 7ef913679d0699b80fac5dc33d5e1f8cf5186b7fa7ddde1e9f9a17c6ec56af3d
MD5 98826f1ff9ebb7f8ffd6bfc1e52257a5
BLAKE2b-256 446bbc8493efec5270168bec18f9fcb5c3729ba3dbd7b0eb2656980253444c14

See more details on using hashes here.

File details

Details for the file plane_compose-0.4.0rc2-py3-none-any.whl.

File metadata

File hashes

Hashes for plane_compose-0.4.0rc2-py3-none-any.whl
Algorithm Hash digest
SHA256 b70f9a92c33a93bd8546320ea1023f848311af7f6db42ced93470641d0d8cf93
MD5 dac77fabcd95f02a982ccffb73510f86
BLAKE2b-256 641867cc47be0d9aef8232e3318574b0757ce3a437e0a00aadb9a2afe576d36e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page