plane-cli/README.md
theo 7c3c2053a5 Make the CLI instance-agnostic: no hard-coded UUIDs
- Config fully from env (PLANE_API_KEY + PLANE_BASE required); remove the
  baked-in ludique.dev / colorcatchers / state-UUID defaults.
- Fetch each project's workflow states live; cache per project per shell.
  Fixes the buggy group->key mapping (unstarted now maps to 'todo', not
  'backlog'). Verified the dynamic map matches the old hard-coded one.
- Persist the active project to $PLANE_CLI_HOME/active so `pluse` is
  remembered across shells; auto-resolve project name from UUID.
- plsync resolves its "Everything" target dynamically (PLANE_EVERYTHING_*
  or by name) and builds its state map from the target's live states.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:57:14 +02:00

4.8 KiB

plane-cli

A lightweight zsh + curl CLI for Plane (works with self-hosted instances). No build step, no dependencies to install beyond what you already have — it's a single sourced shell file that wraps the Plane REST API with fzf pickers.

Personal tool, built for a self-hosted Plane instance. Inspired by cpatrickalves/plane-cli, but deliberately tiny: just functions in your shell.

Requirements

  • zsh
  • curl
  • python3 (used for JSON shaping — stdlib only)
  • fzf (interactive pickers)

Install

git clone https://forgejo.ludique.dev/theo/plane-cli.git ~/Developer/plane-cli

Add to your ~/.zshrc:

export PLANE_API_KEY="plane_api_xxxxxxxxxxxxxxxx"          # keep this out of version control
export PLANE_BASE="https://your-plane.example.com/api/v1/workspaces/your-workspace"
source ~/Developer/plane-cli/plane.zsh

Reload (source ~/.zshrc), then pick your project once:

pluse           # fzf-pick a project — remembered across shells

Configuration

Nothing instance-specific is hard-coded. The CLI reads everything from the environment and fetches each project's workflow states live, so it works against any Plane instance.

Env var Required Purpose
PLANE_API_KEY yes Your API token (Plane → Settings → API tokens).
PLANE_BASE yes https://<host>/api/v1/workspaces/<workspace-slug>.
PLANE_PROJECT no Pin the active project (UUID). Otherwise use pluse.
PLANE_PROJECT_NAME no Display name for a pinned project (auto-resolved if omitted).
PLANE_EVERYTHING_PROJECT no Target project UUID for plsync (see below).
PLANE_EVERYTHING_NAME no Name plsync looks up if the UUID isn't set (default Everything).
PLANE_CLI_HOME no Config dir (default ${XDG_CONFIG_HOME:-~/.config}/plane-cli).

Active projectpluse (no args) opens an fzf picker, fetches that project's states, and persists the choice to $PLANE_CLI_HOME/active, so every new shell remembers it. You can also pass pluse <name|identifier|id>. State keys (backlog/todo/progress/done/cancelled) are derived from Plane's state groups, so they map correctly on any project.

Commands

pl                         list open issues (shows labels)
pl -a                      list ALL issues (incl. done/cancelled)
pl -s backlog|todo|...     filter by state
pl -p urgent|high|...      filter by priority
pl /text                   search titles containing 'text'
pl -j                      raw JSON output (pipe to jq); honours filters
pl -h                      full help

pladd                      create an issue (interactive wizard)
pladd "title" [opts]       create inline
    -p priority   urgent|high|medium|low      (default: none)
    -s state      backlog|todo|progress       (default: backlog)
    -d YYYY-MM-DD due date
    -l a,b,c      attach labels (must already exist)

plstate [#]                change state — fzf picker if no id
pldone  [#]                mark done — fzf picker if no id
plshow                     fzf-pick and inspect an issue (shows labels)
pledit  [#]                edit title/priority/due/labels
plrm    [# ...]            delete — fzf multi-select if no ids

plcomment [#] [text]       list comments on an issue, or add one
pllabels  [add "n" [#hex]] list project labels, or create one

plprojects                 list all projects
pluse [name|id]            switch active project (fzf if no arg)
plsync [--dry-run]         mirror open issues from all projects into an "Everything" project

Examples

pl -s todo                       # only todo issues
pl -p high                       # only high priority
pl /login                        # titles containing "login"
pl -j | jq -r '.[].name'         # scriptable JSON

pladd "Fix export button" -p high -s todo -l bug,urgent
plcomment 7 "fixed in latest deploy"
pllabels add "bug" "#FF0000"

Notes

  • pl -j emits the filtered issue list as JSON for piping to jq.
  • Comments are created via Plane's comment_html field (the API stores HTML).
  • plsync mirrors every project's open issues into one "Everything" project. It resolves the target from PLANE_EVERYTHING_PROJECT, or a project named Everything (override the name with PLANE_EVERYTHING_NAME). It's idempotent — items are keyed by [IDENTIFIER] title and ones closed at the source are removed. Run plsync --dry-run first to preview.

License

MIT