pm-proficiency
Use this skill when the user says "estimate skills", "what is X good at", "infer proficiency", "populate the roster from history", "estimate the team's skills", "/pm-proficiency", or wants to derive a member's skill areas from their issue and code history. Estimates a member's skill areas from three weighted evidence sources — domain labels on issues assigned to them (strongest), labels on issues they commented on (participation), and code areas they touched in git (weakest) — buckets them coarsely as strong or familiar, and writes them into team.md preview-then-apply. Never writes without confirmation. Distinct from pm-team (which manages manual curated edits to the roster) and pm-organize (which assigns issues using the roster).
What this skill does
# PM Proficiency
Makes the roster self-populating. Instead of hand-typing a member's skills into team.md, infer them from evidence. The result is coarse: **strong** (weighted evidence ≥3) and **familiar** (some evidence but below the threshold). No fine scores, no confidence percentages — just a signal good enough to help pm-organize make reasonable assignment recommendations.
Evidence comes from three sources, each weighted by how strongly it signals real proficiency:
| Source | Signal | Weight | How it's gathered |
|--------|--------|--------|-------------------|
| **Assigned issues** | They own the work | `1.0` | cached issues where `issue.assignee === member.name` |
| **Commented issues** | They participate in the area | `0.5` (`COMMENT_WEIGHT`) | `tracker.fetchCommentedIssueLabels({ userId })` |
| **Code areas** | They touch the module | `0.3` (`CODE_WEIGHT`) | `git log --author=<member.email> --name-only` → `gitAreasFromNameOnly` |
The three sources are folded together in one pass by `proficiency-estimator.combineEvidence`, which canonicalizes label casing (so `Backend`/`backend` never split) and applies the type/exec exclusions uniformly. The weights live as exported constants on the estimator — tune them there, not here.
## Usage
```
/pm-proficiency @handle # estimate one member
/pm-proficiency # sweep the entire roster
```
- `@handle` — estimate a single member; omit to run over every member in the roster.
## When to use / not
**Use when:**
- A member's `strong:` and `familiar:` lines in team.md are empty or noticeably stale.
- Onboarding a new team member who has a history in Linear but whose team.md entry is blank.
- The roster is freshly populated from `pm-team sync` and skills are all empty.
**Do not use when:**
- You want to manually edit skills, owns, or aliases — that is `pm-team edit`.
- You want to assign an issue to the best-fit member — that is `pm-organize`.
- The cache is cold or the member has fewer than a handful of assigned issues — the estimates will be thin; see Limits.
## Workflow
### Step 1 — Resolve the target member(s)
Read team.md (`team-roster.parseTeamMd`), read the synced cache (`cache-store.readTeam(slug)`), and merge them (`team-roster.mergeRoster`) to produce each member's `name` field. The `name` is the display name the tracker stores in `issue.assignee`.
If `@handle` was supplied, filter to that member. If the handle is absent from both halves of the merged roster, stop and tell the operator.
Resolve the team.md path from project config: `project-config.inRepoPaths(repoRoot).dir + '/team.md'` (same path pm-team uses).
### Step 2 — Gather evidence
Gather up to three sources. The first is mandatory; the other two are best-effort — if a source is unavailable, skip it and proceed (note which sources contributed in the preview so the operator understands the basis).
**a. Assigned issues (always).** Read the cached issues (`cache-store.readIssues(slug)`). If the cache is missing or empty AND no other source is available, stop and suggest `/pm --refresh` — never write estimates from zero evidence. Filter to issues whose `assignee` equals the member's `name` (exact string, case-sensitive).
**b. Commented issues (if the tracker supports it).** Construct a tracker — `const tracker = require(CLAUDE_PLUGIN_ROOT + '/hooks/lib/tracker.js').getTracker(provider, { apiKey })` (provider + apiKey come from the merged project config, same as every other pm skill). If `tracker.capabilities.commentEvidence` is truthy (Linear), call `await tracker.fetchCommentedIssueLabels({ userId: member.userId })` — `userId` comes from the synced cache half of the roster. Returns deduped `[{ identifier, labels }]` for issues the member commented on. On GitHub this capability is `false` (the call throws `TRACKER_UNSUPPORTED`); skip it — the git source below covers participation there.
Treat this source as best-effort: wrap the call in try/catch and, on any error (transport, auth, an unexpected schema response), skip comment evidence and proceed with the other sources rather than failing the whole estimate. The GraphQL filter (`comments(filter: { user: { id: { eq } } })`) is verified against Linear's published schema, but a live API hiccup should still degrade gracefully. If the tracker prints a "comment evidence truncated" warning, the member has more than ~5000 comments and participation is slightly understated — note that in the preview.
**c. Code areas (if the member has a known email and you're in a git repo).** Run, read-only:
```bash
git log --author="<member.email>" --name-only --pretty=format: --since="18 months ago" | sort -u
```
Pass the output to `proficiency-estimator.gitAreasFromNameOnly(output, { depth: 2 })` → `{ area: fileCount }`. `depth: 2` keys areas like `plugins/project-manager` rather than just `plugins`; pick a depth that matches how the repo is laid out. Skip this source if the member has no email or `git log` returns nothing.
### Step 3 — Estimate
Determine the exclude set: the defaults (`bug`, `feature`, `task`, `spike`, `hitl`, `afk`, `duplicate`, `triage`) plus any custom type tags the project defines in its templates manifest (`_manifest.json`), so they don't bleed into the area counts.
Fold all gathered sources together in one pass:
```js
const evidence = pe.combineEvidence(
{ assigned: assignedIssues, commented: commentedIssues, codeAreas },
{ exclude },
);
const buckets = pe.bucketProficiency(evidence); // default strongThreshold 3
```
`combineEvidence` weights assigned at 1.0, commented at `COMMENT_WEIGHT` (0.5), and each code-area file-touch at `CODE_WEIGHT` (0.3), then merges by canonical (case-insensitive) area name. Because the weights are fractional, the strong threshold of 3 now means roughly "three assigned issues, or six commented ones, or ten files in an area, or any weighted combination."
It also dedupes by issue identifier across the assigned and commented sets: a member is usually a commenter on the issues they own, so an issue that appears in both is counted once at the assignee weight — not 1.0 + 0.5. (Pass the cached `identifier`/`id` through on each issue so the dedupe can see it; the cache issues and `fetchCommentedIssueLabels` both carry it.)
### Step 4 — Preview
Show the proposed `strong:` / `familiar:` values for each member alongside the diff against their current team.md block. This step is **mandatory** — never skip straight to writing. The preview discipline mirrors `pm-improve --apply`: the operator sees exactly what will change before anything touches the file.
If there is no change (buckets match what's already in team.md), say so and stop — do not ask for confirmation on a no-op.
### Step 5 — Apply on confirmation
Ask the operator to confirm. On "yes" / "go ahead" / "do it":
Call `proficiency-estimator.applyToTeamMd(currentTeamMd, handle, buckets)` for each member, chaining the transforms if sweeping the whole roster (output of one is input of the next). Write the result to `<repo>/.claude/pm/team.md`.
**Never write without confirmation.** team.md is checked in and non-PII — strong/familiar bullets are safe to commit. Never write identity fields (name, email, status, userId) into team.md regardless of operator input; those fields belong only in the synced cache.
## The estimation node one-liner
Runnable example — inspect one member's buckets against the live cache without touching any files:
```bash
node -e '
const fs = require("fs");
const cs = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/cache-store.js");
const tr = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/team-roster.js");
const pe = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/proficiency-estimator.js");
const slug = process.argv[1], handle = process.argv[2], teamMdPath = process.argv[3];
const curated = tr.parseTeamMd(fs.existsSync(teamMdPath) ? fs.readFileSync(teamMdPath,"utf8") : "");
const { roster } = tr.mergeRoster(curated, (cRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.