@abimaelmartell/pco-cli
v0.1.4
Published
Agent-friendly TypeScript CLI for the Planning Center API.
Maintainers
Readme
pco-cli
Agent-friendly TypeScript CLI for the Planning Center API, intended for npm publishing.
Planning Center exposes APIs for products including Calendar, Check-Ins, Giving, Groups, People, and Services. This CLI focuses on the Services product for worship planning workflows.
Install
From npm (after the package is published):
npm install -g @abimaelmartell/pco-cli
pco healthThe registry package is scoped because npm rejects unscoped pco-cli as too similar to existing cp-cli. The installed binaries are still pco and pco-cli.
From this repository:
npm install
npm run build
npm link
pco healthnpm install and npm run build alone do not put pco on PATH. Use npm link for a local checkout, or run npm run dev -- health.
Configuration
Copy .env.example to .env and set credentials:
cp .env.example .envTo store credentials once for every working directory, copy the same file to ~/.config/pco/env (or $XDG_CONFIG_HOME/pco/env). Override the path with PCO_CONFIG_PATH if needed.
Supported authentication inputs:
PCO_ACCESS_TOKENfor bearer token flows.PCO_CLIENT_IDandPCO_SECRETfor basic auth. Planning Center personal access tokens are labeled Client ID + Secret.PCO_APP_IDis a compatible alias ofPCO_CLIENT_ID. If both are set to different values, the CLI exits with an error.
You can also pass credentials via CLI flags: --access-token, --client-id (alias --app-id), and --secret. Flags override environment values. Environment values override a project .env, which overrides the global config file. You do not need to pass flags on every command when credentials are in the environment or a config file.
Bearer PCO_ACCESS_TOKEN / --access-token wins over basic auth when both are present.
Agent skill
A portable Agent Skill lives at .agents/skills/pco-cli/SKILL.md. Cursor, Claude Code, Codex, and other compatible agents load it from that path automatically in this repo. The skill is the source of truth for calling the CLI, including the songs + keys + musicians playbook (plan-items add-song with --arrangement-id and --key-id; do not use create-worship-plan when keys matter).
To use the CLI from another project or globally:
mkdir -p ~/.agents/skills/pco-cli
curl -fsSL https://raw.githubusercontent.com/abimaelmartell/pco-cli/main/.agents/skills/pco-cli/SKILL.md \
-o ~/.agents/skills/pco-cli/SKILL.mdProject copy (Cursor also reads .cursor/skills/):
mkdir -p .agents/skills/pco-cli
curl -fsSL https://raw.githubusercontent.com/abimaelmartell/pco-cli/main/.agents/skills/pco-cli/SKILL.md \
-o .agents/skills/pco-cli/SKILL.mdUsage
The CLI prints JSON so automation agents can parse output reliably.
Health check
pco healthJSON includes ok, version (from package.json), baseUrl, auth (basic, bearer, or none), and clientReady.
Service Types
List available service types:
pco service-types list
pco service-types list --per-page 10 --offset 0Songs
Search for songs by title:
pco songs search "Amazing Grace"
pco songs search "How Great" --per-page 5Create or update library songs (Planning Center assignable fields: title, admin, author, copyright, ccli_number, hidden, themes):
pco songs get <song-id>
pco songs create --title "Holy Forever" --author "Chris Tomlin" --ccli-number 7200535
pco songs update <song-id> --hidden true --themes "Praise"Tags replace the full set. Look up IDs first:
pco tag-groups list --tags-for song --include tags
pco tag-groups tags <tag-group-id>
pco songs tags <song-id>
pco songs assign-tags <song-id> --tag-ids 5,9Arrangements and keys
Keys belong to an arrangement, not directly to a song. Creating a song usually adds a default arrangement; list it before adding keys.
pco arrangements list <song-id> --include keys
pco arrangements create <song-id> --name "Default" --meter 4/4 --bpm 72
pco arrangements update <song-id> <arrangement-id> --chord-chart "[G]Holy forever"
pco arrangements assign-tags <song-id> <arrangement-id> --tag-ids 12
pco keys list <song-id> <arrangement-id>
pco keys create <song-id> <arrangement-id> --starting-key G --ending-key G
pco keys update <song-id> <arrangement-id> <key-id> --starting-key A --name "Acoustic"--starting-key / --ending-key use Planning Center values (C, Cm, F#, …). --alternate-keys is a JSON array of {"name":"Capo 3","key":"A"}.
Keys do not have their own tags in the Services API. Tag songs and arrangements instead.
People
Search for people by name:
pco people search "John Smith"
pco people search "Sarah" --per-page 10Teams
List teams for a service type, and positions for a team:
pco teams list <service-type-id>
pco teams positions <team-id>Plans
Create and manage service plans:
# List plans
pco plans list <service-type-id>
pco plans list <service-type-id> --filter future --order sort_date
# Get a specific plan (includes planning_center_url for the Services web UI)
pco plans get <service-type-id> <plan-id>
# Create a plan with optional service time (returns plan_time id for reminders)
pco plans create <service-type-id> \
--title "Sunday Worship" \
--series-title "Summer Series" \
--public \
--starts-at "2026-08-30T10:00:00Z" \
--ends-at "2026-08-30T11:30:00Z"
`--starts-at` and `--ends-at` must be ISO 8601 datetimes with a timezone. `--ends-at` requires `--starts-at` and must be later than the start time.
# List times on a plan
pco plan-times list <service-type-id> <plan-id>Plan Items (Songs)
Add songs to a plan:
# List items in a plan
pco plan-items list <service-type-id> <plan-id>
# Add a song by ID
pco plan-items add-song <service-type-id> <plan-id> --song-id <song-id>
# Add a song by title (must match exactly one song across every search page)
pco plan-items add-song <service-type-id> <plan-id> --title "Amazing Grace"
# Add with arrangement and key
pco plan-items add-song <service-type-id> <plan-id> \
--song-id <song-id> \
--arrangement-id <arrangement-id> \
--key-id <key-id>Song items always send title from the song resource (same as the Planning Center UI) plus song_id. Without title, Services shows "New Item".
Team Member Assignments
Assign people to plans:
# List team members on a plan
pco plan-team-members list <service-type-id> <plan-id>
# See who still needs the first Accept/Decline scheduling email (all assigned members)
pco plan-team-members notify-status <service-type-id> <plan-id>
# Assign a person to a plan
pco plan-team-members assign <service-type-id> <plan-id> <person-id> <team-id> \
--position "Worship Leader" \
--prepare-notificationTeam Reminders
Set automated reminder emails for teams:
# Set team reminders (days before service)
pco plan-reminders set <service-type-id> <plan-time-id> \
--team-reminders '{"<team-id>": 7, "<team-id-2>": 3}'The team_reminders object maps team IDs to the number of days (0-7) before the service time to send automated reminder emails.
Complete Worship Planning Workflow
Create a full worship plan in one command:
pco create-worship-plan <service-type-id> \
--title "Sunday Morning Service" \
--starts-at "2026-08-30T10:00:00Z" \
--ends-at "2026-08-30T11:30:00Z" \
--series-title "Summer Worship" \
--public \
--songs "Amazing Grace" "How Great Thou Art" "Oceans" \
--assignments '[{"person_id":"123","team_id":"10","position":"Worship Leader"}]' \
--team-reminders '{"10": 7, "11": 3}'This command:
- Validates assignment JSON and reminder offsets, then looks up each song title in the church library (fails before creating anything if a title is missing or not unique)
- Creates a plan with the specified title and series
- Adds a service time, including
team_reminderswhen provided - Adds the matched songs in order (
song_idandtitle— Planning Center default arrangement/key) - Assigns team members with their positions
- Returns
planning_center_urlfrom the plan resource (the Services web UI URL, not the APIlinks.self)
When the setlist includes specific keys, do not use create-worship-plan. Follow the Worship plan with keys and musicians playbook in .agents/skills/pco-cli/SKILL.md (keys create if needed, then plan-items add-song with --arrangement-id and --key-id).
Important API Limitations
Scheduling Email ("Accept/Decline") Not Supported
Planning Center's "Send scheduling email" button (the one that sends Accept/Decline links to team members) is NOT available via the API. This was confirmed by Planning Center in planningcenter/developers#1475.
Workarounds:
Automated Reminders: Use
team_reminderswhen creating or updating plan times. This sends automated reminder emails to teams at a specified number of days before the service.Manual Sending: After creating a plan via the CLI, use the returned
planning_center_urlto open the plan in the Planning Center web UI and manually send scheduling emails.Check Notification Status: Use
plan-team-members notify-status(orlist) to seenotification_sent_at,prepare_notification, andnotification_prepared_atand who still needs the first scheduling email.
Project layout
src/cli.tswires the command-line interface.src/client.tscontains the reusable Planning Center API client.src/helpers.tsmaps CLI arguments and JSON:API resources.src/config.tsloads and validates environment configuration.src/index.tsexports library primitives for programmatic use.
Publishing
Releases are automated with semantic-release and Conventional Commits. On every push to main, .github/workflows/publish.yml runs tests and then publishes only when the commits since the last release include a feat or fix (semver). npm authenticates with trusted publishing (GitHub OIDC). There is no NPM_TOKEN secret.
Use a Conventional Commit PR title and squash-merge. CI rejects titles that do not match. Do not bump version in package.json or push v* tags by hand — semantic-release publishes to npm, tags vX.Y.Z, and commits the new version back to package.json / package-lock.json.
| PR title | Semver bump | Example |
| --- | --- | --- |
| feat: add song tags | minor (0.1.2 → 0.2.0) | new command or capability |
| fix: handle empty search | patch (0.1.2 → 0.1.3) | bug fix |
| feat!: drop Node 18 or a BREAKING CHANGE: footer | major (0.1.2 → 1.0.0) | breaking change |
| docs:, chore:, ci:, test:, refactor: | no publish | docs and tooling |
perf: also publishes a patch. A GitHub Release and git tag (vX.Y.Z) are created with the npm publish. The release commit is chore(release): X.Y.Z [skip ci] so it does not start another publish.
The workflow filename must stay publish.yml so it matches the trusted publisher on npmjs.com. Do not set NODE_AUTH_TOKEN or NPM_TOKEN on that job, and do not pass registry-url to actions/setup-node — both make npm skip OIDC trusted publishing.
One-time setup on npmjs.com
- Sign in as the npm user
abimaelmartell(the scope must match your npm username). - On npmjs.com open @abimaelmartell/pco-cli → Settings → Trusted Publisher.
- Choose GitHub Actions and set:
- Organization or user:
abimaelmartell - Repository:
pco-cli - Workflow filename:
publish.yml(filename only, including.yml) - Environment name: leave blank
- Allowed actions:
npm publish
- Organization or user:
- After a successful OIDC publish, optionally set Publishing access to require 2FA and disallow tokens.
You can do the same attach step from a local npm 11.5.1+ CLI:
npm trust github @abimaelmartell/pco-cli --repo abimaelmartell/pco-cli --file publish.yml --allow-publishLocal dry run (does not publish):
npm run check
npm run build
npm pack --dry-run
npx semantic-release --dry-run