Build production-ready plugins for the Trello CLI
- Introduction
- Quick Start
- Plugin Architecture
- Creating Your First Plugin
- Environment Variables
- Advanced Examples
- Best Practices
- Official Plugins
The Trello CLI plugin system allows you to extend functionality without modifying core code. Plugins can be written in any language and have full access to the Trello API through injected credentials.
Even the board auditor runs as a plugin.
This isn't a toy feature - it's an architectural statement. If complex workflow analysis can live externally, anything can.
The core doesn't need to change for the CLI to evolve.
# See all available plugins
trello plugin list
# Get detailed info about a plugin
trello plugin info board-audit
# Run a plugin
trello plugin run board-audit <board_id>Plugins are stored in: ~/.trellocli/plugins/
# Create plugin directory
mkdir -p ~/.trellocli/plugins/
# Add your plugin
nano ~/.trellocli/plugins/my-plugin.py
# Make it executable
chmod +x ~/.trellocli/plugins/my-plugin.py
# Verify it's detected
trello plugin listEvery plugin must start with a metadata header:
#!/usr/bin/env python3
# trello-plugin
# name: My Plugin Name
# description: What this plugin does
# usage: plugin run my-plugin [args]
# author: Your Name
# version: 1.0.0
# tags: audit, automation, reportingRequired fields:
trello-pluginmarker (MUST be present)name- Display namedescription- What the plugin doesusage- How to run itauthor- Who made itversion- Semantic version (x.y.z)
Optional fields:
tags- Comma-separated keywords
- User runs:
trello plugin run my-plugin arg1 arg2 - CLI injects environment variables (credentials, config paths)
- CLI executes the plugin with args:
./my-plugin.py arg1 arg2 - Plugin output goes directly to terminal
- Exit code returned to CLI
| Language | Execution Method | Example |
|---|---|---|
| Python | python3 plugin.py |
#!/usr/bin/env python3 |
| Bash | bash plugin.sh |
#!/bin/bash |
| JavaScript | node plugin.js |
#!/usr/bin/env node |
| Ruby | ruby plugin.rb |
#!/usr/bin/env ruby |
| Go | Direct execution | Compile binary, make executable |
| Any other | Direct execution | Make file executable, use shebang |
Create ~/.trellocli/plugins/card-counter.py:
#!/usr/bin/env python3
# trello-plugin
# name: Card Counter
# description: Count cards in a specific list
# usage: plugin run card-counter <list_id>
# author: Your Name
# version: 1.0.0
import os
import sys
import requests
def main():
# Parse arguments
if len(sys.argv) < 2:
print("❌ Usage: trello plugin run card-counter <list_id>")
return 1
list_id = sys.argv[1]
# Get credentials from environment
api_key = os.environ.get('TRELLO_API_KEY')
token = os.environ.get('TRELLO_TOKEN')
base_url = os.environ.get('TRELLO_BASE_URL', 'https://api.trello.com/1')
if not api_key or not token:
print("❌ Missing API credentials", file=sys.stderr)
print(" Run 'trello config' to set up credentials", file=sys.stderr)
return 1
# Make API call
try:
url = f"{base_url}/lists/{list_id}/cards"
params = {'key': api_key, 'token': token}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
cards = response.json()
print(f"✅ List has {len(cards)} card(s)")
return 0
except requests.exceptions.RequestException as e:
print(f"❌ API Error: {e}", file=sys.stderr)
return 1
if __name__ == '__main__':
sys.exit(main())Make it executable:
chmod +x ~/.trellocli/plugins/card-counter.pyRun it:
trello plugin run card-counter 68fcff46fa7dbc9cc069eaefThe CLI injects these variables into every plugin execution:
| Variable | Description | Example |
|---|---|---|
TRELLO_API_KEY |
Your Trello API key | a1b2c3d4... |
TRELLO_TOKEN |
Your Trello API token | xyz789... |
TRELLO_USER_ID |
Your Trello user ID | 5a3b1c... |
| Variable | Description | Value |
|---|---|---|
TRELLO_CONFIG_DIR |
Config directory | ~/.trellocli |
TRELLO_PLUGIN_DIR |
Plugin directory | ~/.trellocli/plugins |
TRELLO_CLI_VERSION |
CLI version | 2.2.0 |
TRELLO_BASE_URL |
Trello API base URL | https://api.trello.com/1 |
Python:
import os
api_key = os.environ.get('TRELLO_API_KEY')
token = os.environ.get('TRELLO_TOKEN')Bash:
API_KEY="${TRELLO_API_KEY}"
TOKEN="${TRELLO_TOKEN}"JavaScript:
const apiKey = process.env.TRELLO_API_KEY;
const token = process.env.TRELLO_TOKEN;Ruby:
api_key = ENV['TRELLO_API_KEY']
token = ENV['TRELLO_TOKEN']#!/usr/bin/env python3
# trello-plugin
# name: Board Health Reporter
# description: Generate HTML health report for a board
# usage: plugin run board-health <board_id> [output.html]
# author: Your Name
# version: 1.0.0
import os
import sys
import requests
from datetime import datetime
def main():
if len(sys.argv) < 2:
print("Usage: trello plugin run board-health <board_id> [output.html]")
return 1
board_id = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else 'board-health.html'
# Get credentials
api_key = os.environ.get('TRELLO_API_KEY')
token = os.environ.get('TRELLO_TOKEN')
base_url = os.environ.get('TRELLO_BASE_URL', 'https://api.trello.com/1')
if not api_key or not token:
print("❌ Missing credentials", file=sys.stderr)
return 1
# Fetch board data
try:
# Get board
board = requests.get(
f"{base_url}/boards/{board_id}",
params={'key': api_key, 'token': token}
).json()
# Get lists
lists = requests.get(
f"{base_url}/boards/{board_id}/lists",
params={'key': api_key, 'token': token, 'filter': 'open'}
).json()
# Count cards
total_cards = 0
for lst in lists:
cards = requests.get(
f"{base_url}/lists/{lst['id']}/cards",
params={'key': api_key, 'token': token}
).json()
total_cards += len(cards)
# Generate HTML report
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Board Health Report - {board['name']}</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 40px; }}
h1 {{ color: #0079BF; }}
.metric {{ background: #f4f5f7; padding: 20px; margin: 10px 0; border-radius: 5px; }}
</style>
</head>
<body>
<h1>Board Health Report</h1>
<h2>{board['name']}</h2>
<p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<div class="metric">
<h3>Summary</h3>
<p>Total Lists: {len(lists)}</p>
<p>Total Cards: {total_cards}</p>
</div>
<div class="metric">
<h3>Lists</h3>
<ul>
{''.join([f"<li>{lst['name']}</li>" for lst in lists])}
</ul>
</div>
</body>
</html>
"""
# Write report
with open(output_file, 'w') as f:
f.write(html)
print(f"✅ Health report generated: {output_file}")
return 0
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
return 1
if __name__ == '__main__':
sys.exit(main())#!/bin/bash
# trello-plugin
# name: Quick Card Creator
# description: Quickly create a card with just a title
# usage: plugin run quick-card <list_id> "Card Title"
# author: Your Name
# version: 1.0.0
set -e
# Check arguments
if [ $# -lt 2 ]; then
echo "Usage: trello plugin run quick-card <list_id> \"Card Title\""
exit 1
fi
LIST_ID="$1"
CARD_NAME="$2"
# Get credentials
API_KEY="${TRELLO_API_KEY}"
TOKEN="${TRELLO_TOKEN}"
BASE_URL="${TRELLO_BASE_URL:-https://api.trello.com/1}"
if [ -z "$API_KEY" ] || [ -z "$TOKEN" ]; then
echo "❌ Missing API credentials" >&2
exit 1
fi
# Create card
response=$(curl -s -X POST \
"${BASE_URL}/cards" \
-d "key=${API_KEY}" \
-d "token=${TOKEN}" \
-d "idList=${LIST_ID}" \
-d "name=${CARD_NAME}")
# Extract card ID
card_id=$(echo "$response" | jq -r '.id')
if [ "$card_id" != "null" ]; then
echo "✅ Card created: $card_id"
echo " Name: $CARD_NAME"
exit 0
else
echo "❌ Failed to create card" >&2
exit 1
fi# ✅ GOOD: Clear error messages
if not api_key:
print("❌ Missing API credentials", file=sys.stderr)
print(" Run 'trello config' to set up", file=sys.stderr)
return 1
# ❌ BAD: Generic errors
print("Error")# ✅ GOOD: Help message with usage
if len(sys.argv) < 2:
print("❌ Usage: trello plugin run my-plugin <board_id>")
print("\nExample:")
print(" trello plugin run my-plugin 68fcf05e481843db13204397")
return 1
# ❌ BAD: Silent failure
board_id = sys.argv[1] if len(sys.argv) > 1 else ""# ✅ GOOD: Set timeouts
response = requests.get(url, timeout=30)
# ❌ BAD: No timeout (can hang forever)
response = requests.get(url)# ✅ GOOD: Meaningful exit codes
if success:
return 0 # Success
else:
return 1 # Generic error
# ❌ BAD: Always return 0
return 0# ✅ GOOD: Structured output
print(f"✅ Found {len(cards)} card(s)")
print(f" Board: {board['name']}")
print(f" List: {list_name}")
# ❌ BAD: Unclear output
print(cards)# ✅ GOOD: Complete metadata
# trello-plugin
# name: Card Analyzer
# description: Analyzes card distribution across lists
# usage: plugin run card-analyzer <board_id> [--json]
# author: Your Name
# version: 1.0.0
# tags: analysis, reporting, statistics
# ❌ BAD: Minimal metadata
# trello-plugin
# name: PluginComprehensive workflow auditor (600+ lines)
What it validates:
- 🔴 Cards in Done without due dates
- 🔴 Cards in Done with incomplete checklists
- 🔴 Overdue cards not marked complete
- 🟠 Active cards without due dates
- 🟠 Execution cards without owners
- 🟡 Empty checklists
- 🟡 Pattern violations
- 🟡 Missing descriptions
Usage:
# Run audit
trello plugin run board-audit <board_id>
# JSON output for CI/CD
trello plugin run board-audit <board_id> --json
# With pattern validation
trello plugin run board-audit <board_id> --pattern "PF-[A-Z]+-\\d+"Why this matters: This plugin proves the plugin system can handle complex production logic. It's not a toy - it's a fully functional auditor with 8 validation rules, health scoring, and CI/CD integration.
Simple Python template showing basic API usage
trello plugin run example-python <board_id>Simple Bash template using curl and jq
trello plugin run example-bash <board_id>Here are some plugin ideas to inspire you:
- Time Tracker - Track time spent on cards
- Automated Labeler - Auto-label cards based on patterns
- Dependency Checker - Validate card dependencies
- Slack Notifier - Send Slack notifications for card updates
- GitHub Sync - Sync Trello cards with GitHub issues
- Custom Reporter - Generate custom reports (PDF, Excel, etc.)
- Deadline Enforcer - Warn about approaching deadlines
- Card Archiver - Auto-archive old Done cards
- Member Balancer - Balance workload across team members
- Sprint Analyzer - Analyze sprint metrics and velocity
# Check if plugin is in correct directory
ls ~/.trellocli/plugins/
# Verify plugin has correct extension or is executable
ls -la ~/.trellocli/plugins/my-plugin.py
# Make it executable
chmod +x ~/.trellocli/plugins/my-plugin.py⚠️ Warning: Plugin 'my-plugin' is missing trello-plugin marker
Fix: Add # trello-plugin to the beginning of your file
# Verify config exists
cat ~/.trello_config.json
# If missing, reconfigure
trello config# Install required libraries
pip3 install requests
# Or use virtual environment
python3 -m venv ~/.trellocli/venv
source ~/.trellocli/venv/bin/activate
pip install requestsWant to contribute a plugin to the official collection?
- Create a well-documented plugin following best practices
- Test it thoroughly with different boards
- Add comprehensive metadata
- Submit a pull request to the repository
- Include example usage and screenshots
Quality standards:
- Complete metadata header
- Error handling for all API calls
- Clear usage examples
- Meaningful exit codes
- Professional output formatting
The plugin system transforms the Trello CLI from a tool into a platform.
If the board auditor—the most complex analysis logic—can live as a plugin, then anything can.
Build something useful. Share it with the community. Extend the platform.
The core doesn't need to change for the CLI to evolve.
Questions? Issues?
- GitHub: trello-cli-python/issues
- Documentation: README.md
- Examples: plugins/