feat: workflow recipes, eval badges, one-click MCP, playground upgrades, sample gallery, skill-of-the-week
Signature features that turn breadth (174 skills) into a differentiated product: - Workflow recipes: 5 cross-profession chains (workflows.json) that pass each output forward — slash commands (/ship-a-feature etc.), WORKFLOWS.md generated by scripts/build-workflows.mjs, README + MCP (list_workflows/get_workflow) wired - Eval-backed quality: real per-skill scores from evals/results.json surfaced as badges in the playground and an honest README section (6 scored skills) - One-click MCP: 'claude mcp add' install + workflow tools, works in any MCP client - Playground: 'which skill?' recommender, with/without compare toggle, shareable ?skill= deep-links with prefilled inputs - Sample-output gallery: hand-written examples for the hero five + generator (scripts/build-samples.mjs) + web/examples.html - Skill-of-the-week: scheduled workflow + script that composes X/LinkedIn posts and posts to an optional webhook Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||||
@@ -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 |
|
| 🔭 [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 |
|
| 📝 [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
|
## Contents
|
||||||
|
|
||||||
- [👋 New here? Start in 30 seconds](#-new-here-start-in-30-seconds)
|
- [👋 New here? Start in 30 seconds](#-new-here-start-in-30-seconds)
|
||||||
- [🔄 One library, the whole professional workflow](#-one-library-the-whole-professional-workflow)
|
- [🔄 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)
|
- [🚀 Quick Install](#-quick-install-2-minutes)
|
||||||
- [🔌 Works With — Cross-Tool Compatibility](#-works-with--cross-tool-compatibility)
|
- [🔌 Works With — Cross-Tool Compatibility](#-works-with--cross-tool-compatibility)
|
||||||
- [🤖 Subagents & Slash Commands](#-subagents--slash-commands)
|
- [🤖 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
|
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:
|
**In Claude Code**, run:
|
||||||
|
|
||||||
/plugin marketplace add mohitagw15856/pm-claude-skills
|
/plugin marketplace add mohitagw15856/pm-claude-skills
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -11,6 +11,18 @@ Claude Code **slash commands** that run a skill on whatever you pass them.
|
|||||||
| `/retro` | Structured sprint retrospective | retro-analysis |
|
| `/retro` | Structured sprint retrospective | retro-analysis |
|
||||||
| `/exec-summary` | Crisp executive summary | executive-summary |
|
| `/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
|
## Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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
|
- [ ] 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)
|
- [ ] Add a `topics` set on the GitHub repo (claude, claude-code, ai-agents, prompt-engineering, productivity)
|
||||||
|
|||||||
@@ -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 3–4 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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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?
|
||||||
@@ -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
@@ -1,6 +1,14 @@
|
|||||||
# MCP Server
|
# 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
|
## 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. |
|
| `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. |
|
| `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. |
|
| `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
|
## 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):
|
**From a local clone** (no npm install):
|
||||||
|
|
||||||
```json
|
```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
|
## How it works
|
||||||
|
|
||||||
|
|||||||
+34
-1
@@ -71,6 +71,15 @@ function loadSkills() {
|
|||||||
const SKILLS = loadSkills();
|
const SKILLS = loadSkills();
|
||||||
const byName = new Map(SKILLS.map((s) => [s.name, s]));
|
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 ───────────────────────────────────────────────────────────────────
|
// ── Tools ───────────────────────────────────────────────────────────────────
|
||||||
const 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.',
|
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'] },
|
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 = {}) {
|
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.`);
|
if (!s) throw new Error(`Unknown skill "${args.name}". Use search_skills or list_skills to find one.`);
|
||||||
return s.body;
|
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}`);
|
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`);
|
process.stderr.write(`[${SERVER_NAME}] ⭐ Star the repo: https://github.com/mohitagw15856/pm-claude-skills\n`);
|
||||||
const rl = createInterface({ input: process.stdin });
|
const rl = createInterface({ input: process.stdin });
|
||||||
rl.on('line', (line) => {
|
rl.on('line', (line) => {
|
||||||
|
|||||||
+6
-2
@@ -46,14 +46,18 @@
|
|||||||
"commands/",
|
"commands/",
|
||||||
"output-styles/",
|
"output-styles/",
|
||||||
"exports/",
|
"exports/",
|
||||||
"skill-tiers.json"
|
"skill-tiers.json",
|
||||||
|
"workflows.json"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"new-skill": "node scripts/new-skill.mjs",
|
"new-skill": "node scripts/new-skill.mjs",
|
||||||
"skillcheck": "node scripts/skillcheck.mjs",
|
"skillcheck": "node scripts/skillcheck.mjs",
|
||||||
"build:exports": "node scripts/build-exports.mjs",
|
"build:exports": "node scripts/build-exports.mjs",
|
||||||
"build:web": "node web/build-skills.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": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|||||||
@@ -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.`);
|
||||||
@@ -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.`);
|
||||||
|
}
|
||||||
@@ -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
@@ -15,6 +15,11 @@ const TIER_META = {
|
|||||||
experimental: { label: 'Experimental', cls: 'tier-experimental', dot: '🟡' },
|
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();
|
init();
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
@@ -46,6 +51,10 @@ async function init() {
|
|||||||
el('copyGemini').addEventListener('click', () => copyPrompt('gemini'));
|
el('copyGemini').addEventListener('click', () => copyPrompt('gemini'));
|
||||||
el('copyClaude').addEventListener('click', () => copyPrompt('claude'));
|
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 {
|
try {
|
||||||
const res = await fetch('skills.json');
|
const res = await fetch('skills.json');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@@ -64,6 +73,84 @@ async function init() {
|
|||||||
sel.appendChild(o);
|
sel.appendChild(o);
|
||||||
}
|
}
|
||||||
renderGallery();
|
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) ----------
|
// ---------- Gallery (tiles) ----------
|
||||||
@@ -94,9 +181,16 @@ function renderGallery() {
|
|||||||
const card = document.createElement('button');
|
const card = document.createElement('button');
|
||||||
card.className = 'skill-card';
|
card.className = 'skill-card';
|
||||||
card.innerHTML =
|
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>`;
|
`<h3 class="card-title"></h3><p class="card-summary"></p>`;
|
||||||
card.querySelector('.card-bundle').textContent = s.plugin;
|
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');
|
const tierEl = card.querySelector('.card-tier');
|
||||||
tierEl.textContent = `${meta.dot} ${meta.label}`;
|
tierEl.textContent = `${meta.dot} ${meta.label}`;
|
||||||
tierEl.classList.add(meta.cls);
|
tierEl.classList.add(meta.cls);
|
||||||
@@ -127,12 +221,24 @@ function selectSkill(s) {
|
|||||||
const tierTag = el('skillTier');
|
const tierTag = el('skillTier');
|
||||||
tierTag.textContent = `${meta.dot} ${meta.label}`;
|
tierTag.textContent = `${meta.dot} ${meta.label}`;
|
||||||
tierTag.className = 'tier-tag ' + meta.cls;
|
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('skillTitle').textContent = s.title;
|
||||||
el('skillDesc').textContent = s.description;
|
el('skillDesc').textContent = s.description;
|
||||||
el('elsewhere').open = false;
|
el('elsewhere').open = false;
|
||||||
el('copyMsg').textContent = '';
|
el('copyMsg').textContent = '';
|
||||||
|
el('shareMsg').textContent = '';
|
||||||
el('outputWrap').hidden = true;
|
el('outputWrap').hidden = true;
|
||||||
el('output').innerHTML = '';
|
el('output').innerHTML = '';
|
||||||
|
el('output').hidden = false;
|
||||||
|
el('compareGrid').hidden = true;
|
||||||
|
el('compareGrid').innerHTML = '';
|
||||||
setStatus('');
|
setStatus('');
|
||||||
window.scrollTo({ top: 0 });
|
window.scrollTo({ top: 0 });
|
||||||
|
|
||||||
@@ -160,6 +266,58 @@ function makeField(inp, i) {
|
|||||||
return wrap;
|
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 ----------
|
// ---------- Run ----------
|
||||||
async function run() {
|
async function run() {
|
||||||
const key = el('apiKey').value.trim();
|
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);
|
if (missing.length) return setStatus(`Fill in: ${missing.map((f) => f.dataset.label).join(', ')}`, true);
|
||||||
|
|
||||||
const userMessage = buildUserMessage(fields);
|
const userMessage = buildUserMessage(fields);
|
||||||
const system = current.instructions +
|
const system = current.instructions + 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.';
|
const model = el('model').value;
|
||||||
|
const compare = el('compareToggle').checked;
|
||||||
|
|
||||||
const out = el('output');
|
|
||||||
out.innerHTML = '';
|
|
||||||
out.dataset.raw = '';
|
|
||||||
el('outputWrap').hidden = false;
|
el('outputWrap').hidden = false;
|
||||||
el('runBtn').disabled = true;
|
el('runBtn').disabled = true;
|
||||||
el('stopBtn').hidden = false;
|
el('stopBtn').hidden = false;
|
||||||
setStatus('Running…');
|
|
||||||
|
|
||||||
controller = new AbortController();
|
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 = '';
|
let acc = '';
|
||||||
try {
|
try {
|
||||||
const res = await fetch(API_URL, {
|
setStatus(compare ? 'Running both…' : 'Running…');
|
||||||
method: 'POST',
|
if (compare) {
|
||||||
headers: {
|
// Plain = the same inputs with no skill system prompt.
|
||||||
'content-type': 'application/json',
|
[acc] = await Promise.all([
|
||||||
'x-api-key': key,
|
streamCompletion({ key, model, system, userMessage, node: withNode, signal: controller.signal }),
|
||||||
'anthropic-version': '2023-06-01',
|
streamCompletion({ key, model, system: '', userMessage, node: plainNode, signal: controller.signal }),
|
||||||
'anthropic-dangerous-direct-browser-access': 'true',
|
]);
|
||||||
},
|
} else {
|
||||||
body: JSON.stringify({
|
acc = await streamCompletion({ key, model, system, userMessage, node: out, signal: controller.signal });
|
||||||
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.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
renderMarkdown(out, acc, false);
|
out.dataset.raw = acc; // copy/download use the skill output, in either mode
|
||||||
out.dataset.raw = acc;
|
|
||||||
setStatus('Done.');
|
setStatus('Done.');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e.name === 'AbortError') {
|
||||||
out.dataset.raw = acc;
|
|
||||||
renderMarkdown(out, acc, false);
|
|
||||||
setStatus('Stopped.');
|
setStatus('Stopped.');
|
||||||
} else {
|
} else {
|
||||||
setStatus(e.message || 'Request failed.', true);
|
setStatus(e.message || 'Request failed.', true);
|
||||||
|
|||||||
@@ -20,6 +20,26 @@ const experimentalSet = new Set(TIERS.experimental);
|
|||||||
const tierFor = (name) =>
|
const tierFor = (name) =>
|
||||||
productionSet.has(name) ? 'production' : experimentalSet.has(name) ? 'experimental' : 'stable';
|
productionSet.has(name) ? 'production' : experimentalSet.has(name) ? 'experimental' : 'stable';
|
||||||
|
|
||||||
|
// --- Eval scores (from evals/results.json) ---
|
||||||
|
// Average the per-model "overall" (a 0–5 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) ---
|
// --- Map each skill name -> plugin bundle (for grouping/filtering) ---
|
||||||
const skillToPlugin = {};
|
const skillToPlugin = {};
|
||||||
if (existsSync(pluginsDir)) {
|
if (existsSync(pluginsDir)) {
|
||||||
@@ -109,6 +129,7 @@ for (const name of readdirSync(skillsDir)) {
|
|||||||
summary: summarize(meta.description || ''),
|
summary: summarize(meta.description || ''),
|
||||||
plugin: skillToPlugin[name] || 'other',
|
plugin: skillToPlugin[name] || 'other',
|
||||||
tier: tierFor(name),
|
tier: tierFor(name),
|
||||||
|
eval: evalScores[meta.name || name] || null,
|
||||||
inputs: parseInputs(body),
|
inputs: parseInputs(body),
|
||||||
instructions: body.trim(),
|
instructions: body.trim(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+11
-1
@@ -34,7 +34,12 @@
|
|||||||
<div class="key-note">
|
<div class="key-note">
|
||||||
🔒 Your key is stored only in this browser and sent directly to api.anthropic.com — never to us.
|
🔒 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>.
|
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>
|
||||||
|
|
||||||
<div class="controls" id="controls">
|
<div class="controls" id="controls">
|
||||||
@@ -60,8 +65,11 @@
|
|||||||
<div class="skill-head">
|
<div class="skill-head">
|
||||||
<span class="bundle-tag" id="skillBundle"></span>
|
<span class="bundle-tag" id="skillBundle"></span>
|
||||||
<span class="tier-tag" id="skillTier"></span>
|
<span class="tier-tag" id="skillTier"></span>
|
||||||
|
<span class="eval-badge" id="skillEval" hidden></span>
|
||||||
<h2 id="skillTitle"></h2>
|
<h2 id="skillTitle"></h2>
|
||||||
<p id="skillDesc" class="skill-desc"></p>
|
<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>
|
</div>
|
||||||
|
|
||||||
<details class="elsewhere" id="elsewhere">
|
<details class="elsewhere" id="elsewhere">
|
||||||
@@ -80,6 +88,7 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button id="runBtn" class="primary" type="button">Run with my Claude key</button>
|
<button id="runBtn" class="primary" type="button">Run with my Claude key</button>
|
||||||
<button id="stopBtn" class="ghost" type="button" hidden>Stop</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>
|
<span id="status" class="status"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -92,6 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<article id="output" class="output markdown"></article>
|
<article id="output" class="output markdown"></article>
|
||||||
|
<div id="compareGrid" class="compare-grid" hidden></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -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
File diff suppressed because one or more lines are too long
@@ -187,8 +187,65 @@ button.ghost:hover { border-color: var(--accent); }
|
|||||||
.cursor::after { content: "▍"; color: var(--accent); animation: blink 1s steps(2) infinite; }
|
.cursor::after { content: "▍"; color: var(--accent); animation: blink 1s steps(2) infinite; }
|
||||||
@keyframes blink { 50% { opacity: 0; } }
|
@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) {
|
@media (max-width: 620px) {
|
||||||
.key-field input { width: 200px; }
|
.key-field input { width: 200px; }
|
||||||
.brand-text h1 { font-size: 15px; }
|
.brand-text h1 { font-size: 15px; }
|
||||||
.gallery { grid-template-columns: 1fr; }
|
.gallery { grid-template-columns: 1fr; }
|
||||||
|
.compare-grid { grid-template-columns: 1fr; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user