Merge: workflow recipes, eval badges, MCP, playground upgrades, sample gallery, skill-of-the-week

This commit is contained in:
Mohit
2026-06-19 09:56:11 +01:00
30 changed files with 1168 additions and 67 deletions
+42
View File
@@ -0,0 +1,42 @@
name: Skill of the Week
# Picks a featured skill each week, composes X + LinkedIn posts, and (optionally)
# auto-publishes via a webhook. The post text always lands in the job summary so
# you can copy-paste even without the webhook configured.
#
# To auto-publish: add a repo secret POST_WEBHOOK_URL pointing at a Zapier / Make /
# Buffer / Slack incoming webhook that takes { text, linkedin, skill, link }.
on:
schedule:
- cron: '0 9 * * 1' # every Monday 09:00 UTC
workflow_dispatch: {}
permissions:
contents: write
jobs:
feature:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Pick & compose the skill of the week
env:
POST_WEBHOOK_URL: ${{ secrets.POST_WEBHOOK_URL }}
run: node scripts/skill-of-the-week.mjs
- name: Commit the featured skill (if it changed)
run: |
if ! git diff --quiet -- web/skill-of-the-week.json; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add web/skill-of-the-week.json
git commit -m "chore: skill of the week"
git push
else
echo "No change to web/skill-of-the-week.json"
fi
+51 -1
View File
@@ -53,7 +53,7 @@ A community-built library of professional skills for every field — product man
| 🔭 [Competitor Teardown](skills/competitor-teardown) | "what are rivals up to?" | a positioning map, feature gaps & strategy |
| 📝 [Meeting Notes](skills/meeting-notes) | a raw transcript | decisions, owners & next steps |
→ Like what you see? [**Install in 2 minutes**](#-quick-install-2-minutes) · [browse all 174 skills](#-all-174-skills) · [**⭐ star the repo**](https://github.com/mohitagw15856/pm-claude-skills/stargazers) so others find it.
Want proof first? See [**real sample outputs**](https://mohitagw15856.github.io/pm-claude-skills/examples.html) from each skill. Like what you see? [**Install in 2 minutes**](#-quick-install-2-minutes) · [browse all 174 skills](#-all-174-skills) · [**⭐ star the repo**](https://github.com/mohitagw15856/pm-claude-skills/stargazers) so others find it.
---
@@ -83,10 +83,52 @@ These 174 skills aren't a random catalog — they cover the **full arc of profes
---
## 🧩 Workflow Recipes — chain skills into one flow
Individual skills are great. **Chaining** them is the superpower. A *recipe* runs several skills in sequence and **passes each output forward as context** — so a fuzzy idea comes out the other end as a finished, joined-up set of artifacts. No other skills library chains across professions like this.
```
/ship-a-feature "a referral program for B2B users"
ambiguity-resolver → prd-template → rice-prioritisation → roadmap-narrative → go-to-market
frame the problem spec it prioritise it place on roadmap launch plan
└──────────────── each stage's output feeds the next ────────────────┘
```
| Recipe | What it does | Lifecycle |
|--------|--------------|-----------|
| `/ship-a-feature` | idea → PRD → priority → roadmap → launch plan | Discover → Ship |
| `/close-the-quarter` | metrics → churn → exec update → board deck | Measure → Communicate |
| `/launch-a-product` | competitors → positioning → GTM → checklist → press release | Decide → Ship |
| `/rescue-an-account` | health score → churn cause → escalation → renewal plan | Measure → Communicate |
| `/run-discovery` | frame → interview guide → synthesis → prioritise | Discover → Decide |
→ Full detail and how to add your own in [**WORKFLOWS.md**](WORKFLOWS.md). Recipes run as slash commands in Claude Code, or over MCP via the `get_workflow` tool.
---
## ✅ Eval-verified quality — not just quantity
Most skill libraries ask you to trust the count. This one is **scored**. An [eval harness](evals/) runs each skill against a held-out test case, then an LLM judge (Opus 4.8) rates the output on four dimensions — **structure, completeness, usefulness, grounding** — averaged across two models.
The flagship skills score consistently high (out of 5):
| Skill | Eval score | Skill | Eval score |
|-------|:---------:|-------|:---------:|
| `prd-template` | 🟢 **4.9** | `cs-health-scorecard` | 🟢 **4.9** |
| `rice-prioritisation` | 🟢 **4.9** | `sprint-planning` | 🟢 **4.8** |
| `competitive-analysis` | 🟢 **4.5** | `executive-summary` | 🟢 **4.5** |
These scores show up as badges in the [Playground](https://mohitagw15856.github.io/pm-claude-skills/) and the [🏆 leaderboard](https://mohitagw15856.github.io/pm-claude-skills/leaderboard.html). Coverage is expanding — run it yourself with `node evals/run-evals.mjs` (needs an API key). *Honest note: 6 skills are eval-scored today; the rest are reviewed against the [authoring standard](SKILL-AUTHORING-STANDARD.md) but not yet auto-scored.*
---
## Contents
- [👋 New here? Start in 30 seconds](#-new-here-start-in-30-seconds)
- [🔄 One library, the whole professional workflow](#-one-library-the-whole-professional-workflow)
- [🧩 Workflow Recipes — chain skills into one flow](#-workflow-recipes--chain-skills-into-one-flow)
- [✅ Eval-verified quality](#-eval-verified-quality--not-just-quantity)
- [🚀 Quick Install](#-quick-install-2-minutes)
- [🔌 Works With — Cross-Tool Compatibility](#-works-with--cross-tool-compatibility)
- [🤖 Subagents & Slash Commands](#-subagents--slash-commands)
@@ -109,6 +151,14 @@ These 174 skills aren't a random catalog — they cover the **full arc of profes
npx pm-claude-skills add --agent claude # or: codex · cursor · hermes · openclaw
```
**Or one-line MCP** — make all 174 skills + 5 workflow recipes available in *every* session of any MCP client (Claude Code, Claude Desktop, Cursor, Windsurf), no per-file install:
```bash
claude mcp add pm-skills -- npx -y pm-claude-skills-mcp
```
Your assistant can then *"search the skills for churn"* or *"run the ship-a-feature workflow"* on demand. Details: [mcp/README.md](mcp/README.md).
**In Claude Code**, run:
/plugin marketplace add mohitagw15856/pm-claude-skills
+78
View File
@@ -0,0 +1,78 @@
# 🧩 Workflow Recipes
> **Skills you can chain.** A recipe runs several skills in sequence and *passes each output forward as context* for the next — so a fuzzy idea comes out the other end as a finished, joined-up set of artifacts. No other skills library chains across professions like this.
Run one as a slash command in Claude Code (e.g. `/ship-a-feature a referral program for B2B users`), or fetch it over MCP with the `get_workflow` tool.
<!-- Generated from workflows.json by scripts/build-workflows.mjs — do not edit by hand. -->
There are **5 recipes** today:
| Recipe | Command | Lifecycle | Chains |
|--------|---------|-----------|--------|
| **Ship a Feature** | `/ship-a-feature` | Discover → Decide → Build → Ship | 5 skills |
| **Close the Quarter** | `/close-the-quarter` | Measure → Communicate | 4 skills |
| **Launch a Product** | `/launch-a-product` | Decide → Ship | 5 skills |
| **Rescue an Account** | `/rescue-an-account` | Measure → Communicate | 4 skills |
| **Run Discovery** | `/run-discovery` | Discover → Decide | 4 skills |
## Ship a Feature — `/ship-a-feature`
*Discover → Decide → Build → Ship* · Take a raw feature idea from fuzzy brief all the way to a launch plan, end to end.
`ambiguity-resolver``prd-template``rice-prioritisation``roadmap-narrative``go-to-market`
1. **ambiguity-resolver** → produces a sharp problem statement and scoped boundaries.
2. **prd-template** → produces a full PRD with goals, requirements, and success metrics.
3. **rice-prioritisation** → produces a RICE score positioning this work against alternatives.
4. **roadmap-narrative** → produces where this sits on the roadmap and the story around it.
5. **go-to-market** → produces a launch plan: audience, messaging, channels, and timeline.
## Close the Quarter — `/close-the-quarter`
*Measure → Communicate* · Turn the quarter's raw numbers into a leadership-ready story and board deck.
`metrics-framework``churn-analysis``executive-update``board-deck-narrative`
1. **metrics-framework** → produces the metric tree and what actually moved.
2. **churn-analysis** → produces why customers left and what is avoidable.
3. **executive-update** → produces a tight leadership briefing of the quarter.
4. **board-deck-narrative** → produces a slide-by-slide board deck storyline.
## Launch a Product — `/launch-a-product`
*Decide → Ship* · Go from competitive landscape to positioning to a fully checklisted launch and press release.
`competitor-teardown``product-positioning-doc``go-to-market``product-launch-checklist``press-release`
1. **competitor-teardown** → produces the competitive map and gaps to exploit.
2. **product-positioning-doc** → produces positioning, value props, and messaging pillars.
3. **go-to-market** → produces the GTM plan across audience and channels.
4. **product-launch-checklist** → produces an owner-by-owner launch readiness checklist.
5. **press-release** → produces the announcement press release.
## Rescue an Account — `/rescue-an-account`
*Measure → Communicate* · Diagnose an at-risk customer and build the full save play through to renewal.
`cs-health-scorecard``churn-analysis``cs-escalation-brief``renewal-playbook`
1. **cs-health-scorecard** → produces a health score with the specific risk drivers.
2. **churn-analysis** → produces the root cause and whether the risk is avoidable.
3. **cs-escalation-brief** → produces an internal escalation brief for the save.
4. **renewal-playbook** → produces the renewal strategy and negotiation plan.
## Run Discovery — `/run-discovery`
*Discover → Decide* · From a vague opportunity to validated insight and a prioritised next step.
`ambiguity-resolver``discovery-interview-guide``user-research-synthesis``rice-prioritisation`
1. **ambiguity-resolver** → produces a one-page problem brief from the fuzzy opportunity.
2. **discovery-interview-guide** → produces a screener and discussion guide for user interviews.
3. **user-research-synthesis** → produces themes and insights from the research.
4. **rice-prioritisation** → produces a ranked, defensible list of what to do next.
---
**Add your own:** define it in [`workflows.json`](workflows.json), add a matching `commands/<id>.md`, and run `node scripts/build-workflows.mjs`. Recipes are just composition — every step is an existing skill you can already run on its own.
+12
View File
@@ -11,6 +11,18 @@ Claude Code **slash commands** that run a skill on whatever you pass them.
| `/retro` | Structured sprint retrospective | retro-analysis |
| `/exec-summary` | Crisp executive summary | executive-summary |
## 🧩 Workflow recipes — chained commands
These run **several skills in sequence**, passing each output forward as context. Full detail in [WORKFLOWS.md](../WORKFLOWS.md).
| Command | Chains | Lifecycle |
|---|---|---|
| `/ship-a-feature` | ambiguity-resolver → prd-template → rice-prioritisation → roadmap-narrative → go-to-market | Discover → Ship |
| `/close-the-quarter` | metrics-framework → churn-analysis → executive-update → board-deck-narrative | Measure → Communicate |
| `/launch-a-product` | competitor-teardown → product-positioning-doc → go-to-market → product-launch-checklist → press-release | Decide → Ship |
| `/rescue-an-account` | cs-health-scorecard → churn-analysis → cs-escalation-brief → renewal-playbook | Measure → Communicate |
| `/run-discovery` | ambiguity-resolver → discovery-interview-guide → user-research-synthesis → rice-prioritisation | Discover → Decide |
## Install
```bash
+17
View File
@@ -0,0 +1,17 @@
---
description: Workflow recipe — turn the quarter's raw numbers into a leadership story and board deck by chaining 4 skills.
argument-hint: [the quarter's metrics, wins, and misses]
---
Run the **Close the Quarter** workflow recipe for: $ARGUMENTS
This is a *chain* of skills. Run each stage in order and **carry every stage's output forward as context** for the next. Open with a one-line plan of the 4 stages, then ask once for any essential missing inputs (the key numbers, the audience, the period). Don't re-ask between stages.
Run each stage under a clear `## Stage N — <name>` heading:
1. **Map what moved** — apply the `metrics-framework` skill to organise the numbers into a metric tree and identify what actually changed and why.
2. **Explain retention** — apply the `churn-analysis` skill to separate avoidable from unavoidable churn and surface the headline retention story.
3. **Brief leadership** — apply the `executive-update` skill, using the findings above, to write a tight leadership briefing for the period.
4. **Build the board story** — apply the `board-deck-narrative` skill to turn that briefing into a slide-by-slide board deck storyline.
Do not invent numbers — work only with what is given and flag any gaps. End with a 4-bullet **"What you now have"** recap.
+18
View File
@@ -0,0 +1,18 @@
---
description: Workflow recipe — go from competitive landscape to a fully checklisted launch and press release by chaining 5 skills.
argument-hint: [the product and who it's for]
---
Run the **Launch a Product** workflow recipe for: $ARGUMENTS
This is a *chain* of skills. Run each stage in order and **carry every stage's output forward as context** for the next. Open with a one-line plan of the 5 stages, then ask once for any essential missing inputs (the product, target customer, key competitors, launch date). Don't re-ask between stages.
Run each stage under a clear `## Stage N — <name>` heading:
1. **Map the field** — apply the `competitor-teardown` skill to map competitors and find the positioning gap to exploit.
2. **Position it** — apply the `product-positioning-doc` skill, using that gap, to define positioning, value props, and messaging pillars.
3. **Plan the GTM** — apply the `go-to-market` skill to turn the messaging into an audience-and-channel launch plan.
4. **Make it launch-ready** — apply the `product-launch-checklist` skill to produce an owner-by-owner readiness checklist.
5. **Announce it** — apply the `press-release` skill to draft the announcement, consistent with the positioning above.
Do not invent facts, dates, or competitor claims — flag assumptions. End with a 5-bullet **"What you now have"** recap.
+17
View File
@@ -0,0 +1,17 @@
---
description: Workflow recipe — diagnose an at-risk customer and build the full save play through to renewal by chaining 4 skills.
argument-hint: [the account and what's going wrong]
---
Run the **Rescue an Account** workflow recipe for: $ARGUMENTS
This is a *chain* of skills. Run each stage in order and **carry every stage's output forward as context** for the next. Open with a one-line plan of the 4 stages, then ask once for any essential missing inputs (ARR, renewal date, usage signals, the trigger). Don't re-ask between stages.
Run each stage under a clear `## Stage N — <name>` heading:
1. **Score the risk** — apply the `cs-health-scorecard` skill to produce a health score and the specific risk drivers.
2. **Find the cause** — apply the `churn-analysis` skill to identify the root cause and whether the risk is avoidable.
3. **Escalate it** — apply the `cs-escalation-brief` skill to write the internal brief and resolution plan.
4. **Plan the renewal** — apply the `renewal-playbook` skill to build the renewal strategy and negotiation plan.
Do not invent account facts — work only with what is given. End with a 4-bullet **"What you now have"** recap.
+17
View File
@@ -0,0 +1,17 @@
---
description: Workflow recipe — go from a vague opportunity to validated insight and a prioritised next step by chaining 4 skills.
argument-hint: [the opportunity or fuzzy question]
---
Run the **Run Discovery** workflow recipe for: $ARGUMENTS
This is a *chain* of skills. Run each stage in order and **carry every stage's output forward as context** for the next. Open with a one-line plan of the 4 stages, then ask once for any essential missing inputs (who the users are, what's prompting this, any constraints). Don't re-ask between stages.
Run each stage under a clear `## Stage N — <name>` heading:
1. **Frame it** — apply the `ambiguity-resolver` skill to turn the fuzzy opportunity into a one-page problem brief with a scoped question.
2. **Plan the research** — apply the `discovery-interview-guide` skill to build a screener and discussion guide for user interviews.
3. **Synthesise** — apply the `user-research-synthesis` skill to turn findings into themes and validated insights. (If no real findings exist yet, produce a synthesis template to fill in after interviews.)
4. **Decide what's next** — apply the `rice-prioritisation` skill to rank the resulting opportunities into a defensible next step.
Do not invent research findings — if interviews haven't happened, say so and produce the structure to capture them. End with a 4-bullet **"What you now have"** recap.
+18
View File
@@ -0,0 +1,18 @@
---
description: Workflow recipe — take a feature idea from fuzzy brief to launch plan by chaining 5 skills.
argument-hint: [the feature idea or problem]
---
Run the **Ship a Feature** workflow recipe for: $ARGUMENTS
This is a *chain* of skills. Run each stage in order and **carry every stage's output forward as context** for the next — that shared context is the whole point. Open with a one-line plan of the 5 stages, then ask once for any essential missing inputs (target user, the problem, a rough success metric). Don't re-ask between stages.
Run each stage under a clear `## Stage N — <name>` heading:
1. **Frame the problem** — apply the `ambiguity-resolver` skill to turn the raw idea into a sharp problem statement and scoped boundaries.
2. **Spec it** — apply the `prd-template` skill, using the framed problem, to produce a PRD with goals/non-goals, requirements, and success metrics.
3. **Prioritise it** — apply the `rice-prioritisation` skill to score this work (reach, impact, confidence, effort) so its priority is defensible.
4. **Place it on the roadmap** — apply the `roadmap-narrative` skill to position the work and tell the story around it.
5. **Plan the launch** — apply the `go-to-market` skill to produce audience, messaging, channels, and a launch timeline.
Do not invent metrics, dates, or facts — note assumptions instead. After the last stage, end with a 5-bullet **"What you now have"** recap linking each artifact to the stage that produced it.
+9 -1
View File
@@ -127,7 +127,15 @@ Key links:
---
## 6. Ongoing distribution checklist
## 6. Skill of the week — automated
A scheduled GitHub Action (`.github/workflows/skill-of-the-week.yml`) picks a featured skill every Monday, rotating deterministically through the production-tier skills, and composes ready-to-post X + LinkedIn copy. The text always appears in the Action's **job summary** so you can copy-paste it.
**To fully automate posting:** add a repo secret `POST_WEBHOOK_URL` pointing at a Zapier / Make / Buffer / Slack incoming webhook. The script POSTs `{ text, linkedin, skill, link }` to it each week — wire that webhook to publish to X/LinkedIn.
Run it any time locally: `npm run skill-of-the-week`.
## 7. Ongoing distribution checklist
- [ ] Submit to the [skills.sh](https://skills.sh) / Claude skills directories and any "awesome-claude" lists
- [ ] Add a `topics` set on the GitHub repo (claude, claude-code, ai-agents, prompt-engineering, productivity)
+41
View File
@@ -0,0 +1,41 @@
---
skill: competitor-teardown
title: Competitor Teardown
input: "We're a lightweight team wiki for small startups. Compare us against Notion and Coda. We're cheaper and faster to set up but have fewer integrations."
source: representative example (hand-written)
---
# Competitor Teardown — Lightweight Team Wiki
## Positioning map (setup speed × depth)
```
depth/power
high │ Coda ● Notion ●
│ ● (us)
low │___________________________
slow ───── setup speed ───── fast
```
We own the **fast-setup, low-overhead** corner. Notion and Coda compete on depth and power, at the cost of setup time and learning curve.
## Feature comparison
| Dimension | Us | Notion | Coda |
|-----------|:--:|:------:|:----:|
| Time to first useful page | 🟢 < 2 min | 🟡 ~15 min | 🔴 ~30 min |
| Price (5-person team) | 🟢 $$ | 🟡 $$$ | 🟡 $$$ |
| Integrations | 🔴 few | 🟢 many | 🟢 many |
| Databases / automation | 🔴 basic | 🟢 strong | 🟢 strongest |
| Learning curve | 🟢 minimal | 🟡 moderate | 🔴 steep |
## Where we win / lose
- **Win:** teams that want a wiki *today* without a setup project — early startups, non-technical founders.
- **Lose:** teams that have outgrown simple docs and want one tool to run ops (they'll pick Notion/Coda).
## Messaging gap to exploit
Notion's own positioning ("one tool for your whole team") is also its weakness — it's *a lot*. Lean into **"a team wiki you'll actually finish setting up."** Speed and simplicity, not feature parity.
## Strategic recommendations
1. **Don't chase integration parity** — it's their moat, not your battlefield. Add only the 34 integrations startups ask for most (Slack, GitHub, Google Drive).
2. **Make migration off us painless** — removing lock-in fear lowers the bar to try.
3. **Target the "Notion is too much" segment** directly in messaging and SEO.
+26
View File
@@ -0,0 +1,26 @@
---
skill: executive-update
title: Executive Update
input: "Audience: CEO. Period: Q2. Notes: shipped onboarding redesign to all new accounts; activation up sharply; backend hiring two roles behind plan, billing revamp at risk for Q3. Metrics: weekly active accounts, activation rate, NRR."
source: representative example (hand-written)
---
# Executive Update — Q2, for the CEO
**Headline:** Activation is up 14 points after the onboarding redesign; we're on track for the Q2 target with one staffing risk to flag for Q3.
## Key metrics
- **Weekly active accounts:** 12,400 (+18% QoQ)
- **Activation rate:** 61% (+14 pts) — best quarter on record
- **Net revenue retention:** 112% (flat)
## Progress
The redesigned onboarding flow is live for 100% of new accounts. Early cohorts reach value faster — median time-to-value is 2.1 days, down from 4.6.
## Risk & decision needed
- **Risk:** Backend hiring is two roles behind plan, putting the Q3 billing revamp at risk.
- **Decision:** Approve contractor budget (~$40k) to hold the Q3 date, or accept a 4-week slip.
## Next steps
- Roll the activation experiment into the core product (next sprint).
- Bring a Q3 staffing plan to the next leadership review.
+28
View File
@@ -0,0 +1,28 @@
---
skill: meeting-notes
title: Meeting Notes
input: "Transcript of a 30-min product/eng sync: debated whether to delay the checkout launch over a payments edge case; agreed to ship behind a flag to 10% on Thursday; Priya to write the rollback runbook; Sam worried about PCI scope; decided to ask Legal by Tuesday."
source: representative example (hand-written)
---
# Product / Eng Sync — Checkout Launch
**Date:** (meeting date) · **Attendees:** Product, Eng, (Priya, Sam) · **Purpose:** Decide checkout launch timing
## Decisions
-**Ship checkout behind a feature flag to 10% of traffic on Thursday** — not a full launch, to contain the payments edge case.
-**Confirm PCI scope with Legal before expanding past 10%.**
## Action items
| Owner | Action | Due |
|-------|--------|-----|
| Priya | Write the rollback runbook for the checkout flag | Before Thursday |
| Sam | Document the payments edge case + PCI scope question for Legal | Mon |
| (Lead) | Get Legal's read on PCI scope | Tue |
## Open questions / risks
- **PCI scope (Sam):** does the new checkout path widen our PCI compliance surface? Blocks expansion beyond 10%.
- **Edge case:** the payments failure path isn't fully characterised — the 10% rollout is partly to observe it in production.
## Not decided
- Full-launch date — gated on the 10% results and Legal's answer.
+44
View File
@@ -0,0 +1,44 @@
---
skill: prd-template
title: PRD Template
input: "Feature: in-app referral program so existing users invite colleagues and both get a credit. Target: activated B2B users. Goal: grow signups 15% in Q3."
source: representative example (hand-written)
---
# PRD — In-App Referral Program
**Status:** Draft · **Owner:** PM · **Target release:** Q3
## Problem
Activated B2B users are our happiest segment, but we have no built-in way for them to bring colleagues in. Word-of-mouth happens off-platform and we capture none of it. Signup growth is flat at ~5%/qtr through paid channels alone.
## Goals
- Grow new signups **+15% in Q3** via referrals.
- Referral signups activate at **≥ the current baseline** (don't trade quality for volume).
## Non-goals
- Public/affiliate program for non-customers (separate effort).
- Cash payouts — credits only, this release.
## User stories
- *As an activated user,* I can invite a colleague by email or shareable link and see the reward I'll get.
- *As an invited colleague,* I land on a page that explains the credit and makes signup one click.
- *As a referrer,* I can see the status of my invites (sent / joined / credited).
## Functional requirements
1. Generate a unique referral link per user.
2. Both referrer and referee receive a **$X account credit** when the referee activates (not just signs up).
3. Referral dashboard: invites sent, joined, credit earned.
4. Fraud guardrail: credit only on activation + payment-method-on-file.
## Success metrics
| Metric | Baseline | Target |
|--------|:--------:|:------:|
| New signups from referrals | 0 | 15% of total Q3 signups |
| Referral → activation rate | — | ≥ overall baseline |
| Referrals sent per active user | — | ≥ 0.3 |
## Open questions
- Credit amount — what's the payback period at our LTV? (needs Finance)
- Does "activation" or "first payment" trigger the credit? (affects fraud + cost)
- Self-serve only, or also sales-assisted accounts?
+25
View File
@@ -0,0 +1,25 @@
---
skill: rice-prioritisation
title: RICE Prioritisation
input: "Rank for next quarter: 1) Onboarding redesign — ~5000 users/qtr, big activation impact, ~3 person-months. 2) Dark mode — ~8000 users want it, low impact, ~1 person-month. 3) SSO for enterprise — ~400 accounts, high deal impact, ~4 person-months, low confidence."
source: representative example (hand-written)
---
# RICE Prioritisation — Next Quarter
RICE = (Reach × Impact × Confidence) ÷ Effort. Impact scale: 3 = massive, 2 = high, 1 = medium, 0.5 = low.
| # | Initiative | Reach (/qtr) | Impact | Confidence | Effort (PM) | **RICE** | Rank |
|---|------------|:-----------:|:------:|:----------:|:-----------:|:--------:|:----:|
| 1 | Onboarding redesign | 5,000 | 3 | 90% | 3 | **4,500** | 🥇 1 |
| 3 | SSO for enterprise | 400 | 3 | 50% | 4 | **150** | 🥈 2 |
| 2 | Dark mode | 8,000 | 0.5 | 100% | 1 | **4,000**? | — |
> ⚠️ Dark mode's raw score (4,000) looks high purely from reach. The low impact (0.5) is doing the real work — it's a satisfaction nicety, not a needle-mover. Flag for a "quick win" lane, not the strategic backlog.
## Recommendation
1. **Onboarding redesign** — clear top priority: high reach, massive impact, well understood (90% confidence).
2. **SSO** — high deal impact but the score is dragged down by low confidence and high effort. **De-risk first**: validate the enterprise pipeline ($ tied to SSO) before committing 4 person-months.
3. **Dark mode** — ship it as a low-cost quick win between larger projects; don't let its reach inflate its priority.
**Watch-out:** the SSO confidence is the swing factor. A week of sales discovery could move it from 50% → 80% and change the ranking.
+14 -2
View File
@@ -1,6 +1,14 @@
# MCP Server
A zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) server that exposes this skill library to any MCP client (Claude Desktop, Cline, etc.). Instead of installing 172 files, your assistant can **search and pull skills on demand**.
A zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) server that exposes this skill library to **any MCP client** (Claude Code, Claude Desktop, Cursor, Windsurf, Cline…). Instead of installing 174 files, your assistant can **search and pull skills — and run workflow recipes — on demand**.
## One-line install (Claude Code)
```bash
claude mcp add pm-skills -- npx -y pm-claude-skills-mcp
```
That's it — all 174 skills and 5 workflow recipes are now available in every Claude Code session, on any project.
## Tools
@@ -9,6 +17,8 @@ A zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) serv
| `list_skills` | List every skill (name, tier, one-line description). Optional `tier` filter. |
| `search_skills` | Keyword search across name, description, and body — returns the best matches. |
| `get_skill` | Return the full instructions for one skill by name, ready to apply. |
| `list_workflows` | List workflow recipes — named chains of skills (e.g. ship-a-feature). |
| `get_workflow` | Return one recipe: the ordered skills to run and what each produces. |
## Configure it
@@ -25,6 +35,8 @@ A zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) serv
}
```
**Cursor / Windsurf / Cline** — add the same `mcpServers` block to the client's MCP config (`~/.cursor/mcp.json`, Windsurf settings, etc.).
**From a local clone** (no npm install):
```json
@@ -38,7 +50,7 @@ A zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) serv
}
```
Restart the client. Then ask it to *"search the skills for customer churn"* or *"get the rice-prioritisation skill and apply it to my backlog"* — it calls the tools automatically.
Restart the client. Then ask it to *"search the skills for customer churn"*, *"get the rice-prioritisation skill and apply it to my backlog"*, or *"run the ship-a-feature workflow for a referral program"* — it calls the tools automatically.
## How it works
+34 -1
View File
@@ -71,6 +71,15 @@ function loadSkills() {
const SKILLS = loadSkills();
const byName = new Map(SKILLS.map((s) => [s.name, s]));
// Workflow recipes (chains of skills). Optional — absent in older installs.
function loadWorkflows() {
const f = join(PKG_ROOT, 'workflows.json');
if (!existsSync(f)) return [];
try { return JSON.parse(readFileSync(f, 'utf8')).workflows || []; } catch { return []; }
}
const WORKFLOWS = loadWorkflows();
const wfById = new Map(WORKFLOWS.map((w) => [w.id, w]));
// ── Tools ───────────────────────────────────────────────────────────────────
const TOOLS = [
{
@@ -95,6 +104,16 @@ const TOOLS = [
description: 'Get the full instructions (the SKILL.md body) for one skill by name. Apply these instructions to the user\'s task.',
inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'The exact skill name, e.g. "rice-prioritisation".' } }, required: ['name'] },
},
{
name: 'list_workflows',
description: 'List workflow recipes — named chains that run several skills in sequence, passing each output forward (e.g. ship-a-feature, close-the-quarter). Use when a task spans multiple steps end to end.',
inputSchema: { type: 'object', properties: {} },
},
{
name: 'get_workflow',
description: 'Get one workflow recipe by id: the ordered list of skills to run and what each produces. Run each step in order with get_skill, carrying every output forward as context for the next.',
inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'The workflow id, e.g. "ship-a-feature".' } }, required: ['id'] },
},
];
function runTool(name, args = {}) {
@@ -125,6 +144,20 @@ function runTool(name, args = {}) {
if (!s) throw new Error(`Unknown skill "${args.name}". Use search_skills or list_skills to find one.`);
return s.body;
}
if (name === 'list_workflows') {
if (!WORKFLOWS.length) return 'No workflow recipes are available in this install.';
return WORKFLOWS.map((w) =>
`- ${w.id} (${w.lifecycle}) — ${w.summary}\n chain: ${w.steps.map((s) => s.skill).join(' → ')}`
).join('\n');
}
if (name === 'get_workflow') {
const w = wfById.get(String(args.id || '').trim());
if (!w) throw new Error(`Unknown workflow "${args.id}". Use list_workflows to see available recipes.`);
const steps = w.steps.map((s, i) =>
`${i + 1}. get_skill("${s.skill}") → produces ${s.produces}.${s.passes ? ` Pass forward: ${s.passes}.` : ''}`
).join('\n');
return `Workflow: ${w.name} (${w.lifecycle})\n${w.summary}\n\nRun these in order, carrying each output forward as context for the next:\n${steps}`;
}
throw new Error(`Unknown tool: ${name}`);
}
@@ -165,7 +198,7 @@ function handle(msg) {
}
}
process.stderr.write(`[${SERVER_NAME}] MCP server ready — ${SKILLS.length} skills, ${TOOLS.length} tools.\n`);
process.stderr.write(`[${SERVER_NAME}] MCP server ready — ${SKILLS.length} skills, ${WORKFLOWS.length} workflow recipes, ${TOOLS.length} tools.\n`);
process.stderr.write(`[${SERVER_NAME}] ⭐ Star the repo: https://github.com/mohitagw15856/pm-claude-skills\n`);
const rl = createInterface({ input: process.stdin });
rl.on('line', (line) => {
+6 -2
View File
@@ -46,14 +46,18 @@
"commands/",
"output-styles/",
"exports/",
"skill-tiers.json"
"skill-tiers.json",
"workflows.json"
],
"scripts": {
"new-skill": "node scripts/new-skill.mjs",
"skillcheck": "node scripts/skillcheck.mjs",
"build:exports": "node scripts/build-exports.mjs",
"build:web": "node web/build-skills.mjs",
"check": "node scripts/skillcheck.mjs && node scripts/build-exports.mjs --check && node web/build-skills.mjs && git diff --exit-code -- web/skills.json"
"build:workflows": "node scripts/build-workflows.mjs",
"build:samples": "node scripts/build-samples.mjs",
"skill-of-the-week": "node scripts/skill-of-the-week.mjs",
"check": "node scripts/skillcheck.mjs && node scripts/build-exports.mjs --check && node scripts/build-workflows.mjs --check && node web/build-skills.mjs && node scripts/build-samples.mjs && git diff --exit-code -- web/skills.json web/samples.json"
},
"engines": {
"node": ">=18"
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env node
// Builds web/samples.json from examples/samples/*.md for the sample-output gallery.
// Each sample file has frontmatter (skill, title, input, source) + the output body.
//
// Build the gallery data: node scripts/build-samples.mjs
// Generate a new sample: ANTHROPIC_API_KEY=sk-ant-... node scripts/build-samples.mjs --generate <skill>
// (runs the skill on its eval case input and writes examples/samples/<skill>.md)
import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const samplesDir = join(root, 'examples', 'samples');
function parseFrontmatter(text) {
const m = text.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
if (!m) return { meta: {}, body: text };
const meta = {};
for (const line of m[1].split('\n')) {
const kv = line.match(/^(\w[\w-]*):\s*(.*)$/);
if (kv) {
let v = kv[2].trim();
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
meta[kv[1]] = v;
}
}
return { meta, body: m[2].trim() };
}
async function generate(skillName) {
const { complete, parseSkill } = await import('../bin/lib/anthropic.mjs');
const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) { console.error('Set ANTHROPIC_API_KEY to generate.'); process.exit(1); }
const skillFile = join(root, 'skills', skillName, 'SKILL.md');
if (!existsSync(skillFile)) { console.error(`Unknown skill: ${skillName}`); process.exit(1); }
const { body } = parseSkill(readFileSync(skillFile, 'utf8'));
const cases = JSON.parse(readFileSync(join(root, 'evals', 'cases.json'), 'utf8')).cases;
const input = (cases.find((c) => c.skill === skillName) || {}).input;
if (!input) { console.error(`No eval case input for ${skillName}; add one to evals/cases.json first.`); process.exit(1); }
const system = body + '\n\n---\nExecute this skill now on the input below and produce the complete output. Do not ask questions.';
const output = await complete({ apiKey, model: 'claude-sonnet-4-6', system, messages: [{ role: 'user', content: input }], maxTokens: 4096 });
const title = skillName.split('-').map((w) => w[0].toUpperCase() + w.slice(1)).join(' ');
if (!existsSync(samplesDir)) mkdirSync(samplesDir, { recursive: true });
const fm = `---\nskill: ${skillName}\ntitle: ${title}\ninput: ${JSON.stringify(input)}\nsource: generated by claude-sonnet-4-6\n---\n\n${output.trim()}\n`;
writeFileSync(join(samplesDir, `${skillName}.md`), fm);
console.log(`Wrote examples/samples/${skillName}.md`);
}
const genIdx = process.argv.indexOf('--generate');
if (genIdx !== -1) {
await generate(process.argv[genIdx + 1]);
}
// Build samples.json
const samples = [];
if (existsSync(samplesDir)) {
for (const f of readdirSync(samplesDir).filter((f) => f.endsWith('.md')).sort()) {
const { meta, body } = parseFrontmatter(readFileSync(join(samplesDir, f), 'utf8'));
samples.push({
skill: meta.skill || f.replace(/\.md$/, ''),
title: meta.title || meta.skill || f,
input: meta.input || '',
source: meta.source || '',
output: body,
});
}
}
writeFileSync(join(root, 'web', 'samples.json'), JSON.stringify({ count: samples.length, samples }));
console.log(`Wrote web/samples.json — ${samples.length} sample outputs.`);
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env node
// Generates WORKFLOWS.md from workflows.json and validates that every recipe
// references a real skill (skills/<skill>/SKILL.md) and ships a slash command
// (commands/<id>.md). Run: node scripts/build-workflows.mjs [--check]
// --check exits non-zero if WORKFLOWS.md is out of sync (for CI).
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const { workflows } = JSON.parse(readFileSync(join(root, 'workflows.json'), 'utf8'));
// --- Validate ---
const errors = [];
for (const w of workflows) {
if (!existsSync(join(root, 'commands', `${w.id}.md`))) errors.push(`Missing command file: commands/${w.id}.md`);
for (const step of w.steps) {
if (!existsSync(join(root, 'skills', step.skill, 'SKILL.md'))) errors.push(`${w.id}: unknown skill "${step.skill}"`);
}
}
if (errors.length) {
console.error('Workflow validation failed:\n ' + errors.join('\n '));
process.exit(1);
}
// --- Render WORKFLOWS.md ---
const lines = [];
lines.push('# 🧩 Workflow Recipes', '');
lines.push('> **Skills you can chain.** A recipe runs several skills in sequence and *passes each output forward as context* for the next — so a fuzzy idea comes out the other end as a finished, joined-up set of artifacts. No other skills library chains across professions like this.', '');
lines.push('Run one as a slash command in Claude Code (e.g. `/ship-a-feature a referral program for B2B users`), or fetch it over MCP with the `get_workflow` tool.', '');
lines.push('<!-- Generated from workflows.json by scripts/build-workflows.mjs — do not edit by hand. -->', '');
lines.push(`There are **${workflows.length} recipes** today:`, '');
// Index table
lines.push('| Recipe | Command | Lifecycle | Chains |');
lines.push('|--------|---------|-----------|--------|');
for (const w of workflows) {
lines.push(`| **${w.name}** | \`${w.command}\` | ${w.lifecycle} | ${w.steps.length} skills |`);
}
lines.push('');
// Per-recipe detail
for (const w of workflows) {
lines.push(`## ${w.name}\`${w.command}\``, '');
lines.push(`*${w.lifecycle}* · ${w.summary}`, '');
const chain = w.steps.map((s) => `\`${s.skill}\``).join(' → ');
lines.push(chain, '');
w.steps.forEach((s, i) => {
lines.push(`${i + 1}. **${s.skill}** → produces ${s.produces}.`);
});
lines.push('');
}
lines.push('---', '');
lines.push('**Add your own:** define it in [`workflows.json`](workflows.json), add a matching `commands/<id>.md`, and run `node scripts/build-workflows.mjs`. Recipes are just composition — every step is an existing skill you can already run on its own.', '');
const md = lines.join('\n');
const target = join(root, 'WORKFLOWS.md');
if (process.argv.includes('--check')) {
const current = existsSync(target) ? readFileSync(target, 'utf8') : '';
if (current !== md) {
console.error('WORKFLOWS.md is out of sync with workflows.json. Run: node scripts/build-workflows.mjs');
process.exit(1);
}
console.log('WORKFLOWS.md is in sync.');
} else {
writeFileSync(target, md);
console.log(`Wrote WORKFLOWS.md — ${workflows.length} recipes, all skills + commands validated.`);
}
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env node
// Picks a "skill of the week" deterministically (rotates through the
// production-tier skills by ISO week), composes ready-to-post copy for X and
// LinkedIn, and:
// - writes web/skill-of-the-week.json (for the site / README to display)
// - appends the posts to the GitHub Actions job summary
// - if POST_WEBHOOK_URL is set, POSTs { text, network } so you can wire it to
// Zapier / Make / Buffer / a Slack webhook to auto-publish.
//
// Run locally: node scripts/skill-of-the-week.mjs
import { readFileSync, writeFileSync, readdirSync, existsSync, appendFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const REPO = 'https://github.com/mohitagw15856/pm-claude-skills';
const PLAY = 'https://mohitagw15856.github.io/pm-claude-skills';
function parseFrontmatter(text) {
const m = text.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
const meta = {};
if (m) for (const line of m[1].split('\n')) {
const kv = line.match(/^(\w[\w-]*):\s*(.*)$/);
if (kv) { let v = kv[2].trim(); if (/^["'].*["']$/.test(v)) v = v.slice(1, -1); meta[kv[1]] = v; }
}
const body = m ? m[2] : text;
const title = (body.match(/^#\s+(.+)$/m)?.[1] || '').replace(/\s+Skill$/i, '');
return { meta, title };
}
function firstSentence(desc) {
let s = (desc || '').split(/(?<=\.)\s+/)[0].trim();
if (/^use\b/i.test(s)) s = (desc || '').replace(/\s+/g, ' ');
return s.replace(/\s+/g, ' ').trim();
}
// Pool = production-tier skills (the most impressive to feature).
const tiers = existsSync(join(root, 'skill-tiers.json'))
? JSON.parse(readFileSync(join(root, 'skill-tiers.json'), 'utf8'))
: { productionReady: [] };
let pool = (tiers.productionReady || []).filter((n) => existsSync(join(root, 'skills', n, 'SKILL.md')));
if (!pool.length) pool = readdirSync(join(root, 'skills')).filter((n) => existsSync(join(root, 'skills', n, 'SKILL.md')));
pool.sort();
// Deterministic weekly rotation.
const weeks = Math.floor(Date.now() / (7 * 24 * 3600 * 1000));
const name = pool[weeks % pool.length];
const { meta, title } = parseFrontmatter(readFileSync(join(root, 'skills', name, 'SKILL.md'), 'utf8'));
const summary = firstSentence(meta.description);
const link = `${PLAY}/index.html?skill=${name}`;
const xPost =
`🛠️ Skill of the week: ${title}\n\n${summary}\n\n` +
`Run it free in your browser (no install) 👇\n${link}\n\n#ClaudeAI #AItools #ProductManagement`;
const liPost =
`🛠️ Skill of the week: ${title}\n\n${summary}\n\n` +
`It's one of 174 open-source AI skills that make Claude, ChatGPT, and Gemini produce real professional work. ` +
`Try this one free in the browser — no install:\n${link}\n\n${REPO}`;
const out = { week: weeks, skill: name, title, summary, link, generatedAt: new Date().toISOString(), posts: { x: xPost, linkedin: liPost } };
writeFileSync(join(root, 'web', 'skill-of-the-week.json'), JSON.stringify(out, null, 2) + '\n');
console.log(`Skill of the week (#${weeks}): ${title} (${name})\n`);
console.log('--- X ---\n' + xPost + '\n\n--- LinkedIn ---\n' + liPost);
if (process.env.GITHUB_STEP_SUMMARY) {
appendFileSync(process.env.GITHUB_STEP_SUMMARY,
`## 🛠️ Skill of the week: ${title}\n\n${summary}\n\n[Run it](${link})\n\n` +
`### X / Twitter\n\`\`\`\n${xPost}\n\`\`\`\n\n### LinkedIn\n\`\`\`\n${liPost}\n\`\`\`\n`);
}
// Optional auto-publish hook (Zapier / Make / Buffer / Slack incoming webhook).
const webhook = process.env.POST_WEBHOOK_URL;
if (webhook) {
try {
const res = await fetch(webhook, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ text: xPost, linkedin: liPost, skill: name, link }),
});
console.log(`\nPosted to webhook: HTTP ${res.status}`);
} catch (e) {
console.error('Webhook post failed:', e.message);
process.exitCode = 1;
}
}
+192 -58
View File
@@ -15,6 +15,11 @@ const TIER_META = {
experimental: { label: 'Experimental', cls: 'tier-experimental', dot: '🟡' },
};
// Small "eval-verified" badge for skills scored by the eval harness.
function evalBadgeText(s) {
return s.eval ? `${s.eval.score}/5` : '';
}
init();
async function init() {
@@ -46,6 +51,10 @@ async function init() {
el('copyGemini').addEventListener('click', () => copyPrompt('gemini'));
el('copyClaude').addEventListener('click', () => copyPrompt('claude'));
// "Which skill do I need?" recommender + shareable links.
el('recommendInput').addEventListener('input', renderRecommendations);
el('shareBtn').addEventListener('click', shareSkill);
try {
const res = await fetch('skills.json');
const data = await res.json();
@@ -64,6 +73,84 @@ async function init() {
sel.appendChild(o);
}
renderGallery();
applyShareLink(); // open a skill (and prefill inputs) if the URL points to one
}
// ---------- Recommender: rank skills by a free-text task description ----------
function scoreSkill(s, terms) {
const name = s.name.toLowerCase();
const desc = (s.description || '').toLowerCase();
const hay = (name + ' ' + desc + ' ' + (s.instructions || '')).toLowerCase();
let score = 0;
for (const t of terms) {
if (name.includes(t)) score += 5;
if (desc.includes(t)) score += 3;
else if (hay.includes(t)) score += 1;
}
return score;
}
function renderRecommendations() {
const box = el('recommendResults');
const q = el('recommendInput').value.toLowerCase().trim();
if (q.length < 3) { box.hidden = true; box.innerHTML = ''; return; }
const terms = [...new Set(q.split(/\s+/).filter((t) => t.length > 2))];
const ranked = SKILLS.map((s) => ({ s, score: scoreSkill(s, terms) }))
.filter((x) => x.score > 0)
.sort((a, b) => b.score - a.score || (b.s.eval?.score || 0) - (a.s.eval?.score || 0))
.slice(0, 4);
box.innerHTML = '';
if (!ranked.length) { box.hidden = false; box.innerHTML = '<span class="recommend-empty">No close match — try the search below.</span>'; return; }
const lead = document.createElement('span');
lead.className = 'recommend-lead';
lead.textContent = 'Top matches:';
box.appendChild(lead);
for (const { s } of ranked) {
const chip = document.createElement('button');
chip.className = 'recommend-chip';
chip.type = 'button';
chip.textContent = s.title + (s.eval ? `${s.eval.score}/5` : '');
chip.addEventListener('click', () => { el('recommendInput').value = ''; box.hidden = true; selectSkill(s); });
box.appendChild(chip);
}
box.hidden = false;
}
// ---------- Shareable links: ?skill=<name>&i=<base64 inputs> ----------
function shareSkill() {
if (!current) return;
const fields = [...el('inputForm').querySelectorAll('input, textarea')];
const values = fields.map((f) => f.value);
const url = new URL(location.href.split('?')[0]);
url.searchParams.set('skill', current.name);
if (values.some((v) => v.trim())) {
try {
const packed = btoa(unescape(encodeURIComponent(JSON.stringify(values))));
if (packed.length < 1800) url.searchParams.set('i', packed); // keep links sane
} catch (_) { /* skip inputs if they don't encode */ }
}
const link = url.toString();
navigator.clipboard.writeText(link).then(
() => { el('shareMsg').textContent = 'Link copied — anyone who opens it lands on this skill, prefilled.'; },
() => { el('shareMsg').textContent = link; }
);
}
function applyShareLink() {
const params = new URLSearchParams(location.search);
const name = params.get('skill');
if (!name) return;
const s = SKILLS.find((x) => x.name === name);
if (!s) return;
selectSkill(s);
const packed = params.get('i');
if (packed) {
try {
const values = JSON.parse(decodeURIComponent(escape(atob(packed))));
const fields = [...el('inputForm').querySelectorAll('input, textarea')];
fields.forEach((f, i) => { if (values[i] != null) f.value = values[i]; });
} catch (_) { /* ignore malformed input payloads */ }
}
}
// ---------- Gallery (tiles) ----------
@@ -94,9 +181,16 @@ function renderGallery() {
const card = document.createElement('button');
card.className = 'skill-card';
card.innerHTML =
`<div class="card-tags"><span class="card-bundle"></span><span class="card-tier"></span></div>` +
`<div class="card-tags"><span class="card-bundle"></span><span class="card-eval"></span><span class="card-tier"></span></div>` +
`<h3 class="card-title"></h3><p class="card-summary"></p>`;
card.querySelector('.card-bundle').textContent = s.plugin;
const evalEl = card.querySelector('.card-eval');
if (s.eval) {
evalEl.textContent = evalBadgeText(s);
evalEl.title = `Eval-scored ${s.eval.score}/5 across ${s.eval.runs} model runs`;
} else {
evalEl.remove();
}
const tierEl = card.querySelector('.card-tier');
tierEl.textContent = `${meta.dot} ${meta.label}`;
tierEl.classList.add(meta.cls);
@@ -127,12 +221,24 @@ function selectSkill(s) {
const tierTag = el('skillTier');
tierTag.textContent = `${meta.dot} ${meta.label}`;
tierTag.className = 'tier-tag ' + meta.cls;
const evalTag = el('skillEval');
if (s.eval) {
evalTag.textContent = `✅ Eval-scored ${s.eval.score}/5`;
evalTag.title = `Scored ${s.eval.score}/5 by an LLM judge across ${s.eval.runs} model runs`;
evalTag.hidden = false;
} else {
evalTag.hidden = true;
}
el('skillTitle').textContent = s.title;
el('skillDesc').textContent = s.description;
el('elsewhere').open = false;
el('copyMsg').textContent = '';
el('shareMsg').textContent = '';
el('outputWrap').hidden = true;
el('output').innerHTML = '';
el('output').hidden = false;
el('compareGrid').hidden = true;
el('compareGrid').innerHTML = '';
setStatus('');
window.scrollTo({ top: 0 });
@@ -160,6 +266,58 @@ function makeField(inp, i) {
return wrap;
}
// Stream one completion from the API into a target node. Returns the full text.
async function streamCompletion({ key, model, system, userMessage, node, signal }) {
const res = await fetch(API_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': key,
'anthropic-version': '2023-06-01',
'anthropic-dangerous-direct-browser-access': 'true',
},
body: JSON.stringify({
model,
max_tokens: 8192,
stream: true,
...(system ? { system } : {}),
messages: [{ role: 'user', content: userMessage }],
}),
signal,
});
if (!res.ok) throw new Error(parseApiError(await res.text(), res.status));
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let acc = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const payload = line.slice(5).trim();
if (!payload || payload === '[DONE]') continue;
let evt;
try { evt = JSON.parse(payload); } catch (_) { continue; }
if (evt.type === 'content_block_delta' && evt.delta && evt.delta.text) {
acc += evt.delta.text;
renderMarkdown(node, acc, true);
} else if (evt.type === 'error') {
throw new Error(evt.error ? evt.error.message : 'Stream error from the API.');
}
}
}
renderMarkdown(node, acc, false);
return acc;
}
const SKILL_SUFFIX =
'\n\n---\nThe user has provided their inputs below. Execute this skill now and produce the complete output. Do not ask follow-up questions — work with what is given and note any reasonable assumptions.';
// ---------- Run ----------
async function run() {
const key = el('apiKey').value.trim();
@@ -171,74 +329,50 @@ async function run() {
if (missing.length) return setStatus(`Fill in: ${missing.map((f) => f.dataset.label).join(', ')}`, true);
const userMessage = buildUserMessage(fields);
const system = current.instructions +
'\n\n---\nThe user has provided their inputs below. Execute this skill now and produce the complete output. Do not ask follow-up questions — work with what is given and note any reasonable assumptions.';
const system = current.instructions + SKILL_SUFFIX;
const model = el('model').value;
const compare = el('compareToggle').checked;
const out = el('output');
out.innerHTML = '';
out.dataset.raw = '';
el('outputWrap').hidden = false;
el('runBtn').disabled = true;
el('stopBtn').hidden = false;
setStatus('Running…');
controller = new AbortController();
// Single mode → #output. Compare mode → two panes in #compareGrid.
const out = el('output');
const grid = el('compareGrid');
let withNode, plainNode;
if (compare) {
out.hidden = true;
grid.hidden = false;
grid.innerHTML =
'<div class="compare-pane"><div class="compare-label">✨ With the skill</div><article class="output markdown" id="paneWith"></article></div>' +
'<div class="compare-pane"><div class="compare-label">📄 Plain prompt (no skill)</div><article class="output markdown" id="panePlain"></article></div>';
withNode = el('paneWith');
plainNode = el('panePlain');
} else {
grid.hidden = true;
out.hidden = false;
out.innerHTML = '';
out.dataset.raw = '';
}
let acc = '';
try {
const res = await fetch(API_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': key,
'anthropic-version': '2023-06-01',
'anthropic-dangerous-direct-browser-access': 'true',
},
body: JSON.stringify({
model: el('model').value,
max_tokens: 8192,
stream: true,
system,
messages: [{ role: 'user', content: userMessage }],
}),
signal: controller.signal,
});
if (!res.ok) throw new Error(parseApiError(await res.text(), res.status));
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const payload = line.slice(5).trim();
if (!payload || payload === '[DONE]') continue;
let evt;
try {
evt = JSON.parse(payload);
} catch (_) {
continue; // skip an unparseable / partial SSE line
}
if (evt.type === 'content_block_delta' && evt.delta && evt.delta.text) {
acc += evt.delta.text;
renderMarkdown(out, acc, true);
} else if (evt.type === 'error') {
throw new Error(evt.error ? evt.error.message : 'Stream error from the API.');
}
}
setStatus(compare ? 'Running both…' : 'Running…');
if (compare) {
// Plain = the same inputs with no skill system prompt.
[acc] = await Promise.all([
streamCompletion({ key, model, system, userMessage, node: withNode, signal: controller.signal }),
streamCompletion({ key, model, system: '', userMessage, node: plainNode, signal: controller.signal }),
]);
} else {
acc = await streamCompletion({ key, model, system, userMessage, node: out, signal: controller.signal });
}
renderMarkdown(out, acc, false);
out.dataset.raw = acc;
out.dataset.raw = acc; // copy/download use the skill output, in either mode
setStatus('Done.');
} catch (e) {
if (e.name === 'AbortError') {
out.dataset.raw = acc;
renderMarkdown(out, acc, false);
setStatus('Stopped.');
} else {
setStatus(e.message || 'Request failed.', true);
+21
View File
@@ -20,6 +20,26 @@ const experimentalSet = new Set(TIERS.experimental);
const tierFor = (name) =>
productionSet.has(name) ? 'production' : experimentalSet.has(name) ? 'experimental' : 'stable';
// --- Eval scores (from evals/results.json) ---
// Average the per-model "overall" (a 05 rubric score) for each scored skill.
// Only skills with eval cases get a score; the rest stay unscored (honest).
const evalScores = {};
const evalsFile = join(root, 'evals', 'results.json');
if (existsSync(evalsFile)) {
try {
const acc = {};
for (const r of JSON.parse(readFileSync(evalsFile, 'utf8')).results || []) {
(acc[r.skill] ||= []).push(r.overall);
}
for (const [skill, arr] of Object.entries(acc)) {
evalScores[skill] = {
score: Math.round((arr.reduce((a, b) => a + b, 0) / arr.length) * 10) / 10,
runs: arr.length,
};
}
} catch { /* leave unscored on parse error */ }
}
// --- Map each skill name -> plugin bundle (for grouping/filtering) ---
const skillToPlugin = {};
if (existsSync(pluginsDir)) {
@@ -109,6 +129,7 @@ for (const name of readdirSync(skillsDir)) {
summary: summarize(meta.description || ''),
plugin: skillToPlugin[name] || 'other',
tier: tierFor(name),
eval: evalScores[meta.name || name] || null,
inputs: parseInputs(body),
instructions: body.trim(),
});
+78
View File
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sample Outputs — see what the skills produce</title>
<meta name="description" content="Real example outputs from the professional skill library — see exactly what each skill produces before you run it." />
<link rel="stylesheet" href="styles.css" />
<script src="https://cdn.jsdelivr.net/npm/marked@12.0.0/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.9/dist/purify.min.js"></script>
</head>
<body>
<header class="topbar">
<div class="brand">
<img src="assets/product-notes.jpg" alt="Product Notes" class="brand-logo" />
<div class="brand-text">
<h1>Sample Outputs</h1>
<p class="tagline">See exactly what each skill produces — before you run anything.</p>
</div>
</div>
</header>
<div class="key-note">
📄 These are real outputs from the skills. Like one? <a href="index.html">Run it yourself in the Playground →</a>
· 📚 <a href="catalog.html">Catalog</a> · 🏆 <a href="leaderboard.html">Leaderboard</a>
</div>
<main class="main">
<div class="recommend" style="margin-bottom:18px">
<input id="filter" type="search" placeholder="Filter samples…" />
</div>
<section id="samples"></section>
</main>
<script>
const elx = (id) => document.getElementById(id);
let SAMPLES = [];
async function init() {
try {
SAMPLES = (await (await fetch('samples.json')).json()).samples;
} catch (e) {
elx('samples').innerHTML = '<p class="empty-msg">Could not load samples.json. Run <code>node scripts/build-samples.mjs</code>.</p>';
return;
}
elx('filter').addEventListener('input', render);
render();
}
function render() {
const q = elx('filter').value.toLowerCase().trim();
const box = elx('samples');
box.innerHTML = '';
const list = SAMPLES.filter((s) => !q || (s.title + ' ' + s.skill + ' ' + s.input).toLowerCase().includes(q));
if (!list.length) { box.innerHTML = '<p class="empty-msg">No samples match.</p>'; return; }
for (const s of list) {
const card = document.createElement('details');
card.className = 'sample-card';
card.open = list.length <= 2;
const note = s.source ? `<span class="sample-source">${s.source}</span>` : '';
card.innerHTML =
`<summary><span class="sample-title">${s.title}</span> ${note}` +
`<a class="sample-run" href="index.html?skill=${encodeURIComponent(s.skill)}" onclick="event.stopPropagation()">Run this →</a></summary>` +
`<p class="sample-input"><strong>Input:</strong> ${escapeHtml(s.input)}</p>` +
`<div class="output markdown sample-output"></div>`;
card.querySelector('.sample-output').innerHTML = DOMPurify.sanitize(marked.parse(s.output));
box.appendChild(card);
}
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
}
init();
</script>
</body>
</html>
+11 -1
View File
@@ -34,7 +34,12 @@
<div class="key-note">
🔒 Your key is stored only in this browser and sent directly to api.anthropic.com — never to us.
Get one at <a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener">console.anthropic.com</a>.
· 📚 <a href="catalog.html">Catalog</a> · 🏆 <a href="leaderboard.html">Leaderboard</a>
· 📄 <a href="examples.html">Sample outputs</a> · 📚 <a href="catalog.html">Catalog</a> · 🏆 <a href="leaderboard.html">Leaderboard</a>
</div>
<div class="recommend" id="recommend">
<input id="recommendInput" type="text" placeholder="🧭 Not sure which skill? Describe your task — e.g. “explain a metric drop to my CEO”" />
<div id="recommendResults" class="recommend-results" hidden></div>
</div>
<div class="controls" id="controls">
@@ -60,8 +65,11 @@
<div class="skill-head">
<span class="bundle-tag" id="skillBundle"></span>
<span class="tier-tag" id="skillTier"></span>
<span class="eval-badge" id="skillEval" hidden></span>
<h2 id="skillTitle"></h2>
<p id="skillDesc" class="skill-desc"></p>
<button id="shareBtn" class="ghost share-btn" type="button" title="Copy a link that opens this skill with these inputs">🔗 Share</button>
<span id="shareMsg" class="copy-msg"></span>
</div>
<details class="elsewhere" id="elsewhere">
@@ -80,6 +88,7 @@
<div class="actions">
<button id="runBtn" class="primary" type="button">Run with my Claude key</button>
<button id="stopBtn" class="ghost" type="button" hidden>Stop</button>
<label class="compare-toggle" title="Run the same inputs with and without the skill, side by side"><input type="checkbox" id="compareToggle" /> ⚖️ Compare vs. plain prompt</label>
<span id="status" class="status"></span>
</div>
@@ -92,6 +101,7 @@
</div>
</div>
<article id="output" class="output markdown"></article>
<div id="compareGrid" class="compare-grid" hidden></div>
</div>
</section>
</main>
+1
View File
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
{
"week": 2946,
"skill": "retention-analysis",
"title": "Retention Analysis",
"summary": "Structure a retention analysis, churn investigation, or engagement deep-dive for any product team.",
"link": "https://mohitagw15856.github.io/pm-claude-skills/index.html?skill=retention-analysis",
"generatedAt": "2026-06-19T08:52:42.226Z",
"posts": {
"x": "🛠️ Skill of the week: Retention Analysis\n\nStructure a retention analysis, churn investigation, or engagement deep-dive for any product team.\n\nRun it free in your browser (no install) 👇\nhttps://mohitagw15856.github.io/pm-claude-skills/index.html?skill=retention-analysis\n\n#ClaudeAI #AItools #ProductManagement",
"linkedin": "🛠️ Skill of the week: Retention Analysis\n\nStructure a retention analysis, churn investigation, or engagement deep-dive for any product team.\n\nIt's one of 174 open-source AI skills that make Claude, ChatGPT, and Gemini produce real professional work. Try this one free in the browser — no install:\nhttps://mohitagw15856.github.io/pm-claude-skills/index.html?skill=retention-analysis\n\n⭐ https://github.com/mohitagw15856/pm-claude-skills"
}
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+57
View File
@@ -187,8 +187,65 @@ button.ghost:hover { border-color: var(--accent); }
.cursor::after { content: "▍"; color: var(--accent); animation: blink 1s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }
/* ---------- Recommender ---------- */
.recommend { max-width: 1100px; margin: 18px auto 0; padding: 0 4px; }
.recommend > input {
width: 100%; box-sizing: border-box; padding: 12px 14px; font-size: 14px;
background: var(--panel); color: var(--text);
border: 1px solid var(--border); border-radius: 10px;
}
.recommend > input:focus { outline: none; border-color: var(--accent); }
.recommend-results {
display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px;
}
.recommend-lead { font-size: 12.5px; color: var(--muted); }
.recommend-empty { font-size: 12.5px; color: var(--muted); }
.recommend-chip {
font-size: 12.5px; padding: 6px 11px; border-radius: 99px; cursor: pointer;
background: rgba(217,119,87,.12); color: var(--accent-2);
border: 1px solid rgba(217,119,87,.35);
}
.recommend-chip:hover { background: rgba(217,119,87,.22); }
/* ---------- Eval badges ---------- */
.card-eval, .eval-badge {
font-size: 10px; font-weight: 700; letter-spacing: .02em; padding: 2px 7px;
border-radius: 99px; white-space: nowrap;
color: #6ee7b7; background: rgba(16,185,129,.12); border: 1px solid rgba(16,185,129,.35);
}
.eval-badge { display: inline-block; font-size: 11px; margin: 0 0 8px 6px; }
/* ---------- Share button ---------- */
.share-btn { margin-top: 10px; }
/* ---------- Sample-output gallery ---------- */
.sample-card {
max-width: 880px; margin: 0 auto 14px; background: var(--panel);
border: 1px solid var(--border); border-radius: 12px; padding: 4px 18px;
}
.sample-card summary {
cursor: pointer; padding: 12px 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.sample-title { font-size: 15px; font-weight: 600; }
.sample-source { font-size: 11px; color: var(--muted); }
.sample-run { margin-left: auto; font-size: 12.5px; color: var(--accent-2); text-decoration: none; }
.sample-run:hover { text-decoration: underline; }
.sample-input { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin: 0 0 8px; }
.sample-output { border-top: 1px solid var(--border); padding-top: 12px; }
/* ---------- Compare mode ---------- */
.compare-toggle { font-size: 12.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.compare-pane { min-width: 0; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.compare-label {
font-size: 12px; font-weight: 600; padding: 8px 12px;
background: var(--panel-2); border-bottom: 1px solid var(--border);
}
.compare-pane .output { padding: 12px 14px; }
@media (max-width: 620px) {
.key-field input { width: 200px; }
.brand-text h1 { font-size: 15px; }
.gallery { grid-template-columns: 1fr; }
.compare-grid { grid-template-columns: 1fr; }
}
+72
View File
@@ -0,0 +1,72 @@
{
"_comment": "Workflow recipes — composite chains that run several skills in sequence, passing each output forward as context for the next. Single source of truth: consumed by the slash commands (commands/<command>.md), the MCP server (list_workflows/get_workflow), and WORKFLOWS.md. Each step names an existing skill in skills/<skill>/SKILL.md.",
"workflows": [
{
"id": "ship-a-feature",
"command": "/ship-a-feature",
"name": "Ship a Feature",
"lifecycle": "Discover → Decide → Build → Ship",
"summary": "Take a raw feature idea from fuzzy brief all the way to a launch plan, end to end.",
"steps": [
{ "skill": "ambiguity-resolver", "produces": "a sharp problem statement and scoped boundaries", "passes": "the framed problem" },
{ "skill": "prd-template", "produces": "a full PRD with goals, requirements, and success metrics", "passes": "the PRD scope and metrics" },
{ "skill": "rice-prioritisation", "produces": "a RICE score positioning this work against alternatives", "passes": "the priority and confidence" },
{ "skill": "roadmap-narrative", "produces": "where this sits on the roadmap and the story around it", "passes": "the roadmap framing" },
{ "skill": "go-to-market", "produces": "a launch plan: audience, messaging, channels, and timeline", "passes": null }
]
},
{
"id": "close-the-quarter",
"command": "/close-the-quarter",
"name": "Close the Quarter",
"lifecycle": "Measure → Communicate",
"summary": "Turn the quarter's raw numbers into a leadership-ready story and board deck.",
"steps": [
{ "skill": "metrics-framework", "produces": "the metric tree and what actually moved", "passes": "the key metric movements" },
{ "skill": "churn-analysis", "produces": "why customers left and what is avoidable", "passes": "the retention findings" },
{ "skill": "executive-update", "produces": "a tight leadership briefing of the quarter", "passes": "the headline narrative" },
{ "skill": "board-deck-narrative", "produces": "a slide-by-slide board deck storyline", "passes": null }
]
},
{
"id": "launch-a-product",
"command": "/launch-a-product",
"name": "Launch a Product",
"lifecycle": "Decide → Ship",
"summary": "Go from competitive landscape to positioning to a fully checklisted launch and press release.",
"steps": [
{ "skill": "competitor-teardown", "produces": "the competitive map and gaps to exploit", "passes": "the positioning opportunity" },
{ "skill": "product-positioning-doc", "produces": "positioning, value props, and messaging pillars", "passes": "the approved messaging" },
{ "skill": "go-to-market", "produces": "the GTM plan across audience and channels", "passes": "the launch plan" },
{ "skill": "product-launch-checklist", "produces": "an owner-by-owner launch readiness checklist", "passes": "the launch date and scope" },
{ "skill": "press-release", "produces": "the announcement press release", "passes": null }
]
},
{
"id": "rescue-an-account",
"command": "/rescue-an-account",
"name": "Rescue an Account",
"lifecycle": "Measure → Communicate",
"summary": "Diagnose an at-risk customer and build the full save play through to renewal.",
"steps": [
{ "skill": "cs-health-scorecard", "produces": "a health score with the specific risk drivers", "passes": "the risk profile" },
{ "skill": "churn-analysis", "produces": "the root cause and whether the risk is avoidable", "passes": "the diagnosis" },
{ "skill": "cs-escalation-brief", "produces": "an internal escalation brief for the save", "passes": "the resolution plan" },
{ "skill": "renewal-playbook", "produces": "the renewal strategy and negotiation plan", "passes": null }
]
},
{
"id": "run-discovery",
"command": "/run-discovery",
"name": "Run Discovery",
"lifecycle": "Discover → Decide",
"summary": "From a vague opportunity to validated insight and a prioritised next step.",
"steps": [
{ "skill": "ambiguity-resolver", "produces": "a one-page problem brief from the fuzzy opportunity", "passes": "the scoped question" },
{ "skill": "discovery-interview-guide", "produces": "a screener and discussion guide for user interviews", "passes": "the research focus" },
{ "skill": "user-research-synthesis", "produces": "themes and insights from the research", "passes": "the validated insights" },
{ "skill": "rice-prioritisation", "produces": "a ranked, defensible list of what to do next", "passes": null }
]
}
]
}