Plane Compose
Plane Compose is a command-line tool that lets you define and manage Plane projects using YAML configuration files instead of the web interface. Think of it as "infrastructure as code" for project management — you write your project structure in files, version control them with Git, and sync them with Plane.
Features
- Local-first workflow
Define everything in YAML and version control with Git. Your project structure, work item types, workflows, and tasks all live in your repository. - Bidirectional sync
Push local changes to Plane or pull remote changes down. Keep your local files and Plane in sync however you prefer to work. - Auto-create projects
Projects are created automatically in Plane when you push a schema. No need to set things up manually first. - Rich schema management
Define work item types with custom fields, create workflow state machines, and organize labels into groups. - Two sync modes
Use collaborative mode (plane push) for team-friendly additive syncing, or declarative mode (plane apply) when YAML should be the single source of truth. - Intelligent change detection
Content-based diffing means only actual changes get pushed. Stable IDs (user-defined or content-hashed) prevent duplicate work items. - State tracking
Terraform-style .plane/state.json tracks what's been synced, so Plane Compose knows what changed since the last push. - Built-in rate limiting
Respects Plane API limits with configurable throttling (50 requests/minute by default). Monitor usage with plane rate stats. - Project cloning
Clone an existing Plane project by UUID to start working with it locally. - Debug mode
Comprehensive logging and error handling when you need to troubleshoot.
Installation
Install Plane Compose globally using pipx (recommended):
pipx install plane-composeTo install from source:
git clone https://github.com/makeplane/compose.git
cd compose
pipx install -e .To upgrade to the latest version:
pipx upgrade plane-composeGetting started
This tutorial walks you through creating your first Plane Compose project and syncing it with Plane.
Initialize a project
Create a new project directory with the default structure:
plane init my-project
cd my-projectThis creates:
my-project/
├── plane.yaml # Project configuration
├── schema/
│ ├── types.yaml # Work item types (task, bug, etc.)
│ ├── workflows.yaml # State machines
│ └── labels.yaml # Label definitions
├── work/
│ └── inbox.yaml # Work items to create
└── .plane/
└── state.json # Sync state (auto-managed)TIP
You can set the workspace and project key directly from the command line:
plane init my-project --workspace myteam --project APIThis saves you from having to edit plane.yaml afterward.
Authenticate with Plane
Log in using your API key:
plane auth loginWhen prompted, enter your API key. You can generate one at https://app.plane.so/<workspace-slug>/settings/account/api-tokens/.
To verify your authentication:
plane auth whoamiConfigure your workspace
Open plane.yaml and set your workspace name:
workspace: your-workspace-slug
project:
key: PROJ
name: My Project
defaults:
type: task
workflow: standardTIP
The default schema includes common work item types, states, and labels. You can customize schema/types.yaml, schema/workflows.yaml, and schema/labels.yaml before pushing, or use the defaults and adjust later.
Push your schema
Create the project in Plane along with its work item types, states, and labels:
plane schema pushAfter this runs, plane.yaml is automatically updated with the project UUID.
Add work items
Edit work/inbox.yaml to define your work items:
- id: "auth-oauth"
title: Implement user authentication
type: task
priority: high
labels: [backend, feature]
state: todo
description: Add OAuth2 authentication
assignee: dev@example.com
watchers:
- pm@example.com
- qa@example.com
- id: "bug-login-css"
title: Fix login button CSS
type: bug
priority: medium
labels: [frontend, bug]
state: backlogPush work items
Preview what will be pushed:
plane push --dry-runWhen you're ready, push to Plane:
plane pushCloning an existing project
If you want to work with a project that already exists in Plane, clone it by UUID:
plane clone abc-123-def-456 --workspace my-workspace
cd <project-name>The remote work items are pulled to .plane/remote/items.yaml. You can review them, make changes in work/inbox.yaml, and push updates back to Plane.
Project structure
When you initialize a project, Plane Compose creates the following structure:
my-project/
├── plane.yaml # Project configuration
├── schema/
│ ├── types.yaml # Work item types (task, bug, etc.)
│ ├── workflows.yaml # State machines
│ └── labels.yaml # Label definitions
├── work/
│ └── inbox.yaml # Work items to create
└── .plane/
└── state.json # Sync state (auto-managed)plane.yaml
The main configuration file for your project:
workspace: myteam
project:
key: API # Short key or UUID
name: API Project
defaults:
type: task
workflow: standardschema/types.yaml
Define work item types and their fields:
task:
description: A single unit of work
workflow: standard
fields:
- name: title
type: string
required: true
- name: priority
type: enum
options: [none, low, medium, high, urgent]schema/workflows.yaml
Define state machines with states and transitions:
standard:
states:
- name: backlog
group: unstarted
color: "#858585"
- name: in_progress
group: started
color: "#f59e0b"
- name: done
group: completed
color: "#22c55e"
initial: backlog
terminal: [done]schema/labels.yaml
Organize labels into groups:
groups:
area:
color: "#3b82f6"
labels:
- name: frontend
- name: backendwork/inbox.yaml
Define work items to sync with Plane:
- id: "auth-oauth"
title: Implement user authentication
type: task
priority: high
labels: [backend, feature]
state: todo
description: Add OAuth2 authentication
assignee: dev@example.com
watchers:
- pm@example.com
- qa@example.com
- id: "bug-login-css"
title: Fix login button CSS
type: bug
priority: medium
labels: [frontend, bug]
state: backlogUnderstanding sync modes
Plane Compose offers two sync modes for different workflows.
Collaborative mode (plane push)
Use plane push when working with a team. This mode is additive-only — it creates and updates work items but never deletes them. Team members can safely push their changes without accidentally removing someone else's work.
plane push # Push new and updated items
plane push --dry-run # Preview changes first
plane push --force # Skip confirmation promptDeclarative mode (plane apply)
Use plane apply when you want your YAML files to be the single source of truth. This mode creates, updates, and deletes work items to match exactly what's in your files.
To prevent accidental deletions, declarative mode only affects work items within a defined scope. Configure the scope in plane.yaml:
apply_scope:
labels: ["automated"]
assignee: "bot@example.com"
id_prefix: "AUTO-"With this configuration, plane apply only manages work items that match this scope—everything else is left untouched.
plane apply # Sync with create/update/delete
plane apply --dry-run # Preview all changes including deletions
plane apply --force # Skip confirmation promptWorking with schemas
Schemas define the structure of your project: work item types, workflows, and labels.
Work item types
Define types in schema/types.yaml:
task:
description: A single unit of work
workflow: standard
fields:
- name: title
type: string
required: true
- name: priority
type: enum
options: [none, low, medium, high, urgent]
bug:
description: A defect to fix
workflow: bug-workflow
fields:
- name: title
type: string
required: true
- name: severity
type: enum
options: [minor, major, critical]Workflows
Define state machines in schema/workflows.yaml:
standard:
states:
- name: backlog
group: unstarted
color: "#858585"
- name: in_progress
group: started
color: "#f59e0b"
- name: done
group: completed
color: "#22c55e"
initial: backlog
terminal: [done]
bug-workflow:
states:
- name: reported
group: unstarted
color: "#ef4444"
- name: investigating
group: started
color: "#f59e0b"
- name: fixed
group: completed
color: "#22c55e"
initial: reported
terminal: [fixed]Labels
Organize labels into groups in schema/labels.yaml:
groups:
area:
color: "#3b82f6"
labels:
- name: frontend
- name: backend
- name: infrastructure
priority:
color: "#ef4444"
labels:
- name: urgent
- name: high
- name: lowValidating and pushing schemas
Before pushing, validate your schema files:
plane schema validatePush the schema to Plane:
plane schema push
plane schema push --dry-run # Preview firstWorking with work items
Work item fields
Each work item in your YAML files can include these fields:
| Field | Description |
|---|---|
id | Stable identifier for tracking (recommended) |
title | Work item title (required) |
type | Work item type (defaults to value in plane.yaml) |
priority | none, low, medium, high, or urgent |
state | Current state from the workflow |
labels | List of label names |
description | Detailed description |
assignee | Email of the assignee |
watchers | List of watcher emails |
Using stable IDs
Always use stable IDs to prevent duplicate work items:
- id: "feature-user-auth"
title: Implement user authentication
type: taskThe ID can be any string that uniquely identifies the work item. Without an ID, Plane Compose uses content hashing, which can create duplicates if you change the title or description.
Pulling remote changes
To see what's currently in Plane:
plane pullThis downloads work items to .plane/remote/items.yaml. You can compare this with your local files to see what's different.
Combined sync
Run schema push and work item push together:
plane syncMonitoring and rate limits
Check sync status
View the current state of your local project:
plane statusRate limit management
Plane Compose includes built-in rate limiting (50 requests per minute by default). To check your current rate limit stats:
plane rate statsTo reset the statistics:
plane rate resetIf you hit rate limits, you can reduce the request rate:
export PLANE_RATE_LIMIT_PER_MINUTE=30
plane pushConfiguration reference
plane.yaml
workspace: my-workspace
project:
key: PROJ # User-defined short key
uuid: abc-123 # Auto-added after schema push
name: My Project
defaults:
type: task
workflow: standard
# Optional: Scope for declarative mode
apply_scope:
labels: ["automated"]
assignee: "bot@example.com"
id_prefix: "AUTO-"Environment variables
| Variable | Default | Description |
|---|---|---|
PLANE_API_URL | https://api.plane.so | API endpoint |
PLANE_API_TIMEOUT | 30 | Request timeout in seconds |
PLANE_RATE_LIMIT_PER_MINUTE | 50 | Maximum requests per minute |
PLANE_DEBUG | false | Enable debug mode |
PLANE_VERBOSE | false | Enable verbose output |
PLANE_LOG_TO_FILE | false | Write logs to file |
Credentials storage
API keys are stored securely at ~/.config/plane-compose/credentials.
Logs are written to ~/.config/plane-compose/plane.log when debug mode is enabled.
Command reference
Project management
| Command | Description |
|---|---|
plane init [path] | Initialize a new project |
plane status | Show sync status |
plane clone <uuid> | Clone an existing project by UUID |
Authentication
| Command | Description |
|---|---|
plane auth login | Authenticate with API key |
plane auth whoami | Show current user |
plane auth logout | Remove stored credentials |
Schema management
| Command | Description |
|---|---|
plane schema validate | Validate local schema files |
plane schema push | Push schema to Plane |
plane schema push --dry-run | Preview schema changes |
Work items (collaborative mode)
| Command | Description |
|---|---|
plane push | Push new/updated work items |
plane push --dry-run | Preview changes |
plane push --force | Push without confirmation |
plane pull | Pull work items from Plane |
plane sync | Run schema push + push together |
Work items (declarative mode)
| Command | Description |
|---|---|
plane apply | Declarative sync with delete support |
plane apply --dry-run | Preview creates/updates/deletes |
plane apply --force | Apply without confirmation |
Monitoring
| Command | Description |
|---|---|
plane rate stats | Show rate limit statistics |
plane rate reset | Reset rate limit statistics |
Global options
| Option | Description |
|---|---|
--verbose, -v | Enable verbose output |
--debug | Enable debug logging |
Troubleshooting
Authentication failed (401)
Your API key may be invalid or expired. Log out and log in again:
plane auth logout
plane auth loginPermission denied (403)
You may not have access to the workspace. Verify your membership:
plane auth whoamiContact your workspace administrator if you need access.
Project not found (404)
The project UUID in plane.yaml may be stale. Remove it and recreate:
# Edit plane.yaml and delete the uuid line
plane schema pushRate limit exceeded (429)
Check your rate limit status:
plane rate statsWait for the limit to reset, or reduce your request rate:
export PLANE_RATE_LIMIT_PER_MINUTE=30
plane pushDuplicate work items
Always use stable IDs in your work items:
- id: "unique-identifier"
title: "My task"Without IDs, content changes can create duplicates instead of updates.
State corruption
If your local state gets corrupted, back it up and reset:
cp .plane/state.json .plane/state.json.backup
rm .plane/state.json
plane pullDebug mode
Enable debug logging to troubleshoot issues:
plane --debug pushView the logs:
tail -f ~/.config/plane-compose/plane.log
