Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,27 @@ This project aims to follow [Keep a Changelog](https://keepachangelog.com/en/1.1

Earlier project history may predate this file.

## [Unreleased]
## 1.2.0

### Added

- **Project Stats & Analytics.** `plane stats` aggregates issues by state group, priority, assignment, and period counts. Supports `--since`/`--until` date windows, `--cycle`/`--module` scoping, `--assignee` filtering, and workspace-wide aggregation via `plane stats workspace`. Outputs human-readable summaries or structured data via `--json`/`--xml`. All aggregation is client-side with paginated issue fetches, and workspace mode skips inaccessible projects while reporting which ones were skipped.
- **Issue Data Visibility.** `plane issue get` and `plane issues list --json` now include `start_date`, `target_date`, `completed_at`, `created_at`, `updated_at`, `estimate_point`, and full label objects (with `id`, `name`, `color`). The API expand was broadened from `state` to `state,labels`.
- **Issue Attribute Writing.** `plane issue create` and `plane issue update` support new flags: `--start-date`, `--target-date` (alias `--due-date`), `--estimate`, `--cycle` (name or UUID), and `--module` (name or UUID). `--label` can now be passed multiple times for multi-label assignment.
- **Advanced Issue Filtering.** `plane issues list` supports `--no-assignee`, `--stale <days>` (issues not updated in N+ days), and `--cycle <name|UUID>` filters.
- **Cycle Lifecycle Management.** `plane cycles create`, `plane cycles update`, and `plane cycles delete` commands with date validation and name-based resolution. `plane cycles list` now shows issue stats (`total_issues`, `completed_issues`, `cancelled_issues`) and a computed cycle status (draft, upcoming, current, completed).
- **Smart Resolution.** `resolveCycle` joins `resolveModule` for name-to-UUID resolution so automation scripts stay readable.

### Changed

- **Archived project defaults.** Project-listing contexts now exclude archived projects by default, including interactive init selection and workspace stats aggregation. Use `--include-archived` to opt back in when needed.
- Extracted issue link, comments, and worklogs sub-commands into `src/commands/issue-sub.ts` to keep `issue.ts` under the 700-line file-size limit.

### Validated

- Full release gate passed via `bun run check:all`: TypeScript typecheck, Biome check, file-size gate, and coverage check all succeeded for the 1.2.0 release train.
- Full Bun test suite passed: **304 tests across 22 files** with **598 expectations**, plus coverage at **98.33% functions** and **96.57% lines**.
- 1.2.0 command surfaces now covered by tests include issue field visibility and write-path expansions, advanced issue list filters, cycle lifecycle commands, workspace/project stats aggregation, and archived-project exclusion by default with `--include-archived` opt-in for init/project-listing contexts.

## 1.1.0

Expand Down Expand Up @@ -46,4 +66,4 @@ Earlier project history may predate this file.
- Full live test sweep completed against a real Plane instance. All core CLI workflows exercised: init (global, local, alias), project resolution, issue CRUD with rich options, comments, links, activity, cycles, modules, intake mutations, states, labels, members, and structured output.
- Confirmed the CLI's project-page endpoint routes are correct; both page API surfaces (project pages and workspace wiki pages) return 404 on some deployments regardless of feature flags.
- Confirmed worklogs are a Pro-plan-gated feature; the CLI returns explicit compatibility errors on non-Pro deployments.
- Added and validated first-class CLI cleanup commands for label delete and module delete.
- Added and validated first-class CLI cleanup commands for label delete and module delete.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ For path-local overrides in the current project directory:

```bash
plane init --local
plane init --local --include-archived
plane . init
```

Expand All @@ -58,6 +59,7 @@ environment variables > nearest .plane/config.json > ~/.config/plane/config.json

The local config is discovered from the current working directory upward, so a config written at the repo root applies inside nested folders unless a deeper `.plane/config.json` overrides it.
When you run `plane init --local`, the CLI also reads the project's feature flags from Plane and reports which project-scoped features are actually enabled. Cycles, modules, pages, and intake commands return explicit feature-disabled errors when the project has them turned off.
Project lists and project-selection prompts exclude archived projects by default. Add `--include-archived` to `plane init`, `plane . init`, `plane projects list`, or `plane stats ... workspace` when you intentionally want archived projects included.
It also writes `.plane/project-context.json`, a machine-readable helper snapshot of the project's existing states, labels, and estimate points so agents can reuse what already exists instead of inventing duplicates.
If `AGENTS.md` already exists in that directory, `plane init --local` appends a managed Plane project context section at the bottom without removing the existing content. If it does not exist, the CLI creates it. The managed section points agents at `.plane/project-context.json`, tells them to prefer the repo-local `plane` CLI for Plane work, and includes a small command pattern for clearing inherited `PLANE_*` overrides before using the local config.

Expand All @@ -74,6 +76,7 @@ To persist a current project after setup:

```bash
plane projects list
plane projects list --include-archived
plane projects use PROJ
plane projects use PROJ --local
plane projects use PROJ --global
Expand All @@ -94,6 +97,7 @@ Project-scoped feature availability still depends on the target Plane project. O
```bash
# Projects
plane projects list
plane projects list --include-archived
plane projects use PROJ
plane projects use PROJ --local
plane projects current
Expand All @@ -102,10 +106,20 @@ plane projects current
plane issues list
plane issues list PROJ
plane issues list PROJ --state started
plane issues list PROJ --no-assignee
plane issues list PROJ --stale 7
plane issues list PROJ --cycle "Week 14"
plane issue get PROJ-29
plane issue create --title "Title"
plane issue create --title "Title" PROJ
plane issue create --start-date 2025-04-01 --target-date 2025-04-14 --title "Sprint task" PROJ
plane issue create --label bug --label urgent --title "Regression" PROJ
plane issue create --cycle "Week 14" --title "Scoped task" PROJ
plane issue update --state completed --priority high PROJ-29
plane issue update --start-date 2025-04-01 --target-date 2025-04-14 PROJ-29
plane issue update --estimate <UUID> PROJ-29
plane issue update --cycle "Week 14" PROJ-29
plane issue update --module "Sprint 3" PROJ-29
plane issue delete PROJ-29

# Comments
Expand All @@ -129,6 +143,9 @@ plane issue worklogs add --description "standup" PROJ-29 30

# Cycles
plane cycles list PROJ
plane cycles create --name "Week 14" --start-date 2025-04-01 --end-date 2025-04-07 PROJ
plane cycles update --end-date 2025-04-08 PROJ "Week 14"
plane cycles delete PROJ "Week 14"
plane cycles issues list PROJ CYCLE_ID
plane cycles issues add PROJ CYCLE_ID PROJ-29

Expand All @@ -154,8 +171,21 @@ plane states list PROJ
plane labels list PROJ
plane labels delete PROJ bug
plane members list

# Stats
plane stats
plane stats PROJ
plane stats --since 2025-01-01 --until 2025-02-01 PROJ
plane stats --cycle "Sprint 1" PROJ
plane stats --module "Sprint 3" PROJ
plane stats --assignee Alice PROJ
plane stats workspace
plane stats --include-archived workspace
plane stats --since 2025-01-01 workspace --json
```

For `plane stats`, command-specific options such as `--since`, `--until`, `--cycle`, `--module`, and `--assignee` must come before the `PROJECT` argument or the special `workspace` keyword because of `@effect/cli` parsing rules. `--json` and `--xml` still work as global output flags. Workspace aggregation skips projects that return `403` for issue listing and reports them in the output.

Project identifiers: short strings like `PROJ`, `WEB`. Issue refs: `PROJ-29`, `WEB-5`.

State groups: `backlog` | `unstarted` | `started` | `completed` | `cancelled`
Expand All @@ -178,6 +208,9 @@ plane cycles list PROJ --json

- `plane issue update` expects flags before the issue ref, for example `plane issue update --state completed PROJ-29`.
- `--description` for issue and page create or update commands is sent through to Plane as HTML in `description_html`.
- `--target-date` has an alias `--due-date` for convenience.
- `--label` can be passed multiple times to assign several labels at once.
- `--cycle` and `--module` accept either a UUID or the exact name shown by `plane cycles list` / `plane modules list`.
- `plane issue link add` accepts an optional link title via `--title`.
- `plane labels delete` accepts either the label UUID or the exact label name returned by `plane labels list`.
- `plane modules create --lead` accepts a member display name, email, or UUID from `plane members list`.
Expand Down
62 changes: 57 additions & 5 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: plane-cli
description: >
Use when working with Plane project management via the `plane` CLI. Covers
listing/creating/updating/deleting issues, managing cycles, modules, pages,
intake, comments, worklogs, links, states, labels, and members. Works with
any Plane instance (cloud or self-hosted). Supports structured --xml/--json
output for AI agents.
intake, comments, worklogs, links, states, labels, members, and project
stats/analytics. Works with any Plane instance (cloud or self-hosted).
Supports structured --xml/--json output for AI agents.
---

# Plane CLI Skill Guide
Expand All @@ -31,6 +31,7 @@ For path-local overrides in the current directory:

```bash
plane init --local
plane init --local --include-archived
plane . init
```

Expand All @@ -43,6 +44,7 @@ PLANE_* environment variables > nearest .plane/config.json > ~/.config/plane/con
`plane init --local` also fetches the project's feature flags from Plane and reports which project-scoped features are actually enabled. Cycles, modules, pages, and intake commands fail with explicit feature-disabled errors when the project has them turned off.
It also writes `.plane/project-context.json`, a machine-readable helper snapshot of the project's existing states, labels, and estimate points so agents can reuse current project conventions instead of creating duplicates.
It also creates or updates `AGENTS.md` in that directory with a managed Plane context section at the bottom so AI agents know to read `.plane/project-context.json`, prefer the repo-local `plane` CLI, and clear inherited `PLANE_*` overrides before relying on local project config.
Project lists and project-selection prompts exclude archived projects by default. Add `--include-archived` to `plane init`, `plane . init`, `plane projects list`, or `plane stats ... workspace` when you intentionally need archived projects included.

Or set environment variables (override saved config):

Expand All @@ -57,6 +59,7 @@ You can also save a current project explicitly:

```bash
plane projects list
plane projects list --include-archived
plane projects use PROJ
plane projects use PROJ --local
plane projects use PROJ --global
Expand Down Expand Up @@ -91,6 +94,7 @@ All list commands support `--xml` and `--json` flags.
plane projects list --xml
plane issues list PROJ --xml
plane issues list PROJ --state started --xml
plane stats --json PROJ
plane states list PROJ --xml
plane labels list PROJ --xml
plane members list --xml
Expand All @@ -111,6 +115,7 @@ plane modules list PROJ --xml

```bash
plane projects list
plane projects list --include-archived
plane projects use PROJ
plane projects use PROJ --local
plane projects current
Expand All @@ -130,6 +135,9 @@ plane issues list PROJ --state started
plane issues list PROJ --state backlog
plane issues list PROJ --assignee "Jane Doe"
plane issues list PROJ --priority high
plane issues list PROJ --no-assignee
plane issues list PROJ --stale 7
plane issues list PROJ --cycle "Week 14"
plane issues list PROJ --xml
```

Expand All @@ -149,7 +157,11 @@ plane issue create --title "Issue title" PROJ
plane issue create --priority high --state started --title "Fix lint pipeline"
plane issue create --description '<p>Detailed context</p>' --title "Add dark mode" PROJ
plane issue create --assignee "Jane Doe" --title "Onboarding bug" PROJ
plane issue create --label "bug" --title "Regression in login flow" PROJ
plane issue create --label "bug" --label "urgent" --title "Regression in login flow" PROJ
plane issue create --start-date 2025-04-01 --target-date 2025-04-14 --title "Sprint task" PROJ
plane issue create --estimate <UUID> --title "Sized work" PROJ
plane issue create --cycle "Week 14" --title "Scoped to cycle" PROJ
plane issue create --module "Sprint 3" --title "Scoped to module" PROJ
```

### Update
Expand All @@ -166,6 +178,11 @@ plane issue update --description '<p>Updated context</p>' PROJ-29
plane issue update --assignee "Jane Doe" PROJ-29
plane issue update --no-assignee PROJ-29
plane issue update --label "enhancement" PROJ-29
plane issue update --label "bug" --label "critical" PROJ-29
plane issue update --start-date 2025-04-01 --target-date 2025-04-14 PROJ-29
plane issue update --estimate <UUID> PROJ-29
plane issue update --cycle "Week 14" PROJ-29
plane issue update --module "Sprint 3" PROJ-29
```

### Delete
Expand Down Expand Up @@ -251,17 +268,43 @@ Members are workspace-scoped. This command does not take a project argument.

---

## Stats (analytics)

```bash
plane stats
plane stats PROJ
plane stats --since 2025-01-01 --until 2025-02-01 PROJ
plane stats --cycle "Sprint 1" PROJ
plane stats --module "Sprint 3" PROJ
plane stats --assignee Alice PROJ
plane stats workspace
plane stats --include-archived workspace
plane stats --since 2025-01-01 workspace --json
plane stats workspace --xml
```

Aggregates issues client-side by state group, priority, assignment, and period counts. Supports `--since`/`--until` date filtering, cycle/module scoping, assignee filtering, and the special `workspace` target for cross-project totals. Workspace stats exclude archived projects by default; add `--include-archived` to opt in. `--json` returns a structured object with `total_issues`, `by_state_group`, `by_priority`, `created_in_range`, `completed_in_range`, `assigned`, and `unassigned` counts.

For `plane stats`, command-specific options must come before `PROJ` or `workspace` because of `@effect/cli` parsing rules. Workspace aggregation skips projects that return `403` for issue listing and reports them in the output.

---

## Cycles (sprints)

```bash
plane cycles list
plane cycles list PROJ
plane cycles list PROJ --xml
plane cycles create --name "Week 14" --start-date 2025-04-01 --end-date 2025-04-07 PROJ
plane cycles update --end-date 2025-04-08 PROJ "Week 14"
plane cycles delete PROJ "Week 14"
plane cycles issues list PROJ <cycle-id>
plane cycles issues add PROJ <cycle-id> PROJ-29
```

Cycle IDs are UUIDs. Fetch them from `plane cycles list PROJ`.
Cycle create/update/delete accept cycle names for convenience — the CLI resolves names to UUIDs internally.
`plane cycles list --json` includes `total_issues`, `completed_issues`, and `cancelled_issues` counts plus a computed status (draft, upcoming, current, completed).

---

Expand Down Expand Up @@ -328,8 +371,14 @@ Some deployments do not expose page endpoints even when the project advertises p
| `state_detail` | Always null — ignore |
| `priority` | `urgent`, `high`, `medium`, `low`, `none` |
| `assignees` | Array of user UUIDs |
| `labels` | Array of label objects (`id`, `name`, `color`) |
| `label_ids` | Array of label UUIDs |
| `due_date` | null or ISO date string |
| `start_date` | null or ISO date string |
| `target_date` | null or ISO date string |
| `completed_at` | null or ISO timestamp (when issue moved to completed state group) |
| `created_at` | ISO timestamp |
| `updated_at` | ISO timestamp |
| `estimate_point` | null or estimate value |

---

Expand All @@ -338,6 +387,9 @@ Some deployments do not expose page endpoints even when the project advertises p
- No server-side text search — fetch all issues and filter locally.
- No epics — use labels or modules to group related issues.
- `description` in issue or page create and update flows is passed through to `description_html`; send HTML such as `<p>Details</p>` when you want formatted output.
- `--target-date` has an alias `--due-date` for convenience.
- `--label` can be specified multiple times for multi-label assignment.
- `--cycle` and `--module` accept either a UUID or the exact name listed by `plane cycles list` / `plane modules list`. The CLI resolves names internally.
- `plane modules create --lead` accepts a member display name, email, or UUID from `plane members list`.
- `plane modules create --status in_progress` is normalized to Plane's `in-progress` API value.
- Always fetch state/label/member IDs live — never hardcode UUIDs across workspaces.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.1.0",
"version": "1.2.0",
"description": "CLI for the Plane project management API",
"author": "Gabriel Reynold and Contributors",
"license": "MIT",
Expand Down
8 changes: 5 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ function request(
);
let url = `${host}/api/v1/workspaces/${workspace}/${path}`;

// Always expand state on issue list/get calls (not intake-issues/ or cycle-issues/)
if (method === "GET" && /(?:^|\/)(issues\/)/.test(path)) {
url += url.includes("?") ? "&expand=state" : "?expand=state";
// Always expand state and labels on issue list/get calls (not intake-issues/ or cycle-issues/)
if (method === "GET" && /(?:^|\/)(?:issues\/)/.test(path)) {
url += url.includes("?")
? "&expand=state,labels"
: "?expand=state,labels";
}

const headers: Record<string, string> = {
Expand Down
Loading
Loading