|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Plane Python SDK (`plane-sdk` on PyPI, v0.2.4) — a synchronous, type-annotated Python client for the Plane API. Built on `requests` + `pydantic` v2, targeting Python 3.10+. |
| 8 | + |
| 9 | +## Common Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Install for development |
| 13 | +pip install -e . |
| 14 | +pip install -r requirements.txt |
| 15 | + |
| 16 | +# Run all unit tests (requires env vars, see below) |
| 17 | +pytest tests/unit/ |
| 18 | + |
| 19 | +# Run a specific test file or test |
| 20 | +pytest tests/unit/test_projects.py |
| 21 | +pytest tests/unit/test_projects.py::TestProjectsAPICRUD::test_create_project |
| 22 | + |
| 23 | +# Integration/script tests (excluded by default via addopts) |
| 24 | +pytest tests/scripts/ --override-ini="addopts=" |
| 25 | + |
| 26 | +# Formatting & linting |
| 27 | +black plane tests |
| 28 | +ruff check plane tests |
| 29 | +ruff check --fix plane tests |
| 30 | + |
| 31 | +# Type checking |
| 32 | +mypy plane |
| 33 | +``` |
| 34 | + |
| 35 | +### Required Environment Variables for Tests |
| 36 | + |
| 37 | +Tests make real HTTP requests (no mocking). Set these before running: |
| 38 | + |
| 39 | +- `PLANE_BASE_URL` — API base URL |
| 40 | +- `PLANE_API_KEY` or `PLANE_ACCESS_TOKEN` — authentication (exactly one) |
| 41 | +- `WORKSPACE_SLUG` — test workspace |
| 42 | +- `AGENT_SLUG` — (optional) needed only for agent run tests |
| 43 | + |
| 44 | +## Architecture |
| 45 | + |
| 46 | +### Client → Resource → Model pattern |
| 47 | + |
| 48 | +`PlaneClient` is the single entry point. It holds a `Configuration` and exposes resource objects as attributes: |
| 49 | + |
| 50 | +``` |
| 51 | +PlaneClient |
| 52 | + ├── .projects → Projects(BaseResource) |
| 53 | + ├── .work_items → WorkItems(BaseResource) |
| 54 | + │ ├── .comments |
| 55 | + │ ├── .attachments |
| 56 | + │ ├── .links |
| 57 | + │ └── ...sub-resources |
| 58 | + ├── .cycles → Cycles(BaseResource) |
| 59 | + └── ...15+ resources |
| 60 | +``` |
| 61 | + |
| 62 | +### Key directories |
| 63 | + |
| 64 | +- `plane/api/` — Resource classes. Every resource extends `BaseResource` which handles HTTP methods, auth headers, URL building (`/api/v1/...`), retry via `urllib3.Retry`, and response parsing. |
| 65 | +- `plane/models/` — Pydantic v2 models. Three kinds per resource: |
| 66 | + - **Response models** (e.g. `Project`): `extra="allow"` for forward compatibility with new API fields. |
| 67 | + - **Request DTOs** (e.g. `CreateProject`, `UpdateProject`): `extra="ignore"` to be strict about inputs. |
| 68 | + - **Query param models** (e.g. `PaginatedQueryParams`): `extra="ignore"`. |
| 69 | +- `plane/client/` — `PlaneClient` (API key / access token auth) and `OAuthClient` (OAuth 2.0 flows). |
| 70 | +- `plane/errors/` — `PlaneError` → `HttpError`, `ConfigurationError`. |
| 71 | +- `plane/config.py` — `Configuration` and `RetryConfig` dataclasses. |
| 72 | + |
| 73 | +### Sub-resource pattern |
| 74 | + |
| 75 | +Resources with children (work_items, customers, initiatives, teamspaces) instantiate sub-resource objects in `__init__`: |
| 76 | + |
| 77 | +```python |
| 78 | +class WorkItems(BaseResource): |
| 79 | + def __init__(self, config): |
| 80 | + super().__init__(config, "/workspaces/") |
| 81 | + self.comments = WorkItemComments(config) |
| 82 | + self.attachments = WorkItemAttachments(config) |
| 83 | +``` |
| 84 | + |
| 85 | +### URL convention |
| 86 | + |
| 87 | +All API endpoints end with a trailing `/`. URLs are built as `{base_path}/api/v1{resource_base_path}/{endpoint}/`. |
| 88 | + |
| 89 | +## Coding Conventions |
| 90 | + |
| 91 | +- Line length: 100 (Black + Ruff) |
| 92 | +- Use `X | None` not `Optional[X]`; use `list[str]` not `List[str]` (Python 3.10+ builtins) |
| 93 | +- Import abstract types from `collections.abc` (e.g. `Mapping`, `Iterable`) |
| 94 | +- Ruff rules: E, F, I (isort), UP (pyupgrade), B (bugbear) |
| 95 | +- Never use "Issue" in endpoint or parameter names — always use "Work Item" |
| 96 | +- Auth is mutually exclusive: `api_key` XOR `access_token` (raises `ConfigurationError` if both/neither) |
| 97 | +- Resource methods accept Pydantic DTOs, serialize with `model_dump(exclude_none=True)`, and validate responses with `Model.model_validate()` |
| 98 | +- All resources follow CRUD verbs: `create`, `retrieve`, `update`, `delete`, `list` |
0 commit comments