Claude
Skills
Sign in
Back

pm-integrations

Included with Lifetime
$97 forever

Connecting and syncing with GitHub Projects, Linear, Notion, Asana, Trello, ClickUp, Monday.com, Todoist, and Local

Productivity

What this skill does


# PM Integrations Skill

## Credential Handling

All PM platform credentials are read exclusively from environment variables injected by Claude Code's plugin userConfig system. The environment variable names follow the pattern `CLAUDE_PLUGIN_OPTION_{KEY}` where `{KEY}` is the uppercase version of the userConfig key defined in `plugin.json`. For example, the `github_token` userConfig entry is available at runtime as `CLAUDE_PLUGIN_OPTION_GITHUB_TOKEN`. Credentials are never hardcoded, never written to state files, never echoed in logs, and never included in research briefs or progress entries. If a credential is missing at sync time, the operation fails with a clear error naming the missing environment variable — it does not fall back to anonymous access or cached credentials.

## Sync Conflict Resolution Strategy

All integrations follow the same conflict resolution strategy: the plugin's internal task state is treated as the source of truth for task content (title, description, completion criteria, status). The external platform is treated as the source of truth for metadata that originates there (assignees, labels added externally, comments, external IDs). When a task is updated both internally and externally between syncs, the internal content wins for the fields the plugin owns (`title`, `description`, `status`, `priority`) and the external platform's values are preserved for fields the plugin does not manage (`assignees`, `external_labels`, `comments`). This means bidirectional sync is limited — the plugin pushes status changes outward and pulls external IDs inward, but does not attempt full two-way content sync.

## GitHub Projects

**Auth method:** Personal Access Token read from `CLAUDE_PLUGIN_OPTION_GITHUB_TOKEN`. The token requires `project` scope for GitHub Projects v2 API access and `repo` scope if the project is linked to a repository. Authentication is verified at connection time by calling the GraphQL `viewer` query; if it fails, the integration is aborted with a clear auth error.

**Key API operations:** GitHub Projects v2 uses a GraphQL API exclusively. Creating an issue: use the `createIssue` mutation on the linked repository, then add it to the project with `addProjectV2ItemById`. Updating status: use `updateProjectV2ItemFieldValue` with the Status field's ID. Listing tasks: use the `projectV2` query with item iteration, filtering by the project node ID. The project node ID is stored in `pm_integration.external_project_id` after initial connection.

**Field mapping:** Internal `title` → GitHub issue title. Internal `description` → issue body. Internal `status` maps to a GitHub project Status field: PENDING → "Todo", IN_PROGRESS → "In Progress", COMPLETE → "Done", BLOCKED → "Blocked" (custom field value; created if not present). Internal `priority` maps to a GitHub project Priority field if one exists; otherwise it is embedded as a label (`priority:critical`, `priority:high`, etc.). The internal `id` is stored as a custom text field named "pm-plugin-id" on the project item.

**Sync approach:** Push-first. On each sync cycle, all tasks with `status` changes since `last_sync_at` are pushed to GitHub first. Then tasks with a null `external_id` are created as new issues. Finally, any items in the GitHub project that do not have a "pm-plugin-id" field value matching a known internal task ID are logged as "untracked external items" in the progress log (they are not imported or deleted).

## Linear

**Auth method:** API Key read from `CLAUDE_PLUGIN_OPTION_LINEAR_API_KEY`. Linear uses this key in the `Authorization` header as a bearer token. Verified at connection by calling the `viewer` query on Linear's GraphQL API at `https://api.linear.app/graphql`.

**Key API operations:** Creating an issue: `issueCreate` mutation specifying `teamId`, `title`, `description`, and `stateId`. Updating status: `issueUpdate` mutation with the `stateId` corresponding to the target status. Listing tasks: `issues` query filtered by `teamId` and a label matching the project ID. The team ID and workflow state IDs are fetched once at connection time and cached in `pm_integration.external_project_id` as a JSON object (serialized).

**Field mapping:** Internal `title` → Linear issue title. Internal `description` → Linear issue description (markdown supported). Internal `priority` maps directly to Linear's priority integers: CRITICAL → 1 (Urgent), HIGH → 2 (High), MEDIUM → 3 (Medium), LOW → 4 (Low). Internal `status` maps to Linear workflow states: the mapping uses the state names "Todo," "In Progress," "Done," and "Cancelled" — if a team uses custom state names, the connection wizard prompts the user to map each internal status to the correct state. Internal `estimate_minutes` maps to Linear's cycle time estimate field (divided by 60, rounded to nearest 0.5).

**Sync approach:** Linear's webhook system is not available in the CLI context, so sync is polling-based. The sync cycle reads all issues in the team with the project label, compares their `updatedAt` timestamps against `last_sync_at`, and pulls status changes for issues whose external status differs from the internal status. Internal status is authoritative when both changed since last sync.

## Notion

**Auth method:** Integration Token read from `CLAUDE_PLUGIN_OPTION_NOTION_TOKEN`. Notion uses this token in the `Authorization: Bearer` header. The integration must be connected to the target Notion workspace page before it will have access. Verified at connection by calling `GET /v1/users/me`.

**Key API operations:** Creating a page (task) in a database: `POST /v1/pages` with `parent.database_id` set to the Notion database ID and properties matching the database schema. Updating properties: `PATCH /v1/pages/{page_id}`. Querying the database: `POST /v1/databases/{database_id}/query` with filter and sort options. Notion's API is REST-based and paginated; the sync layer handles cursor-based pagination automatically.

**Field mapping:** Internal `title` → Notion "Name" (title) property. Internal `status` → Notion "Status" select property with options matching internal status names. Internal `priority` → Notion "Priority" select property. Internal `description` → Notion page body as a paragraph block (sent as a Blocks write after page creation). Internal `estimate_minutes` → Notion "Estimate" number property (in minutes). Internal `tags` → Notion "Tags" multi-select property. The Notion database must have these property names; the connection wizard validates their presence and creates missing ones using the `PATCH /v1/databases/{database_id}` endpoint.

**Sync approach:** Notion does not support webhooks in the API tier available to integrations, so sync is polling-based using the `last_edited_time` property. Pages modified since `last_sync_at` are fetched and their status changes are reconciled using the conflict resolution strategy above.

## Asana

**Auth method:** Personal Access Token read from `CLAUDE_PLUGIN_OPTION_ASANA_TOKEN`. Sent in the `Authorization: Bearer` header. Verified at connection by calling `GET /api/1.0/users/me`. Asana also supports OAuth; the PAT is used here for simplicity in the CLI context.

**Key API operations:** Creating a task: `POST /api/1.0/tasks` with `workspace`, `name`, `notes`, `projects` (array), and `custom_fields`. Updating a task: `PUT /api/1.0/tasks/{task_gid}`. Listing tasks in a project: `GET /api/1.0/projects/{project_gid}/tasks`. Updating custom fields requires the custom field GID, fetched once at connection from `GET /api/1.0/projects/{project_gid}/custom_field_settings`.

**Field mapping:** Internal `title` → Asana task name. Internal `description` → Asana task notes (plain text; markdown is not rendered by Asana). Internal `status` maps to Asana task completion: COMPLETE → `completed: true`; all other statuses → `completed: false`. Finer-grained status (IN_PROGRESS, BLOCKED) is stored in a custom field named "Plugin Status." Internal `priority` maps to Asana's

Related in Productivity