Skip to content

Conversation

@dheeru0198
Copy link
Member

@dheeru0198 dheeru0198 commented Oct 30, 2025

Description

Complete rewrite replacing OpenAPI-generated code with a hand-crafted, type-safe SDK.

Developer experience

  • Unified PlaneClient with resource-first API (client.work_items.list(), client.projects.create())
  • Full type annotations with Pydantic v2 for IDE autocomplete and validation
  • Consistent resource patterns (list, create, retrieve, update, delete) across endpoints
  • Centralized error handling (HttpError, ConfigurationError)

Architecture

  • Removed ~55k lines of generated boilerplate (*_api.py, api_client.py, configuration.py)
  • Introduced BaseResource for shared HTTP logic with retry/connection pooling
  • Organized resources into logical modules (work_items/, work_item_properties/, customers/)
  • Modern Python 3.10+ typing, pyproject.toml packaging

Coverage

  • Resources: work items, projects, labels, states, modules, cycles, epics, pages, users, workspaces, customers, intake, work item types/properties
  • Comprehensive test suite (unit + integration scripts) covering major workflows

Breaking change: Not backward compatible. See README.md for migration guide.

Type of Change

  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring

Test Scenarios

Verified by running test suite at tests/ folder

References

SILO-598

Summary by CodeRabbit

  • Documentation
    • Major README rewrite and new SDK architecture/agents docs; removed many autogenerated model/API reference pages to streamline docs and present a Python-first SDK.
    • Added runnable examples and setup guidance demonstrating project setup, work item creation, and OAuth flows.
  • Chores
    • Added an MIT-style LICENSE.
    • Expanded .gitignore with additional common patterns.
  • Style
    • Simplified the package's public export surface for a leaner, more ergonomic SDK.

Note

Replaces OpenAPI-generated client with a resource-first, Pydantic v2–typed SDK, reorganizes models/resources, updates packaging, and adds comprehensive tests (breaking).

  • SDK/Core:
    • Replace generated client with handcrafted, resource-first PlaneClient (Python 3.10+, Pydantic v2).
    • Centralize request/pagination/query handling (e.g., query_params.py, pagination.py).
  • Models/Resources:
    • Add typed models and resources: work_items, projects, labels, states, modules, cycles, epics, pages, users, workspaces, customers, intake, work_item_types, work_item_properties.
    • Introduce consolidated enums in enums.py.
  • Packaging/Build:
    • Migrate to pyproject.toml (new name/version), modern tooling (black/ruff/mypy), and simplified dependencies (requests, pydantic).
    • Remove legacy setup.py, rest.py, tox/poetry configs.
  • Tests:
    • Add extensive unit tests and runnable integration scripts under tests/ for end‑to‑end workflows.
  • Removals/Cleanup:
    • Delete large set of OpenAPI-generated files and oauth/rest scaffolding.
  • Breaking:
    • Non‑backward compatible API surface; new resource methods and types.

Written by Cursor Bugbot for commit e610057. This will update automatically on new commits. Configure here.

    - Introduced new test scripts for comprehensive testing of various SDK functionalities.
    - Updated README.md to reflect changes and added a link to the documentation.
    - Enhanced model configurations to allow extra fields in Pydantic models.
    - Added LICENSE file for project licensing.
@makeplane
Copy link

makeplane bot commented Oct 30, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces 100+ autogenerated OpenAPI Markdown docs with a curated Plane Python SDK: adds MIT LICENSE, updates README and agents architecture for PlaneClient/Pydantic v2, adds examples and setup guides, updates package exports in plane/__init__.py, and refines .gitignore.

Changes

Cohort / File(s) Summary
Repository meta
\.gitignore, LICENSE
Add miscellaneous ignore patterns (.DS_Store, *.pem, .history, tsconfig.tsbuildinfo) and an MIT-style LICENSE.
Top-level docs & SDK narrative
README.md, agents.md
Replace README with Plane Python SDK-centric documentation (PlaneClient, Pydantic v2, migration guidance) and add agents.md describing SDK architecture, BaseResource, resources, errors, configuration, and development/testing guidance.
Examples & onboarding
examples/README.md, examples/SETUP.md, examples/setup_project.py, examples/create_work_items.py, examples/oauth_example.py
Add examples README and SETUP guide plus runnable example scripts demonstrating client initialization, project/state/label/work-item creation flows and multiple OAuth patterns with structured error handling.
Package public surface
plane/__init__.py
Replace legacy generated exports with a streamlined public surface: export PlaneClient, OAuth types, Configuration, selected high-level API modules (WorkItems, WorkItemTypes, WorkItemProperties, Projects, Labels, States, Users, Modules, Cycles, Pages) and error types; remove many legacy Api and model exports.
Removed autogenerated docs
docs/*
e.g., docs/AccessBd4Enum.md, docs/AssetsApi.md, docs/Cycle*.md, docs/Issue*.md, docs/PagesApi.md, docs/Paginated*Response.md, docs/Patched*Request.md, ...
Delete a large collection (~100+) of OpenAPI-generated Markdown documentation files for models, enums, request/response types, and API endpoints (property tables, endpoint docs, and example blocks removed).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as Developer
  participant Client as PlaneClient
  participant Res as BaseResource
  participant HTTP as HTTP Layer
  participant API as Plane API

  rect #E8F4FF
    Note right of Client: New high-level SDK entrypoint\n(PlaneClient + typed models)
    Dev->>Client: instantiate(base_url, api_key | access_token)
  end

  rect #FFF7E6
    Client->>Res: call resource.method(params)
    Res->>HTTP: build request (URL, headers, body ← Pydantic)
    HTTP->>API: send HTTP request (sync)
    API-->>HTTP: response (200 / 4xx / 5xx)
    HTTP-->>Res: parse JSON → Pydantic model or raise HttpError
    Res-->>Client: return model instance or raise PlaneError
    Client-->>Dev: return value / propagate error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas to focus review on:
    • plane/__init__.py — confirm new exports exist, import paths are correct, and no circular imports.
    • README.md & agents.md — verify code snippets, authentication one-of rule, and API/resource names.
    • examples/*.py — ensure examples import the declared public surface and referenced error types.
    • Large deletion set docs/* — check no unique documentation or usage examples were unintentionally removed.

"🐇
I hopped through docs both old and new,
Tossed stale schemas, planted guides true.
PlaneClient hums with Pydantic glow,
Carrots of examples help code grow. 🥕"

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "[SILO-598] feat: Handcrafted SDK v0.2.0" is directly aligned with the primary change in this pull request. The changeset implements a complete architectural overhaul, replacing the OpenAPI-generated SDK with a hand-crafted, type-safe Python SDK version 0.2.0. The title accurately captures this main transformation—moving from auto-generated boilerplate code to a unified PlaneClient with a resource-first API design. The title is concise, clear, and provides sufficient context for developers scanning the history to understand the fundamental nature of this change without requiring excessive detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sdk-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
agents.md (1)

287-317: Clarify type hint for timeout in Configuration.

The timeout parameter on line 309 should use a more specific type hint to match the README patterns and improve clarity for SDK users.

Update line 309 to be explicit about the tuple structure:

   def __init__(
     self,
     *,
     base_path: str,
     api_key: str | None = None,
     access_token: str | None = None,
-    timeout: float | tuple | None = 30.0,
+    timeout: float | tuple[float, float] | None = 30.0,
     retry: RetryConfig | None = None,
   ) -> None:

This matches the timeout type shown in README.md (line 781: float \| tuple[float, float]) and provides clearer guidance for requests timeout handling (connect timeout, read timeout).

README.md (1)

56-56: Add language identifier to fenced code block.

The code fence on line 56 lacks a language specifier, impacting markdown rendering and syntax highlighting clarity.

Apply this diff:

-```
+```
 # v0.1.x (OpenAPI-generated)

(Add python or appropriate language identifier after the opening fence.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5c95f1 and e8d9cc9.

📒 Files selected for processing (101)
  • .gitignore (1 hunks)
  • LICENSE (1 hunks)
  • README.md (1 hunks)
  • agents.md (1 hunks)
  • docs/AccessBd4Enum.md (0 hunks)
  • docs/AccessEnum.md (0 hunks)
  • docs/AssetsApi.md (0 hunks)
  • docs/Cycle.md (0 hunks)
  • docs/CycleCreateRequest.md (0 hunks)
  • docs/CycleIssue.md (0 hunks)
  • docs/CycleIssueRequestRequest.md (0 hunks)
  • docs/CycleLite.md (0 hunks)
  • docs/CyclesApi.md (0 hunks)
  • docs/EntityTypeEnum.md (0 hunks)
  • docs/Epic.md (0 hunks)
  • docs/EpicsApi.md (0 hunks)
  • docs/GenericAssetUploadRequest.md (0 hunks)
  • docs/GetWorkspaceMembers200ResponseInner.md (0 hunks)
  • docs/GroupEnum.md (0 hunks)
  • docs/IntakeApi.md (0 hunks)
  • docs/IntakeIssue.md (0 hunks)
  • docs/IntakeIssueCreateRequest.md (0 hunks)
  • docs/IntakeIssueStatusEnum.md (0 hunks)
  • docs/IntakeWorkItemStatusEnum.md (0 hunks)
  • docs/Issue.md (0 hunks)
  • docs/IssueActivity.md (0 hunks)
  • docs/IssueAttachment.md (0 hunks)
  • docs/IssueAttachmentUploadRequest.md (0 hunks)
  • docs/IssueComment.md (0 hunks)
  • docs/IssueCommentCreateRequest.md (0 hunks)
  • docs/IssueDetail.md (0 hunks)
  • docs/IssueExpand.md (0 hunks)
  • docs/IssueForIntakeRequest.md (0 hunks)
  • docs/IssueLink.md (0 hunks)
  • docs/IssueLinkCreateRequest.md (0 hunks)
  • docs/IssuePropertyAPI.md (0 hunks)
  • docs/IssuePropertyAPIRelationTypeEnum.md (0 hunks)
  • docs/IssuePropertyAPIRequest.md (0 hunks)
  • docs/IssuePropertyOptionAPI.md (0 hunks)
  • docs/IssuePropertyOptionAPIRequest.md (0 hunks)
  • docs/IssuePropertyValueAPI.md (0 hunks)
  • docs/IssuePropertyValueAPIDetail.md (0 hunks)
  • docs/IssuePropertyValueAPIRequest.md (0 hunks)
  • docs/IssueRelation.md (0 hunks)
  • docs/IssueRelationCreateRelationTypeEnum.md (0 hunks)
  • docs/IssueRelationCreateRequest.md (0 hunks)
  • docs/IssueRelationRemoveRequest.md (0 hunks)
  • docs/IssueRelationResponse.md (0 hunks)
  • docs/IssueRequest.md (0 hunks)
  • docs/IssueSearch.md (0 hunks)
  • docs/IssueSearchItem.md (0 hunks)
  • docs/IssueTypeAPI.md (0 hunks)
  • docs/IssueTypeAPIRequest.md (0 hunks)
  • docs/IssueWorkLogAPI.md (0 hunks)
  • docs/IssueWorkLogAPIRequest.md (0 hunks)
  • docs/Label.md (0 hunks)
  • docs/LabelCreateUpdateRequest.md (0 hunks)
  • docs/LabelLite.md (0 hunks)
  • docs/LabelsApi.md (0 hunks)
  • docs/MembersApi.md (0 hunks)
  • docs/Module.md (0 hunks)
  • docs/ModuleCreateRequest.md (0 hunks)
  • docs/ModuleIssue.md (0 hunks)
  • docs/ModuleIssueRequestRequest.md (0 hunks)
  • docs/ModuleLite.md (0 hunks)
  • docs/ModuleStatusEnum.md (0 hunks)
  • docs/ModulesApi.md (0 hunks)
  • docs/NetworkEnum.md (0 hunks)
  • docs/NullEnum.md (0 hunks)
  • docs/PageCreateAPI.md (0 hunks)
  • docs/PageCreateAPIAccessEnum.md (0 hunks)
  • docs/PageCreateAPIRequest.md (0 hunks)
  • docs/PageDetailAPI.md (0 hunks)
  • docs/PageDetailAPIRequest.md (0 hunks)
  • docs/PagesApi.md (0 hunks)
  • docs/PaginatedArchivedCycleResponse.md (0 hunks)
  • docs/PaginatedArchivedModuleResponse.md (0 hunks)
  • docs/PaginatedCycleIssueResponse.md (0 hunks)
  • docs/PaginatedCycleResponse.md (0 hunks)
  • docs/PaginatedEpicResponse.md (0 hunks)
  • docs/PaginatedIntakeIssueResponse.md (0 hunks)
  • docs/PaginatedIssueActivityDetailResponse.md (0 hunks)
  • docs/PaginatedIssueActivityResponse.md (0 hunks)
  • docs/PaginatedIssueCommentResponse.md (0 hunks)
  • docs/PaginatedIssueLinkDetailResponse.md (0 hunks)
  • docs/PaginatedIssueLinkResponse.md (0 hunks)
  • docs/PaginatedLabelResponse.md (0 hunks)
  • docs/PaginatedModuleIssueResponse.md (0 hunks)
  • docs/PaginatedModuleResponse.md (0 hunks)
  • docs/PaginatedProjectResponse.md (0 hunks)
  • docs/PaginatedStateResponse.md (0 hunks)
  • docs/PaginatedWorkItemResponse.md (0 hunks)
  • docs/PatchedAssetUpdateRequest.md (0 hunks)
  • docs/PatchedCycleUpdateRequest.md (0 hunks)
  • docs/PatchedGenericAssetUpdateRequest.md (0 hunks)
  • docs/PatchedIntakeIssueUpdateRequest.md (0 hunks)
  • docs/PatchedIssueCommentCreateRequest.md (0 hunks)
  • docs/PatchedIssueLinkUpdateRequest.md (0 hunks)
  • docs/PatchedIssuePropertyAPIRequest.md (0 hunks)
  • docs/PatchedIssuePropertyOptionAPIRequest.md (0 hunks)
  • docs/PatchedIssueRequest.md (0 hunks)
⛔ Files not processed due to max files limit (43)
  • docs/PatchedIssueTypeAPIRequest.md
  • docs/PatchedIssueWorkLogAPIRequest.md
  • docs/PatchedLabelCreateUpdateRequest.md
  • docs/PatchedModuleUpdateRequest.md
  • docs/PatchedProjectUpdateRequest.md
  • docs/PatchedStateRequest.md
  • docs/PriorityEnum.md
  • docs/Project.md
  • docs/ProjectCreateRequest.md
  • docs/ProjectWorklogSummary.md
  • docs/ProjectsApi.md
  • docs/PropertyTypeEnum.md
  • docs/RelationTypeEnum.md
  • docs/RetrieveIssueAttachment400Response.md
  • docs/RetrieveWorkItemAttachment400Response.md
  • docs/State.md
  • docs/StateLite.md
  • docs/StateRequest.md
  • docs/StatesApi.md
  • docs/TimezoneEnum.md
  • docs/TransferCycleIssueRequestRequest.md
  • docs/TransferCycleIssues200Response.md
  • docs/TransferCycleIssues400Response.md
  • docs/TransferCycleWorkItems200Response.md
  • docs/TransferCycleWorkItems400Response.md
  • docs/TypeEnum.md
  • docs/UserAssetUploadRequest.md
  • docs/UserLite.md
  • docs/UsersApi.md
  • docs/WorkItemActivityApi.md
  • docs/WorkItemAttachmentsApi.md
  • docs/WorkItemCommentsApi.md
  • docs/WorkItemLinksApi.md
  • docs/WorkItemPropertiesApi.md
  • docs/WorkItemTypesApi.md
  • docs/WorkItemWorklogsApi.md
  • docs/WorkItemsApi.md
  • docs/WorkspacesApi.md
  • git_push.sh
  • openapitools.json
  • plane/init.py
  • plane/api/init.py
  • plane/api/assets_api.py
💤 Files with no reviewable changes (97)
  • docs/EpicsApi.md
  • docs/PaginatedCycleResponse.md
  • docs/ModuleIssue.md
  • docs/IssueExpand.md
  • docs/IssueActivity.md
  • docs/IssueLink.md
  • docs/ModuleCreateRequest.md
  • docs/PaginatedEpicResponse.md
  • docs/GetWorkspaceMembers200ResponseInner.md
  • docs/PageDetailAPI.md
  • docs/IssuePropertyAPIRequest.md
  • docs/IssueCommentCreateRequest.md
  • docs/PaginatedArchivedCycleResponse.md
  • docs/PatchedGenericAssetUpdateRequest.md
  • docs/IssuePropertyOptionAPIRequest.md
  • docs/IssueTypeAPIRequest.md
  • docs/EntityTypeEnum.md
  • docs/PaginatedStateResponse.md
  • docs/PageCreateAPI.md
  • docs/PaginatedIssueActivityDetailResponse.md
  • docs/LabelCreateUpdateRequest.md
  • docs/IssuePropertyAPI.md
  • docs/Label.md
  • docs/Epic.md
  • docs/IssueRelationResponse.md
  • docs/PatchedAssetUpdateRequest.md
  • docs/PaginatedModuleIssueResponse.md
  • docs/IssueDetail.md
  • docs/Issue.md
  • docs/AccessBd4Enum.md
  • docs/PageCreateAPIAccessEnum.md
  • docs/LabelLite.md
  • docs/IssueRelationCreateRelationTypeEnum.md
  • docs/PaginatedIssueActivityResponse.md
  • docs/PatchedIntakeIssueUpdateRequest.md
  • docs/NullEnum.md
  • docs/IntakeIssue.md
  • docs/IssueRelation.md
  • docs/PaginatedLabelResponse.md
  • docs/ModuleStatusEnum.md
  • docs/PatchedIssuePropertyAPIRequest.md
  • docs/IssueWorkLogAPIRequest.md
  • docs/PatchedIssueCommentCreateRequest.md
  • docs/IssueWorkLogAPI.md
  • docs/PageDetailAPIRequest.md
  • docs/PatchedIssueRequest.md
  • docs/ModulesApi.md
  • docs/IssuePropertyValueAPI.md
  • docs/IssueRelationRemoveRequest.md
  • docs/CyclesApi.md
  • docs/PatchedCycleUpdateRequest.md
  • docs/IssueRequest.md
  • docs/IssueTypeAPI.md
  • docs/PatchedIssuePropertyOptionAPIRequest.md
  • docs/PaginatedCycleIssueResponse.md
  • docs/PaginatedIssueLinkResponse.md
  • docs/PaginatedIssueLinkDetailResponse.md
  • docs/LabelsApi.md
  • docs/PaginatedModuleResponse.md
  • docs/IssueForIntakeRequest.md
  • docs/IssueComment.md
  • docs/MembersApi.md
  • docs/GenericAssetUploadRequest.md
  • docs/ModuleIssueRequestRequest.md
  • docs/IntakeIssueCreateRequest.md
  • docs/IssueSearchItem.md
  • docs/IssueLinkCreateRequest.md
  • docs/GroupEnum.md
  • docs/CycleIssue.md
  • docs/CycleLite.md
  • docs/IssuePropertyOptionAPI.md
  • docs/CycleCreateRequest.md
  • docs/IssuePropertyAPIRelationTypeEnum.md
  • docs/AssetsApi.md
  • docs/PaginatedIssueCommentResponse.md
  • docs/Module.md
  • docs/PaginatedWorkItemResponse.md
  • docs/IssuePropertyValueAPIDetail.md
  • docs/IssuePropertyValueAPIRequest.md
  • docs/PaginatedProjectResponse.md
  • docs/IssueSearch.md
  • docs/PatchedIssueLinkUpdateRequest.md
  • docs/IssueRelationCreateRequest.md
  • docs/IntakeApi.md
  • docs/PaginatedArchivedModuleResponse.md
  • docs/PagesApi.md
  • docs/IssueAttachmentUploadRequest.md
  • docs/PaginatedIntakeIssueResponse.md
  • docs/AccessEnum.md
  • docs/CycleIssueRequestRequest.md
  • docs/IntakeIssueStatusEnum.md
  • docs/NetworkEnum.md
  • docs/IssueAttachment.md
  • docs/PageCreateAPIRequest.md
  • docs/ModuleLite.md
  • docs/IntakeWorkItemStatusEnum.md
  • docs/Cycle.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-30T18:13:46.240Z
Learnt from: Prashant-Surya
PR: makeplane/plane-python-sdk#5
File: plane/oauth/api.py:84-87
Timestamp: 2025-08-30T18:13:46.240Z
Learning: In the Plane Python SDK, the new ApiClient requires absolute URLs (host + path) to be passed to call_api() method, not relative paths. When using the new client, construct full URLs like f"{self.base_url}/auth/o/token/" rather than just "/auth/o/token/".

Applied to files:

  • README.md
🪛 LanguageTool
agents.md

[grammar] ~254-~254: Ensure spelling is correct
Context: ...e use. - Pyupgrade (UP): - Prefer builtin generics: list[str], dict[str, Any]...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~259-~259: Ensure spelling is correct
Context: ... MutableMapping, Set). - Prefer builtin concrete containers (list, dict, `s...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
README.md

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (9)
.gitignore (1)

67-72: LGTM!

The new ignore patterns are appropriate for a Python SDK project. The inclusion of .DS_Store, *.pem (protecting certificate/key files), and .history follows standard practices. The tsconfig.tsbuildinfo pattern is atypical for a Python project but harmless.

LICENSE (1)

1-7: LGTM!

Standard MIT license format; clearly establishes licensing terms for Plane Software, Inc.

agents.md (1)

514-597: Approve AI agent guidelines section.

The Rules for AI Agents and Models section (lines 514–597) clearly articulates SDK standards: strict type annotations, Pydantic v2 models, REST conventions, endpoint structure, naming rules, and testing requirements. This will help maintain consistency across future development.

README.md (6)

15-66: Comprehensive breaking changes section.

The breaking changes section (lines 15–66) clearly contrasts v0.1.x and v0.2.0, covering authentication, headers, resource paths, method names, models, pagination, query parameters, errors, imports, and trailing slashes. This will help users understand the migration path and new patterns.


124-157: Practical quick-start examples.

The quick-start section (lines 124–157) provides clear, runnable examples for authentication, basic operations (list, create, retrieve, update, delete), and query parameters. Well-structured for new users.


159-207: Clear architecture and type-safety documentation.

The architecture section explains PlaneClient structure, resource organization, and Pydantic v2 model conventions with good context for developers on why the SDK is designed this way.


209-362: Comprehensive resource and sub-resource examples.

The available resources section (lines 209–362) covers all major resources (users, workspaces, projects, work items, cycles, modules, states, labels, work item types/properties) with practical CRUD examples and sub-resource patterns. Well-organized and actionable.


786-890: Strong end-to-end workflow examples.

The examples section (lines 786–890) demonstrates real-world workflows (project creation, state/label setup, work item creation with relationships, cycles with work items, comments/attachments). These showcase SDK patterns and build confidence for users.


931-1003: Development and project structure well-documented.

The development section (lines 931–1003) covers setup, testing, code quality tooling (Black, Ruff, MyPy, Pytest), and project structure. Aligns with agents.md guidance and clarifies contributor expectations.

cursor[bot]

This comment was marked as outdated.

- Introduced `create_work_items.py` to demonstrate project creation, state and label setup, and work item management.
- Added `setup_project.py` for comprehensive project setup including work item types, states, and labels.
- Created `README.md` to provide an overview and usage instructions for examples.
- Added `SETUP.md` for environment setup guidance and troubleshooting tips.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
examples/README.md (1)

171-172: Format URLs as markdown links.

The bare URLs should be formatted as proper markdown links for better readability and to comply with markdown best practices.

Apply this diff:

-- **API documentation:** https://docs.plane.so
-- **Support:** dev@plane.so
+- **API documentation:** [https://docs.plane.so](https://docs.plane.so)
+- **Support:** [dev@plane.so](mailto:dev@plane.so)
examples/SETUP.md (2)

120-124: Specify language for fenced code block or use plain text.

The fenced code block showing the URL structure doesn't need to be in a code fence since it's illustrative text, not executable code.

Apply this diff to remove the fence or add a language identifier:

-```
-https://app.plane.so/<workspace-slug>/projects
-                      ^^^^^^^^^^^^^^^^^
-                      This is your workspace slug
-```
+Example URL structure:
+
+    https://app.plane.so/<workspace-slug>/projects
+                          ^^^^^^^^^^^^^^^^^
+                          This is your workspace slug

216-217: Format URLs as markdown links.

The bare URLs should be formatted as proper markdown links.

Apply this diff:

-- [Plane API Documentation](https://docs.plane.so)
-- [Plane Website](https://plane.so)
+- [Plane API Documentation](https://docs.plane.so)
+- [Plane Website](https://plane.so)

Note: The URLs are already properly formatted with markdown link syntax. The linter warning appears to be a false positive. However, if line 227 contains a bare URL elsewhere, please format it appropriately.

examples/setup_project.py (1)

24-30: Consider adding early validation for required environment variables.

While PlaneClient will raise a ConfigurationError if authentication is missing, workspace_slug can be None, leading to less clear error messages when API calls fail later in the script.

Apply this diff to add early validation:

     workspace_slug = os.environ.get("WORKSPACE_SLUG")
+    
+    if not workspace_slug:
+        print("✗ Error: WORKSPACE_SLUG environment variable is required")
+        print("Please set it using: export WORKSPACE_SLUG='your-workspace-slug'")
+        return
 
     print("=" * 60)
examples/create_work_items.py (1)

24-30: Consider adding early validation for required environment variables.

While PlaneClient will raise a ConfigurationError if authentication is missing, workspace_slug can be None, leading to less clear error messages when API calls fail later in the script.

Apply this diff to add early validation:

     workspace_slug = os.environ.get("WORKSPACE_SLUG")
+    
+    if not workspace_slug:
+        print("✗ Error: WORKSPACE_SLUG environment variable is required")
+        print("Please set it using: export WORKSPACE_SLUG='your-workspace-slug'")
+        return
 
     print("=" * 60)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8d9cc9 and 7f3f9b8.

📒 Files selected for processing (4)
  • examples/README.md (1 hunks)
  • examples/SETUP.md (1 hunks)
  • examples/create_work_items.py (1 hunks)
  • examples/setup_project.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
examples/setup_project.py (6)
tests/unit/conftest.py (4)
  • client (43-52)
  • base_url (13-18)
  • api_key (22-24)
  • workspace_slug (34-39)
plane/client.py (1)
  • PlaneClient (19-55)
plane/models/labels.py (1)
  • CreateLabel (28-39)
plane/models/projects.py (1)
  • CreateProject (60-85)
plane/models/states.py (1)
  • CreateState (42-55)
plane/models/work_item_types.py (1)
  • CreateWorkItemType (30-41)
examples/create_work_items.py (6)
tests/unit/conftest.py (4)
  • client (43-52)
  • base_url (13-18)
  • api_key (22-24)
  • workspace_slug (34-39)
plane/client.py (1)
  • PlaneClient (19-55)
plane/models/labels.py (1)
  • CreateLabel (28-39)
plane/models/projects.py (1)
  • CreateProject (60-85)
plane/models/states.py (1)
  • CreateState (42-55)
plane/models/work_items.py (1)
  • CreateWorkItem (126-153)
🪛 markdownlint-cli2 (0.18.1)
examples/SETUP.md

120-120: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


227-227: Bare URL used

(MD034, no-bare-urls)

examples/README.md

171-171: Bare URL used

(MD034, no-bare-urls)


172-172: Bare URL used

(MD034, no-bare-urls)

- Changed all instances of `BASE_URL`, `API_KEY`, and `ACCESS_TOKEN` to `PLANE_BASE_URL`, `PLANE_API_KEY`, and `PLANE_ACCESS_TOKEN` respectively across test scripts and README files.
- Updated usage instructions to reflect the new environment variable names for consistency and clarity.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
agents.md (1)

517-607: AI agent rules are comprehensive and establish clear conventions.

The rules codify naming, architecture, testing, and documentation standards. One minor enhancement: clarify or link the "Never use the word Issue" rule (line 559) to the broader API terminology standard (e.g., add a note that this applies to API responses, parameters, and documentation to avoid confusion).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f3f9b8 and fa555f6.

📒 Files selected for processing (1)
  • agents.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
agents.md

[grammar] ~254-~254: Ensure spelling is correct
Context: ...e use. - Pyupgrade (UP): - Prefer builtin generics: list[str], dict[str, Any]...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~259-~259: Ensure spelling is correct
Context: ... MutableMapping, Set). - Prefer builtin concrete containers (list, dict, `s...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
agents.md

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (4)
agents.md (4)

13-52: Solid client initialization pattern with resource composition.

The PlaneClient example demonstrates a clean resource-first API and proper configuration injection. Type hints are precise and follow the stated guidelines.


94-183: BaseResource implementation is well-architected for centralized HTTP logic.

The approach—with session pooling, retry configuration, unified response handling, and clear error semantics—aligns well with the stated development goals and reduces boilerplate across resources. The trailing-slash normalization in _build_url (lines 154–155) is a good touch for consistency.


243-268: Development guidelines are comprehensive and follow modern Python standards.

The ruff lint rules section is clear and actionable, covering imports, type hints, naming conventions, and error handling. Enforcing these rules via pre-commit will maintain consistency across the codebase.


445-474: Pydantic v2 model patterns are well-defined and appropriate.

The separation of response models (with extra="allow" for forward compatibility) from Create/Update DTOs (with extra="ignore" to reject unknown fields) is a sound practice. Field validation rules are clear.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
agents.md (1)

56-56: Add language specifier to fenced code block.

The folder structure block should declare a language identifier to satisfy markdown linting and improve syntax highlighting. Use text or plaintext since it represents a directory structure, not executable code.

-```
+```text
 plane/
     __init__.py
🧹 Nitpick comments (1)
examples/SETUP.md (1)

120-124: Add language specifier to code block.

The example URL pattern block is missing a language identifier. Since it represents plain text output rather than executable code, use ```text.

-```
+```text
 https://app.plane.so/<workspace-slug>/projects
                       ^^^^^^^^^^^^^^^^^
                       This is your workspace slug
-```
+```
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa555f6 and 8c47052.

📒 Files selected for processing (2)
  • agents.md (1 hunks)
  • examples/SETUP.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
agents.md

[grammar] ~254-~254: Ensure spelling is correct
Context: ...e use. - Pyupgrade (UP): - Prefer builtin generics: list[str], dict[str, Any]...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~259-~259: Ensure spelling is correct
Context: ... MutableMapping, Set). - Prefer builtin concrete containers (list, dict, `s...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
agents.md

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

examples/SETUP.md

120-120: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (1)
examples/SETUP.md (1)

29-92: The test fixtures have already been updated to use PLANE_BASE_URL, PLANE_API_KEY, and PLANE_ACCESS_TOKEN consistently. No naming inconsistencies remain—users following the SETUP.md guide will set the correct environment variables that the tests expect.

- Introduced `OAuthClient` for handling OAuth 2.0 flows including authorization code and client credentials.
- Added examples demonstrating OAuth usage in `oauth_example.py`.
- Updated `README.md` to include OAuth authentication details and usage instructions.
- Enhanced `examples/README.md` to guide users on running the OAuth examples.
- Implemented token management features such as refresh and revoke in the OAuth client.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c47052 and 8bce9be.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • examples/README.md (1 hunks)
  • examples/oauth_example.py (1 hunks)
  • plane/__init__.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-30T18:13:46.240Z
Learnt from: Prashant-Surya
Repo: makeplane/plane-python-sdk PR: 5
File: plane/oauth/api.py:84-87
Timestamp: 2025-08-30T18:13:46.240Z
Learning: In the Plane Python SDK, the new ApiClient requires absolute URLs (host + path) to be passed to call_api() method, not relative paths. When using the new client, construct full URLs like f"{self.base_url}/auth/o/token/" rather than just "/auth/o/token/".

Applied to files:

  • README.md
  • plane/__init__.py
🧬 Code graph analysis (2)
plane/__init__.py (3)
tests/unit/conftest.py (1)
  • client (43-52)
plane/client/oauth_client.py (6)
  • OAuthAuthorizationParams (26-35)
  • OAuthClient (77-360)
  • OAuthClientCredentialsParams (61-73)
  • OAuthRefreshTokenParams (50-58)
  • OAuthToken (14-23)
  • OAuthTokenExchangeParams (38-47)
plane/client/plane_client.py (1)
  • PlaneClient (19-55)
examples/oauth_example.py (2)
plane/client/oauth_client.py (7)
  • OAuthClient (77-360)
  • OAuthToken (14-23)
  • get_authorization_url (160-191)
  • exchange_code (193-220)
  • refresh_token (222-242)
  • get_client_credentials_token (244-272)
  • revoke_token (274-299)
plane/client/plane_client.py (1)
  • PlaneClient (19-55)
🪛 markdownlint-cli2 (0.18.1)
README.md

1026-1026: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


1064-1064: Bare URL used

(MD034, no-bare-urls)

examples/README.md

199-199: Bare URL used

(MD034, no-bare-urls)


200-200: Bare URL used

(MD034, no-bare-urls)

🪛 Ruff (0.14.2)
plane/__init__.py

23-45: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

examples/oauth_example.py

162-162: Possible hardcoded password assigned to argument: "access_token"

(S106)


163-163: Possible hardcoded password assigned to argument: "refresh_token"

(S106)


164-164: Possible hardcoded password assigned to argument: "token_type"

(S106)


177-177: Do not catch blind exception: Exception

(BLE001)


241-241: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot

- Modified `oauth_example.py` to replace workspace listing with project listing using the `workspace_slug` environment variable.
- Updated print statements to reflect the number of projects found instead of workspaces.
- Added documentation for the new `WORKSPACE_SLUG` environment variable requirement.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bce9be and e610057.

📒 Files selected for processing (1)
  • examples/oauth_example.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-30T18:13:46.240Z
Learnt from: Prashant-Surya
Repo: makeplane/plane-python-sdk PR: 5
File: plane/oauth/api.py:84-87
Timestamp: 2025-08-30T18:13:46.240Z
Learning: In the Plane Python SDK, the new ApiClient requires absolute URLs (host + path) to be passed to call_api() method, not relative paths. When using the new client, construct full URLs like f"{self.base_url}/auth/o/token/" rather than just "/auth/o/token/".

Applied to files:

  • examples/oauth_example.py
🧬 Code graph analysis (1)
examples/oauth_example.py (2)
plane/client/oauth_client.py (7)
  • OAuthClient (77-360)
  • OAuthToken (14-23)
  • get_authorization_url (160-191)
  • exchange_code (193-220)
  • refresh_token (222-242)
  • get_client_credentials_token (244-272)
  • revoke_token (274-299)
plane/client/plane_client.py (1)
  • PlaneClient (19-55)
🪛 Ruff (0.14.2)
examples/oauth_example.py

165-165: Possible hardcoded password assigned to argument: "access_token"

(S106)


166-166: Possible hardcoded password assigned to argument: "refresh_token"

(S106)


167-167: Possible hardcoded password assigned to argument: "token_type"

(S106)


180-180: Do not catch blind exception: Exception

(BLE001)


245-245: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (5)
examples/oauth_example.py (5)

1-10: LGTM: Clean imports and clear documentation.

The module docstring and imports are well-structured and appropriate for the OAuth examples.


153-182: LGTM: Clear token management demonstration.

The example effectively demonstrates token refresh and revocation patterns. The hardcoded token strings flagged by static analysis are appropriate placeholders for example code.


189-212: LGTM: Excellent demonstration of context manager pattern.

This example clearly shows automatic session cleanup through context manager usage, which is a best practice for resource management.


219-248: LGTM: Comprehensive error handling demonstration.

This example effectively demonstrates the error hierarchy with ConfigurationError, HttpError, and general Exception handling. The empty string defaults (lines 228-229) are well-suited for triggering errors in this demonstration context.


250-268: LGTM: Well-structured main block with helpful guidance.

The commented-out examples and clear environment variable documentation provide excellent developer experience. Users can safely run the file and selectively enable examples.

Comment on lines +56 to +64
workspace_slug = os.environ.get("WORKSPACE_SLUG", "your-workspace-slug")
plane_client = PlaneClient(
base_url=os.environ.get("PLANE_BASE_URL", "https://api.plane.so"),
access_token=token.access_token,
)

# Now you can use the plane_client to make API calls
projects = plane_client.projects.list(workspace_slug)
print(f"Found {len(projects.results)} projects")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace placeholder defaults with fail-fast environment variable access.

Multiple examples use os.environ.get("WORKSPACE_SLUG", "your-workspace-slug") with a placeholder default. This pattern creates poor developer experience because:

  • The placeholder value causes API calls to fail with unclear 404/authorization errors rather than immediate, actionable KeyError
  • It gives false confidence that the code might work without proper configuration

This partially addresses a past review concern about missing workspace_slug in the bot_token_example (lines 138-146), but the placeholder default still leads to delayed failures.

Consider either:

  1. Preferred: Use os.environ["WORKSPACE_SLUG"] for immediate failure with clear error message
  2. Alternative: Keep the current pattern but add prominent comments explaining these are non-functional placeholders

Apply this diff pattern across affected examples:

-    workspace_slug = os.environ.get("WORKSPACE_SLUG", "your-workspace-slug")
+    workspace_slug = os.environ["WORKSPACE_SLUG"]

Then update the main block documentation (line 266) to emphasize WORKSPACE_SLUG is required:

-    print("  - WORKSPACE_SLUG (required for API calls)")
+    print("  - WORKSPACE_SLUG (required)")

Also applies to: 100-108, 137-145, 202-209

🤖 Prompt for AI Agents
In examples/oauth_example.py around lines 56-64 (and also apply the same change
to ranges 100-108, 137-145, 202-209), the code uses
os.environ.get("WORKSPACE_SLUG", "your-workspace-slug") which masks missing
configuration; replace these get-with-default calls with
os.environ["WORKSPACE_SLUG"] so the process fails fast with a clear KeyError if
the env var is missing (or if you prefer to keep placeholders, add a prominent
inline comment stating the default is non-functional and must be replaced); also
update the main block documentation at line 266 to explicitly state that
WORKSPACE_SLUG is required and the examples will error if it is not set.

@Prashant-Surya Prashant-Surya merged commit 01115a1 into main Oct 31, 2025
2 checks passed
@Prashant-Surya Prashant-Surya deleted the sdk-v2 branch October 31, 2025 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants