skills/composites/pipeline-review/SKILL.md
Pipeline analysis composite. Pulls deal/meeting data from any CRM or tracking system, analyzes the pipeline over a user-defined period (weekly, fortnightly, monthly, quarterly), and produces both an executive summary and a detailed diagnostic report. Covers volume, qualification rates, source effectiveness, stage velocity, stuck deals, and actionable recommendations. Tool-agnostic — works with any CRM (Salesforce, HubSpot, Pipedrive, Close, Supabase, CSV).
npx skillsauth add athina-ai/goose-skills pipeline-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Pulls deal and meeting data from whatever system the user tracks their pipeline in, analyzes it over a chosen time period, and produces a report that answers the questions a founder, sales leader, or AE actually cares about: Are we booking enough? Are they qualified? Where are deals getting stuck? What's working and what isn't?
Two output modes:
Both are always produced. The executive summary sits at the top of the report.
Load this composite when:
On first run, collect and store these preferences. Skip on subsequent runs.
| Question | Options | Stored As |
|----------|---------|-----------|
| Where do you track your pipeline? | Salesforce / HubSpot / Pipedrive / Close / Supabase / Google Sheets / CSV / Notion / Other | crm_tool |
| How do we access it? | API / Export CSV / MCP tools / Direct query | access_method |
| What object represents a "deal" in your system? | Opportunity / Deal / Lead / Meeting / Custom | deal_object |
| Question | Purpose | Stored As |
|----------|---------|-----------|
| What are your pipeline stages in order? | Map data to a standard funnel | pipeline_stages |
| Which stage means "qualified"? | Qualification rate calculation | qualified_stage |
| Which stage means "closed won"? | Win rate calculation | won_stage |
| Which stage means "closed lost"? | Loss analysis | lost_stage |
| What is your expected sales cycle length? (days) | Identify stuck deals | expected_cycle_days |
Example stage mapping:
pipeline_stages: [
"Lead",
"Meeting Booked",
"Meeting Held",
"Qualified (Discovery Done)",
"Proposal Sent",
"Negotiation",
"Closed Won",
"Closed Lost"
]
qualified_stage: "Qualified (Discovery Done)"
won_stage: "Closed Won"
lost_stage: "Closed Lost"
expected_cycle_days: 30
Different CRMs use different field names. Map the user's fields to standard analysis fields.
| Standard Field | Purpose | Stored As | Examples |
|---------------|---------|-----------|---------|
| Deal name | Identification | field_deal_name | "Name", "Deal Name", "Opportunity Name" |
| Company | Account grouping | field_company | "Company", "Account", "Organization" |
| Stage | Funnel position | field_stage | "Stage", "Pipeline Stage", "Status" |
| Owner | Rep-level analysis | field_owner | "Owner", "Assigned To", "Rep" |
| Source | Channel attribution | field_source | "Lead Source", "Source", "Channel", "UTM Source" |
| Created date | Period filtering | field_created_date | "Created Date", "Created At", "Date Added" |
| Close date | Velocity tracking | field_close_date | "Close Date", "Expected Close", "Closed At" |
| Amount | Revenue analysis | field_amount | "Amount", "Deal Value", "ARR", "MRR" |
| Last activity date | Stale deal detection | field_last_activity | "Last Activity", "Last Modified", "Last Touched" |
| Meeting date | Meeting analysis | field_meeting_date | "Meeting Date", "Call Date", "Demo Date" |
| Qualification status | Qual rate | field_qual_status | "Qualified", "ICP Fit", "BANT Score" |
| Loss reason | Loss analysis | field_loss_reason | "Loss Reason", "Closed Lost Reason", "Disqualification Reason" |
Not all fields are required. The analysis adapts to whatever data is available. Minimum viable: deal name + stage + created date.
| Question | Purpose | Stored As |
|----------|---------|-----------|
| What's your target meeting volume per week/month? | Compare actuals to goals | target_meetings |
| What's your target qualification rate? | Flag if below target | target_qual_rate |
| What's your target win rate? | Flag if below target | target_win_rate |
| What's your target pipeline value? | Revenue gap analysis | target_pipeline_value |
Store config in the current working directory or wherever the user prefers.
Purpose: Extract deal/meeting data from the configured CRM for the specified time period.
period: {
type: "weekly" | "fortnightly" | "monthly" | "quarterly" | "custom"
start_date: string # ISO date (auto-calculated from type, or user-specified)
end_date: string # ISO date (default: today)
comparison_period: boolean # Include prior period for trend comparison (default: true)
}
crm_tool: string # From config
access_method: string # From config
field_mapping: { ... } # From config
Based on crm_tool and access_method:
| CRM | Access Method | How to Pull | |-----|--------------|-------------| | Salesforce | API / CSV export | SOQL query or user uploads CSV export | | HubSpot | API / CSV export | Deals API or user uploads CSV export | | Pipedrive | API / CSV export | Deals API or user uploads CSV export | | Close | API / CSV export | Leads/Opportunities API or CSV | | Supabase | Direct query | Query deals/outreach_log tables | | Google Sheets | Sheets API / CSV | Read sheet or user exports CSV | | Notion | Notion MCP | Query database | | CSV | File read | User provides file path |
Pull two datasets:
Regardless of source, normalize all data into a standard structure:
deals: [
{
id: string
name: string
company: string
stage: string # Mapped to pipeline_stages
owner: string | null
source: string | null # Lead source / channel
created_date: string # ISO date
close_date: string | null # ISO date
last_activity_date: string | null
meeting_date: string | null
amount: number | null
qualification_status: string | null
loss_reason: string | null
days_in_current_stage: integer # Calculated
total_age_days: integer # Calculated from created_date
}
]
pipeline_data: {
current_period: {
start_date: string
end_date: string
deals: [...] # Standardized deal records
deal_count: integer
}
comparison_period: {
start_date: string
end_date: string
deals: [...]
deal_count: integer
} | null
}
## Data Pulled
Source: [CRM name]
Current period: [start] to [end] — X deals
Comparison period: [start] to [end] — Y deals
Fields available: [list of mapped fields]
Fields missing: [any unmapped fields — analysis will adapt]
Data looks correct? (Y/n)
Purpose: Run the full analysis across seven dimensions. Pure computation + LLM reasoning — inherently tool-agnostic.
pipeline_data: { ... } # From Step 1
pipeline_stages: string[] # From config
qualified_stage: string # From config
won_stage: string # From config
lost_stage: string # From config
expected_cycle_days: integer # From config
benchmarks: { ... } | null # From config (optional)
Run all seven analyses on the current period data. Where comparison period exists, calculate period-over-period trends.
Questions answered: How many meetings/deals did we book? Is the volume going up or down?
| Metric | How to Calculate |
|--------|-----------------|
| Total deals created | Count deals with created_date in period |
| Meetings booked | Count deals that reached "Meeting Booked" stage or have a meeting_date |
| Meetings held | Count deals at or past "Meeting Held" stage |
| No-show rate | (Meetings booked - Meetings held) / Meetings booked |
| Period-over-period change | Compare to comparison period |
| vs. Target | Compare to target_meetings if set |
| Weekly run rate | Total / weeks in period |
Output:
volume: {
deals_created: integer
meetings_booked: integer
meetings_held: integer
no_show_count: integer
no_show_rate: percentage
weekly_run_rate: float
vs_prior_period: {
deals_change: percentage # "+15%" or "-8%"
meetings_change: percentage
} | null
vs_target: {
target: integer
actual: integer
gap: integer # Positive = ahead, negative = behind
} | null
}
Questions answered: How many meetings were qualified? Unqualified? What's our qualification rate?
| Metric | How to Calculate |
|--------|-----------------|
| Qualified deals | Deals at or past qualified_stage |
| Unqualified deals | Deals that were closed lost before reaching qualified_stage, or deals marked as unqualified |
| Qualification rate | Qualified / (Qualified + Unqualified) |
| Pending qualification | Deals still between "Meeting Held" and qualified_stage |
| Disqualification reasons | Group loss_reason for deals lost pre-qualification |
| vs. Prior period | Compare qualification rate |
| vs. Target | Compare to target_qual_rate if set |
Output:
qualification: {
qualified_count: integer
unqualified_count: integer
pending_count: integer
qualification_rate: percentage
vs_prior_period: percentage_change | null
vs_target: { target: percentage, actual: percentage } | null
top_disqualification_reasons: [
{ reason: string, count: integer, percentage: percentage }
]
}
Questions answered: Where are leads coming from? Which sources produce the most qualified meetings?
| Metric | How to Calculate |
|--------|-----------------|
| Deals by source | Group by source, count |
| Meetings by source | Group by source, count meetings |
| Qualification rate by source | For each source: qualified / total |
| Best source (volume) | Source with most deals |
| Best source (quality) | Source with highest qualification rate (min 5 deals for statistical relevance) |
| Source with highest conversion to won | Source with best close rate |
| Source trends | Compare to prior period |
Output:
source_attribution: {
by_source: [
{
source: string
deals_created: integer
meetings_booked: integer
meetings_qualified: integer
qualification_rate: percentage
deals_won: integer
win_rate: percentage
revenue: number | null
vs_prior_period: percentage_change | null
}
]
best_volume_source: string
best_quality_source: string
best_conversion_source: string
source_concentration_warning: string | null # Flag if >60% from one source
}
Questions answered: Where are deals sitting in the pipeline? How fast are they moving through stages?
| Metric | How to Calculate |
|--------|-----------------|
| Deal count by stage | Group active deals by current stage |
| Revenue by stage | Sum amount per stage |
| Average days in each stage | For deals that passed through each stage, average days_in_current_stage |
| Stage-to-stage conversion | What % of deals move from one stage to the next |
| Pipeline velocity | (Deals × Win Rate × Avg Deal Size) / Avg Cycle Length |
| Total pipeline value | Sum of amount for all active deals |
| Weighted pipeline | Sum of (amount × stage probability) for each deal |
Stage probability defaults (override with actual data if available):
Lead: 5%
Meeting Booked: 10%
Meeting Held: 20%
Qualified: 40%
Proposal Sent: 60%
Negotiation: 80%
Closed Won: 100%
Closed Lost: 0%
Output:
stage_analysis: {
by_stage: [
{
stage: string
deal_count: integer
revenue: number | null
weighted_revenue: number | null
avg_days_in_stage: float
conversion_to_next_stage: percentage
}
]
total_pipeline_value: number | null
weighted_pipeline_value: number | null
pipeline_velocity: number | null
avg_cycle_length_days: float
vs_expected_cycle: string # "On pace", "X days slower than expected", etc.
}
Questions answered: Are there deals that have been sitting too long? What's at risk of going stale?
| Metric | How to Calculate |
|--------|-----------------|
| Stuck deals | Deals where days_in_current_stage > 2× average for that stage, OR > expected_cycle_days total |
| No activity deals | Deals where last_activity_date > 14 days ago |
| Aging deals | Deals where total_age_days > 1.5× expected_cycle_days |
| At-risk revenue | Sum of amount for stuck + no activity + aging deals |
| Concentration risk | Any single deal > 30% of total pipeline value |
Stuck deal threshold: A deal is "stuck" if it's been in its current stage for more than 2× the average time deals spend in that stage. If we don't have enough data for stage averages, use these defaults:
| Stage | Expected Max Days | |-------|------------------| | Lead | 3 days | | Meeting Booked | 7 days | | Meeting Held | 5 days | | Qualified | 10 days | | Proposal Sent | 14 days | | Negotiation | 14 days |
Output:
stuck_and_at_risk: {
stuck_deals: [
{
name: string
company: string
stage: string
days_in_stage: integer
expected_max_days: integer
owner: string | null
amount: number | null
last_activity: string | null
recommended_action: string # "Follow up", "Ask for timeline", "Qualify out"
}
]
no_activity_deals: [
{
name: string
company: string
stage: string
days_since_activity: integer
owner: string | null
recommended_action: string
}
]
at_risk_revenue: number | null
concentration_risks: [
{ deal_name: string, amount: number, percentage_of_pipeline: percentage }
] | null
}
Questions answered: What's our win rate? Why are we losing deals?
| Metric | How to Calculate |
|--------|-----------------|
| Win rate | Won / (Won + Lost) for deals closed in period |
| Win rate by source | Group by source |
| Win rate by owner | Group by rep |
| Average deal size (won) | Avg amount of won deals |
| Average time to close | Avg days from created to closed won |
| Top loss reasons | Group loss_reason, count |
| Loss stage distribution | At which stage are deals most often lost? |
| vs. Prior period | Compare win rate |
| vs. Target | Compare to target_win_rate if set |
Output:
win_loss: {
deals_won: integer
deals_lost: integer
win_rate: percentage
vs_prior_period: percentage_change | null
vs_target: { target: percentage, actual: percentage } | null
avg_deal_size_won: number | null
avg_days_to_close: float
win_rate_by_source: [ { source: string, win_rate: percentage, count: integer } ]
win_rate_by_owner: [ { owner: string, win_rate: percentage, count: integer } ] | null
top_loss_reasons: [
{ reason: string, count: integer, percentage: percentage }
]
loss_by_stage: [
{ stage: string, lost_count: integer, percentage: percentage }
]
}
Questions answered: Are we going to hit our number? Do we have enough pipeline to cover the target?
Only run this analysis if target_pipeline_value or revenue targets are configured.
| Metric | How to Calculate | |--------|-----------------| | Pipeline coverage ratio | Weighted pipeline / remaining quota | | Commit forecast | Sum of deals in Negotiation + Proposal stage | | Best case forecast | Commit + Qualified deals × historical win rate | | Gap to target | Target - best case forecast | | Required deals to close gap | Gap / average deal size | | Required meetings to close gap | Required deals / historical meeting-to-close rate |
Output:
forecast: {
target: number
commit_forecast: number
best_case_forecast: number
gap_to_target: number
pipeline_coverage_ratio: float # 3x+ is healthy, <2x is risky
deals_needed_to_close_gap: integer
meetings_needed_to_close_gap: integer
coverage_assessment: "Healthy (3x+)" | "Adequate (2-3x)" | "At risk (<2x)" | "Critical (<1x)"
} | null
analysis: {
period: { type, start_date, end_date }
volume: { ... }
qualification: { ... }
source_attribution: { ... }
stage_analysis: { ... }
stuck_and_at_risk: { ... }
win_loss: { ... }
forecast: { ... } | null
}
No human checkpoint after this step — the analysis feeds directly into report generation.
Purpose: Transform the raw analysis into two report formats: an executive summary and a detailed diagnostic. Pure LLM reasoning.
analysis: { ... } # From Step 2
benchmarks: { ... } | null # From config
One page. Numbers and trends. What a founder or sales leader needs to see in 60 seconds.
# Pipeline Review — [Period Type]: [Start Date] to [End Date]
## Snapshot
| Metric | This Period | Prior Period | Change |
|--------|------------|-------------|--------|
| Meetings booked | X | Y | +/-Z% |
| Meetings held | X | Y | +/-Z% |
| Qualification rate | X% | Y% | +/-Z pts |
| Win rate | X% | Y% | +/-Z pts |
| Pipeline value | $X | $Y | +/-Z% |
| Avg deal size | $X | $Y | +/-Z% |
| Avg days to close | X | Y | +/-Z |
## Red Flags
- [Any metric trending down significantly]
- [Stuck deals above threshold]
- [Pipeline coverage below 2x]
- [Single source >60% of pipeline]
- [No-show rate above 20%]
## Green Lights
- [Metrics trending up]
- [Sources performing well]
- [Stages moving faster than expected]
## Top 3 Actions
1. [Most impactful thing to do this week]
2. [Second most impactful]
3. [Third most impactful]
Full data tables, charts (as markdown tables), and commentary.
# Pipeline Diagnostic — [Period]
## 1. Volume
[Volume metrics table]
[Commentary: is volume on track? Trending up or down? Meeting goals?]
## 2. Qualification
[Qualification breakdown table]
[Disqualification reasons table]
[Commentary: are we meeting with the right people? Common DQ reasons to address?]
## 3. Source Effectiveness
[Source attribution table — sorted by qualification rate]
[Commentary: which channels are working? Which are wasting time?
Where should we invest more? Where should we cut?]
## 4. Stage Distribution & Velocity
[Stage breakdown table with counts, revenue, avg days, conversion rates]
[Commentary: where is the pipeline fat? Where is it thin?
Is velocity healthy or slowing?]
## 5. Stuck Deals
[Stuck deals table with recommended actions]
[No-activity deals table]
[Commentary: total at-risk revenue, common patterns in stuck deals]
## 6. Win/Loss Analysis
[Win rate table — overall, by source, by owner]
[Loss reasons table]
[Loss by stage table]
[Commentary: why are we losing? At which stage? Any patterns?]
## 7. Forecast & Coverage (if targets set)
[Forecast table]
[Coverage assessment]
[Commentary: will we hit the number? What needs to happen?]
## Recommendations
[Numbered list of specific, actionable recommendations based on the data.
Each recommendation should cite the specific data point that drives it.]
Generate recommendations based on patterns found in the analysis:
| Pattern | Recommendation | |---------|---------------| | Qualification rate <40% | "Review ICP targeting — we're meeting with too many unqualified prospects. Top DQ reason is [X]. Consider tightening [source/criteria]." | | No-show rate >20% | "Implement meeting confirmation flow — send reminders 24h and 1h before. Current no-show rate of X% is costing Y meetings/period." | | One source >60% of pipeline | "Diversify lead sources — [source] drives X% of pipeline. If this channel underperforms, pipeline collapses. Test [alternative channels]." | | High-quality source with low volume | "Scale [source] — it has a X% qualification rate (best in pipeline) but only Y% of volume. Invest more here." | | Deals stuck in a specific stage | "Unblock [stage] — X deals averaging Y days (2× normal). Common pattern: [observation]. Recommended: [specific action]." | | Win rate declining | "Win rate dropped from X% to Y%. Top loss reason shifted to [Z]. Consider: [specific response]." | | Pipeline coverage <2x | "Pipeline coverage is [X]x against [target]. Need Z more qualified deals to close the gap. At current conversion rates, that requires W meetings." | | Average cycle lengthening | "Deals are taking X days longer to close vs. prior period. Bottleneck is at [stage]. Consider: [specific action to accelerate]." | | High loss rate at specific stage | "X% of losses happen at [stage]. This suggests [interpretation]. Consider: [action]." |
report: {
executive_summary: string # Markdown formatted
detailed_diagnostic: string # Markdown formatted
recommendations: [
{
priority: "high" | "medium" | "low"
area: string # "volume", "qualification", "source", "velocity", "stuck", "win_loss", "forecast"
recommendation: string
data_point: string # The specific metric that drove this recommendation
expected_impact: string # What fixing this would do
}
]
stuck_deal_actions: [
{
deal_name: string
company: string
action: string
owner: string | null
}
]
}
Present the executive summary first, then offer the detailed diagnostic:
[Executive Summary rendered]
---
Full detailed diagnostic is also available.
Actions from this review:
1. [High priority recommendation]
2. [High priority recommendation]
3. [Medium priority recommendation]
Stuck deals requiring immediate attention:
| Deal | Company | Stage | Days Stuck | Action |
|------|---------|-------|------------|--------|
| ... | ... | ... | ... | ... |
Want to see the full diagnostic? Or take action on any of these recommendations?
Purpose: Save the report and optionally push it to the user's preferred location.
Based on user preference:
| Destination | How | |-------------|-----| | Markdown file | Save to the current working directory | | Google Sheets | Export data tables to a sheet (metrics, deal list, source breakdown) | | Notion | Push to a Notion database page via Notion MCP | | Slack | Send executive summary to a channel | | Email | Send via AgentMail API (agentmail.dev) or other email API | | stdout | Just display it (default) |
| Step | Tool Dependency | Human Checkpoint | Typical Time | |------|----------------|-----------------|--------------| | 0. Config | None | First run only | 5 min (once) | | 1. Pull Data | Configurable (CRM API, CSV, Supabase, etc.) | Verify data looks correct | 1-2 min | | 2. Analyze | None (computation + LLM reasoning) | None — feeds directly to report | Automatic | | 3. Generate Report | None (LLM reasoning) | Review executive summary, drill into details | 5-10 min | | 4. Export | Configurable (file, Sheets, Notion, etc.) | Optional | 1 min |
Total human review time: ~10-15 minutes for a full pipeline review that would normally take 30-60 minutes of manual CRM digging.
Not every CRM has every field. The analysis degrades gracefully:
| Missing Field | What Gets Skipped | Analysis Still Works? |
|--------------|-------------------|----------------------|
| amount | Revenue metrics, weighted pipeline, forecast | Yes — volume and stage analysis still run |
| source | Source attribution (Analysis 3) | Yes — everything else still runs |
| loss_reason | Loss reason breakdown | Yes — win/loss rate still calculates |
| owner | Per-rep analysis | Yes — aggregate metrics still run |
| last_activity_date | No-activity deal detection | Partially — stuck deals still detected via stage duration |
| close_date | Velocity, avg days to close | Partially — stage distribution still works |
| Comparison period data | Period-over-period trends | Yes — single period analysis still produces full report |
Minimum viable data for a useful report: Deal name + Stage + Created date. Everything else enriches but isn't required.
| Review Type | Period | Audience | Focus | |-------------|--------|----------|-------| | Weekly standup | Last 7 days | Sales team | Volume, stuck deals, this week's priorities | | Fortnightly review | Last 14 days | Sales leader | Qualification, source effectiveness, trends | | Monthly review | Last 30 days | Founder / VP Sales | Full diagnostic, win/loss, forecast | | Quarterly business review | Last 90 days | Leadership / Board | Trends, unit economics, strategic recommendations |
The report depth automatically scales with the period length. A weekly review emphasizes volume and stuck deals. A quarterly review emphasizes trends, conversion rates, and strategic patterns.
content-media
Takes an existing screen recording or demo video and adds professional zoom/pan effects synchronized to the narration. Uses transcript-driven zoom targeting and Remotion for rendering. Optionally replaces audio with a soundtrack.
tools
Repurposes long-form video (podcasts, interviews, talks) into short-form vertical clips for Instagram Reels, TikTok, and YouTube Shorts. Handles transcription, moment selection, clip extraction, speaker-tracked reframing (16:9 to 9:16), and animated captions.
development
Creates talking head videos from any source material (docs, changelogs, blog posts, notes, transcripts). Produces multi-scene videos with avatar narration over screenshots/images using HeyGen v2 API. Supports Quick Shot and Full Producer modes.
tools
Generates Instagram-ready product reels from any e-commerce product page URL. Scrapes product images, classifies by type, generates AI-animated clips via Higgsfield API, creates text overlays with style presets, and composes a 15-20 second reel with music. Supports model-based and product-only reels.