- 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> |
||
|---|---|---|
| .gitignore | ||
| LICENSE | ||
| plane.zsh | ||
| README.md | ||
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
zshcurlpython3(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 project — pluse (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 -jemits the filtered issue list as JSON for piping tojq.- Comments are created via Plane's
comment_htmlfield (the API stores HTML). plsyncmirrors every project's open issues into one "Everything" project. It resolves the target fromPLANE_EVERYTHING_PROJECT, or a project namedEverything(override the name withPLANE_EVERYTHING_NAME). It's idempotent — items are keyed by[IDENTIFIER] titleand ones closed at the source are removed. Runplsync --dry-runfirst to preview.
License
MIT