Claude
Skills
Sign in
Back

pm-team

Included with Lifetime
$97 forever

Use this skill when the user says "sync the team", "who's on the team", "show the roster", "set my skills", "/pm-team", or asks about team members, member skills, or assignee recommendations. Manages the team roster — Linear-synced identity (gitignored cache) paired with a curated checked-in team.md of handle + skills + owns + aliases — merged at read time by handle. The roster is the data source for pm-organize assignee recommendations and pm-notes speaker matching.

General

What this skill does


# PM Team

The team roster is split across two storage locations that are merged at read time by handle.

- **Curated file (`<repo>/.claude/pm/team.md`, checked in):** non-PII — GitHub/Linear handle, skills, proficiency buckets, owned workstreams, and display aliases. This file is safe to commit.
- **Synced cache (`~/.claude/project-manager/cache/<slug>/team.json`, gitignored):** identity pulled from Linear — name, status, and the userId used for assignee mutations. This file is never committed.

The merge key is `handle` (case-insensitive). Members present in both halves are unified; members present only in the cache are surfaced with empty skill fields; curated handles with no cache match generate an `unmatchedHandles` warning.

## Usage

```
/pm-team sync
/pm-team show
/pm-team edit @handle
```

- **sync** — pull current workspace members from Linear and write them into the per-slug cache.
- **show** — merge the curated file with the cache and render a table of every member with warnings for any mismatches.
- **edit @handle** — open (or create) the curated entry for a handle, update its skill/owns/aliases bullets from operator input, preview the diff, and confirm before writing.

## Storage

### Curated file — `<repo>/.claude/pm/team.md`

Checked in alongside `project.json` and `conventions.md`. Contains only non-PII fields:

```markdown
# Team

## @scoussens
- skills: frontend, auth, billing
- owns: ws:billing, src/auth
- aliases: Seth, SC

## @jdoe
- strong: postgres, migrations
- familiar: frontend
- owns: ws:data
- aliases: John, JD
```

Each `## @handle` section opens a member entry. Bullet lines `- <field>: <comma-separated list>` populate the field. Recognised fields:

| Field | Meaning |
|---|---|
| `skills` | Flat skill list |
| `strong` | Skills the member is strong in (coarse proficiency — pm-proficiency writes these) |
| `familiar` | Skills the member is familiar with |
| `owns` | Owned workstreams or path prefixes used for scoring by pm-organize |
| `aliases` | Display names used by pm-notes speaker matching |

All fields are optional. A member block with no bullets is valid.

**Never put email addresses in team.md.** Email is an identity field that belongs only in the synced cache. If a line in team.md contains what looks like an email, the skill must reject it.

### Synced cache — `~/.claude/project-manager/cache/<slug>/team.json`

Gitignored runtime state written by `pm-cache.js team-sync`. Schema:

```json
{
  "version": "<cache-version>",
  "syncedAt": "<ISO-8601 timestamp>",
  "members": [
    { "handle": "scoussens", "name": "Seth Coussens", "status": "active", "userId": "u1" }
  ]
}
```

The `members` array contains identity fields sourced from the tracker. This file is never committed; it lives in the user-home cache directory only.

## sync

Resolve the active slug from `project.json`, then run:

```bash
node "$CLAUDE_PLUGIN_ROOT/hooks/bin/pm-cache.js" team-sync \
  --slug <slug> --provider linear
```

This query runs against `api.linear.app/graphql` using `LINEAR_API_KEY`. It paginates through all workspace users and writes `team.json` into the per-slug cache directory.

**Linear only.** If the active project uses GitHub as its tracker, stop and tell the operator: team-sync is Linear-only; GitHub does not expose a workspace-members API via the plugin's current GraphQL transport.

After writing, print the member count and `syncedAt` timestamp so the operator can confirm the sync completed.

## show

Read both halves and merge them via the roster library. Render a table of every member with columns: handle · name · status · skills · owns. Surface any `unmatchedHandles` (curated handles with no cache identity) and `unmappedMembers` (cache members not in team.md) as warnings below the table.

Use this one-liner to produce the merged JSON from Bash:

```bash
node -e '
  const fs = require("fs");
  const r = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/team-roster.js");
  const cs = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/cache-store.js");
  const slug = process.argv[1];
  const teamMdPath = process.argv[2];
  const curated = r.parseTeamMd(fs.existsSync(teamMdPath) ? fs.readFileSync(teamMdPath, "utf8") : "");
  const cache = cs.readTeam(slug) || { members: [] };
  const { roster, warnings } = r.mergeRoster(curated, cache.members);
  process.stdout.write(JSON.stringify({ roster, warnings }, null, 2));
' "<slug>" "<repo>/.claude/pm/team.md"
```

Replace `<slug>` with the active project slug and `<repo>` with the repo root path (from `git rev-parse --show-toplevel`).

If the cache has never been synced (`team.json` missing), render a table from the curated file only and note that `sync` should be run to add identity data.

## edit

1. Resolve the curated file path: `<repo>/.claude/pm/team.md`.
2. If the file does not exist, offer to create it with an empty `# Team` header.
3. Locate the `## @<handle>` section. If it is absent, append a new block at the end of the file.
4. Parse the operator's requested changes — new skills, removed skills, updated owns or aliases.
5. Preview the diff against the current file content.
6. Ask the operator to confirm before writing.
7. Write the updated block.

Reiterate before writing: **never write email addresses into team.md.** If the operator's input contains an email-shaped string, reject it and ask them to use only the non-PII fields (skills, owns, aliases).

## What this skill does NOT do

- **Does not estimate skills from history** — that is pm-proficiency (a future skill). This skill only reads what the operator explicitly writes in team.md.
- **Does not assign issues** — that is pm-organize. This skill provides the roster data; pm-organize calls `rankAssignees` to score candidates.
- **Does not write identity fields (name, email, status, userId) into team.md** — those fields live only in the synced cache. Edits to team.md are curated, non-PII information only.

Related in General