
Marketing Context
- 80 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
marketing-context is a skill that creates a foundational marketing context document capturing ICP, positioning, competitive landscape, brand voice, and customer language.
About
This skill creates and maintains a foundational marketing context document covering product positioning, ICP, buyer personas, competitive landscape, brand voice, customer language, and switching dynamics. It can auto-draft from a codebase or be built through a guided interview. Marketers use it as the shared context that every other marketing skill reads before starting.
- Builds a foundational marketing context document that all marketing skills read first
- Captures positioning, ICP, buyer personas, competitive landscape, brand voice, and customer language
- Includes JTBD Four Forces switching dynamics and a competitive analysis framework
Marketing Context by the numbers
- 80 all-time installs (skills.sh)
- Ranked #472 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
marketing-context capabilities & compatibility
- Capabilities
- positioning · audience research · competitive analysis · brand voice
- Use cases
- marketing · research
- Pricing
- Free
What marketing-context says it does
The foundational context document that every marketing skill reads before starting. Captures positioning, ICP, competitive landscape, brand voice, and customer language in one place.
Capture exact customer language, not polished summaries
Product category: [The "shelf" — how customers search for you]
npx skills add https://github.com/borghei/claude-skills --skill marketing-contextAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 80 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Define ICP, positioning, brand voice, and competitive landscape in one foundational marketing context document.
Who is it for?
Marketers establishing the shared ICP, positioning, and brand-voice foundation that other marketing work reads first.
Skip if: Executing specific campaigns or writing page copy, which downstream marketing skills handle.
When should I use this skill?
You are setting up marketing context, defining target audience, establishing brand voice, or documenting ICP.
What you get
Produces a single marketing context document with positioning, ICP, personas, competitors, and customer language.
- Marketing context document
- ICP definition
- Buyer personas
By the numbers
- Uses the JTBD Four Forces switching-dynamics model
- Documents direct, secondary, and indirect competitors
Files
Marketing Context
The foundational context document that every marketing skill reads before starting. Captures positioning, ICP, competitive landscape, brand voice, and customer language in one place.
---
Table of Contents
- Keywords
- Quick Start
- Core Workflows
- Context Sections
- Customer Research Methodology
- Competitive Analysis Framework
- Switching Dynamics (JTBD Four Forces)
- Context Maintenance
- Best Practices
- Integration Points
---
Keywords
marketing context, brand voice, target audience, ICP, ideal customer profile, positioning, customer insights, competitive analysis, market research, customer language, brand personality, buyer persona, product marketing, go-to-market, messaging framework, competitive landscape, objection handling, proof points, switching dynamics, value proposition
---
Quick Start
Auto-Draft from Codebase
1. Study the repository: README, landing pages, marketing copy, about pages, docs 2. Draft a V1 context document based on what exists 3. Present the draft and ask: "What needs correcting? What is missing?" 4. Iterate through corrections until the document is accurate
Guided Interview
1. Walk through each section conversationally, one at a time 2. Ask focused questions (not all at once) 3. Capture exact customer language, not polished summaries 4. Validate each section before moving to the next
Update Existing Context
1. Read the current context document 2. Summarize what is captured 3. Ask which sections need updating 4. Make targeted updates while preserving accurate sections
---
Core Workflows
Workflow 1: Build Context from Scratch
Step 1: Gather Product Foundation
## Product Overview
- One-line description: [What it is in one sentence]
- What it does: [2-3 sentences explaining the product]
- Product category: [The "shelf" — how customers search for you]
- Product type: [SaaS / Marketplace / E-commerce / Service / Platform]
- Business model: [Subscription / Freemium / Usage-based / One-time]
- Pricing: [Starting price / tier structure]
- Stage: [Pre-launch / Early / Growth / Scale / Mature]Step 2: Define Target Audience
## Target Audience
- Target company type: [Industry, size, stage, geography]
- Target decision-makers: [Roles, departments, seniority levels]
- Primary use case: [The main problem you solve]
- Jobs to be done (3-5):
1. [Job]: [What they hire your product to do]
2. [Job]: [What they hire your product to do]
3. [Job]: [What they hire your product to do]
- Specific scenarios: [2-3 situations where they need you most]Step 3: Build Buyer Personas
For each stakeholder involved in the buying decision:
## Persona: [Role Name]
- Title: [Job title]
- Role in purchase: [User / Champion / Decision Maker / Financial Buyer / Technical Influencer]
- What they care about: [Their top 3 priorities]
- Their challenge: [Specific problem related to your product]
- Value you promise them: [What you deliver to this persona]
- Language they use: [Exact phrases they use to describe their problem]
- Where they research: [Channels, communities, publications they trust]Step 4: Document Problems and Pain Points
## Problems & Pain Points
- Core challenge: [What customers face before finding you]
- Why current solutions fail: [Specific shortcomings of alternatives]
- Cost of the problem:
- Time cost: [Hours/week wasted]
- Financial cost: [Money lost or spent inefficiently]
- Opportunity cost: [What they cannot do while dealing with this]
- Emotional tension: [Stress, fear, frustration, doubt they experience]Step 5: Map Competitive Landscape
## Competitive Landscape
### Direct Competitors (same solution, same problem)
| Competitor | Positioning | Weakness for Our ICP |
|-----------|------------|---------------------|
| [Name] | [How they position] | [Where they fall short] |
### Secondary Competitors (different solution, same problem)
| Competitor | Their Approach | Why Ours is Better |
|-----------|---------------|-------------------|
| [Name] | [Their method] | [Our advantage] |
### Indirect Competitors (do nothing, spreadsheets, manual process)
| Alternative | Why Customers Use It | Why They Should Switch |
|------------|---------------------|---------------------|
| [Name] | [Inertia reason] | [Switching benefit] |Step 6: Define Differentiation
## Differentiation
- Key differentiators (3-5):
1. [Capability]: [What we do that alternatives cannot]
2. [Capability]: [What we do that alternatives cannot]
3. [Capability]: [What we do that alternatives cannot]
- How we solve it differently: [Our unique approach or mechanism]
- Why that matters: [Benefit of our approach vs. alternatives]
- Why customers choose us: [Top 3 reasons from actual customer feedback]Step 7: Capture Objections and Anti-Personas
## Objections
| Objection | Frequency | Response |
|-----------|-----------|----------|
| "[Objection 1]" | Common | [How to address it] |
| "[Objection 2]" | Occasional | [How to address it] |
| "[Objection 3]" | Rare but important | [How to address it] |
## Anti-Personas (Who is NOT a Good Fit)
- [Type]: [Why they should not buy]
- [Type]: [Why they should not buy]Step 8: Document Customer Language
## Customer Language (Verbatim)
- How they describe the problem:
- "[Exact quote from customer]"
- "[Exact quote from customer]"
- How they describe our solution:
- "[Exact quote from customer]"
- "[Exact quote from customer]"
- Words TO use: [List of customer-approved terms]
- Words to AVOID: [Terms that confuse or alienate]
- Glossary: [Product-specific terms with definitions]Step 9: Establish Brand Voice
## Brand Voice
- Tone: [Professional / Casual / Playful / Authoritative]
- Communication style: [Direct / Conversational / Technical / Storytelling]
- Personality (3-5 adjectives): [e.g., Confident, Clear, Warm]
- Voice DOs: [What we always do in writing]
- Voice DON'Ts: [What we never do in writing]
- Example paragraph: [A paragraph that perfectly captures our voice]Step 10: Compile Proof Points
## Proof Points
- Key metrics: [Numbers we cite regularly]
- Notable customers: [Logos we have permission to use]
- Testimonial snippets:
- "[Quote]" — [Name], [Title] at [Company]
- "[Quote]" — [Name], [Title] at [Company]
- Awards and recognition: [Current, with year]
- Certifications: [Active compliance certifications]Step 11: Content and SEO Context
## Content & SEO Context
- Target keywords by cluster:
- Cluster 1: [keyword 1], [keyword 2], [keyword 3]
- Cluster 2: [keyword 1], [keyword 2], [keyword 3]
- Writing examples (best-performing pieces):
- [URL 1]: [Why it works well]
- [URL 2]: [Why it works well]
- Content tone: [Educational / Authoritative / Conversational]
- Preferred content length: [Short-form / Long-form / Mix]Step 12: Define Goals
## Goals
- Primary business goal: [What success looks like]
- Key conversion action: [What you want people to do]
- Current metrics: [Baseline numbers if available]
- Target metrics: [What you are working toward]---
Customer Research Methodology
Research Sources Ranked by Quality
| Source | Quality | What You Get | Time Required |
|---|---|---|---|
| Customer interviews (6-10) | Highest | Deep understanding of language, pain, decision process | 6-10 hours |
| Sales call recordings | High | Pre-purchase questions, objections, language | 2-4 hours |
| Support ticket analysis | High | Post-purchase confusion, unmet expectations | 1-2 hours |
| Product reviews (yours + competitors) | High | Candid praise and complaints | 1-2 hours |
| Customer surveys | Medium-High | Quantitative validation of qualitative findings | 2-3 hours |
| Community forums | Medium | Questions, debates, misconceptions | 1-2 hours |
| Competitor content analysis | Medium | Positioning gaps, messaging angles | 2-3 hours |
| Social listening | Medium | Trending topics, sentiment, language | 1 hour |
| Analytics data | Medium | Behavioral patterns, not motivations | 1 hour |
Interview Question Framework
Opening (establish context):
- "Walk me through how you handled [problem area] before using our product."
- "What was the moment you decided to look for a solution?"
Problem exploration:
- "What was the hardest part about [problem area]?"
- "What did you try before finding us?"
- "What did those alternatives get wrong?"
Decision process:
- "What made you choose us over the alternatives?"
- "What almost stopped you from signing up?"
- "Who else was involved in the decision?"
Language capture:
- "How would you explain what we do to a colleague?"
- "If you were recommending us, what would you say?"
Outcome validation:
- "What has changed since you started using us?"
- "Can you put a number on the impact?"
---
Competitive Analysis Framework
Three-Layer Analysis
Layer 1: Positioning
- How do they describe themselves? (Tagline, hero copy, meta description)
- What category do they claim? (The "shelf" they put themselves on)
- Who do they target? (ICP signals from their copy, pricing, case studies)
Layer 2: Messaging
- What benefits do they lead with?
- What proof points do they emphasize?
- What objections do they proactively address?
- What is conspicuously absent from their messaging?
Layer 3: Execution
- Content: What topics do they cover? What formats? What frequency?
- Channels: Where are they active? (SEO, social, paid, events)
- Social proof: Who are their reference customers?
- Pricing: How are they positioned on price?
Competitive Positioning Map
Premium
|
Enterprise | Innovator
(Salesforce) | (Your positioning?)
|
Simple ———————————+——————————— Complex
|
Budget | Technical
(Competitor B)| (Competitor C)
|
Affordable---
Switching Dynamics (JTBD Four Forces)
Understanding why customers switch (or do not) is critical for messaging:
The Four Forces
PUSH ————————————> <———————————— HABIT
(Frustration with (Comfort with
current solution) current approach)
PULL ————————————> <———————————— ANXIETY
(Attraction to (Fear about
your product) switching)Push (maximize in messaging):
- What frustrations drive them away from the current solution?
- What is the breaking point that triggers the search?
Pull (amplify in messaging):
- What attracts them to your product specifically?
- What is the "aha moment" they imagine?
Habit (address in messaging):
- What keeps them stuck with the current approach?
- What switching costs (real and perceived) exist?
Anxiety (reduce in messaging):
- What worries them about switching?
- What could go wrong during the transition?
- How do you make switching feel safe?
---
Context Maintenance
Freshness Rules
| Section | Review Frequency | Staleness Signal |
|---|---|---|
| Product overview | When features change | New features not reflected |
| Target audience | Quarterly | Win/loss data shows new segments |
| Competitive landscape | Monthly | New competitors emerging, positioning shifts |
| Customer language | Quarterly | New patterns in sales calls and reviews |
| Proof points | Monthly | New case studies, metrics, logos available |
| Brand voice | Semi-annually | Brand evolution or rebranding |
| Goals | Quarterly | Business priorities shift |
Update Triggers
Flag a context review when:
- A major product launch changes positioning
- Win rate shifts significantly (new objections emerging)
- A new competitor enters the market
- Customer language patterns change (new terminology)
- The ICP shifts (moving upmarket, new verticals)
- Proof points become outdated (old metrics, former customer logos)
---
Best Practices
1. Be specific, not polished — "I wish I knew this before we migrated" is more useful than "Customers value our migration support." Capture exact words.
2. Validate with real customers — Every positioning claim should be traceable to customer feedback. If customers do not say it, it might not be true.
3. Update incrementally — Do not wait for a full overhaul. Update individual sections as new information becomes available.
4. Include anti-personas — Knowing who is NOT a good fit prevents wasted marketing spend on the wrong audience.
5. Capture switching dynamics — Understanding push/pull/habit/anxiety produces better messaging than listing features.
6. Keep it usable — A 50-page context document nobody reads is worse than a 5-page one everyone references. Be concise.
7. Document customer language verbatim — Do not paraphrase. The exact words customers use should appear in your copy.
8. Link to proof — Every claim should reference its source (customer interview, survey, case study, metric).
9. Share across teams — Marketing context should be accessible to sales, product, and customer success. Shared language improves alignment.
10. Review quarterly minimum — Set a calendar reminder. Stale context produces stale messaging.
---
Integration Points
- Copywriting — Reads brand voice and customer language from this context for page copy.
- Content Strategy — Reads target keywords, personas, and competitive landscape for topic planning.
- Ad Creative — Reads ICP, value proposition, and proof points for ad messaging.
- Cold Email — Reads ICP, pain points, and customer language for outreach personalization.
- Marketing Ops — Routes marketing questions using context as the foundation.
- Social Content — Reads brand voice and audience details for platform-specific content.
- Brand Guidelines — Aligns brand voice and personality between context and visual standards.
- Paid Ads — Reads audience targeting details and value proposition for campaign setup.
---
Troubleshooting
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Marketing copy sounds generic across all channels | Context document missing customer language section with verbatim quotes | Conduct 6-10 customer interviews; capture exact phrases used to describe problem and solution |
| Sales and marketing using different messaging | Context document exists but not shared cross-functionally, or multiple conflicting versions | Consolidate into single source of truth; share with sales, product, and CS; version-control updates |
| ICP keeps expanding until it includes everyone | No anti-persona defined; pressure to broaden targeting | Document who is NOT a good fit and why; validate ICP against top 20% customers by LTV |
| Competitive positioning feels reactive | Landscape section only updated after losing deals, not proactively | Set monthly competitive review cadence; monitor competitor websites, pricing, and job postings |
| Context document becomes stale within 2 months | No update triggers or review schedule defined | Assign section owners; set quarterly review calendar; flag automatic updates on product launches or ICP shifts |
| New team members cannot find or understand the context | Document too long (50+ pages) or buried in wiki structure | Keep context under 10 pages; use templates with clear headers; include in onboarding checklist |
---
Success Criteria
- Context completeness score above 80% on context_completeness_checker.py (all 12 sections present with minimum depth)
- Every positioning claim traceable to specific customer feedback or data source
- Customer language section contains 10+ verbatim quotes (not paraphrased summaries)
- Context document reviewed and updated at least quarterly, with change log
- 100% of marketing skills reference context before starting work
- ICP definition validated against actual customer data: A-fit customers have lowest churn and fastest close
- Anti-personas defined with clear exclusion criteria to prevent wasted marketing spend
---
Scope & Limitations
In Scope: Product positioning documentation, ICP definition and validation, buyer persona creation, competitive analysis framework, customer language capture, brand voice establishment, proof point compilation, objection handling, switching dynamics (JTBD Four Forces), content and SEO context, context maintenance and freshness management.
Out of Scope: Brand visual identity (see brand-guidelines skill), marketing execution and campaign management (see marketing-ops skill), product strategy and roadmap (see product-team skills), market sizing and TAM analysis (see c-level-advisor skills).
Limitations: Marketing context is only as accurate as the inputs — garbage in, garbage out. Context derived solely from internal assumptions (without customer interviews) will have blind spots. Competitive analysis is point-in-time; markets shift and require continuous monitoring.
---
Scripts
| Script | Purpose | Usage |
|---|---|---|
scripts/icp_fit_scorer.py | Score prospects against ICP criteria with A/B/C/D grading | python scripts/icp_fit_scorer.py prospects.json --icp icp_config.json --demo |
scripts/competitive_landscape_mapper.py | Map competitive positioning, features, pricing, and identify gaps | python scripts/competitive_landscape_mapper.py competitors.json --demo |
scripts/context_completeness_checker.py | Audit marketing context document for missing or thin sections | python scripts/context_completeness_checker.py context.md --json |
#!/usr/bin/env python3
"""Competitive Landscape Mapper - Map and analyze competitive positioning.
Analyzes competitors across positioning, messaging, features, and pricing
to identify gaps and opportunities.
Usage:
python competitive_landscape_mapper.py competitors.json
python competitive_landscape_mapper.py competitors.json --json
python competitive_landscape_mapper.py --demo
"""
import argparse
import json
import sys
def analyze_landscape(data):
"""Analyze competitive landscape from competitor data."""
our_product = data.get("our_product", {})
competitors = data.get("competitors", [])
all_players = [our_product] + competitors if our_product else competitors
# Feature comparison
all_features = set()
for player in all_players:
all_features.update(player.get("features", []))
feature_matrix = {}
for feature in sorted(all_features):
feature_matrix[feature] = {}
for player in all_players:
name = player.get("name", "Unknown")
feature_matrix[feature][name] = feature in player.get("features", [])
# Unique features per player
unique_features = {}
for player in all_players:
name = player.get("name", "Unknown")
player_features = set(player.get("features", []))
other_features = set()
for other in all_players:
if other.get("name") != name:
other_features.update(other.get("features", []))
unique = player_features - other_features
unique_features[name] = list(unique)
# Feature coverage
our_features = set(our_product.get("features", [])) if our_product else set()
feature_gaps = []
for comp in competitors:
comp_features = set(comp.get("features", []))
gaps = comp_features - our_features
if gaps:
feature_gaps.append({
"competitor": comp.get("name", "Unknown"),
"features_they_have": list(gaps),
"count": len(gaps),
})
# Pricing analysis
pricing = []
for player in all_players:
price = player.get("starting_price")
if price is not None:
pricing.append({
"name": player.get("name", "Unknown"),
"starting_price": price,
"pricing_model": player.get("pricing_model", "unknown"),
})
pricing.sort(key=lambda x: x["starting_price"])
# Positioning analysis
positioning = []
all_categories = set()
all_audiences = set()
for player in all_players:
cat = player.get("category", "unknown")
audience = player.get("target_audience", "unknown")
all_categories.add(cat)
all_audiences.add(audience)
positioning.append({
"name": player.get("name", "Unknown"),
"category": cat,
"target_audience": audience,
"key_differentiator": player.get("key_differentiator", ""),
"tagline": player.get("tagline", ""),
})
# Competitive tiers
tiers = {"direct": [], "adjacent": [], "status_quo": []}
for comp in competitors:
tier = comp.get("tier", "direct")
tiers[tier].append(comp.get("name", "Unknown"))
# SWOT per competitor
competitor_analysis = []
for comp in competitors:
strengths = comp.get("strengths", [])
weaknesses = comp.get("weaknesses", [])
competitor_analysis.append({
"name": comp.get("name", "Unknown"),
"tier": comp.get("tier", "direct"),
"strengths": strengths,
"weaknesses": weaknesses,
"feature_count": len(comp.get("features", [])),
"unique_features": unique_features.get(comp.get("name"), []),
})
# Opportunity gaps
opportunities = []
if len(all_categories) > 1:
opportunities.append("Category positioning is fragmented - opportunity to own a specific category.")
if feature_gaps:
top_gaps = sorted(feature_gaps, key=lambda x: x["count"], reverse=True)
for gap in top_gaps[:3]:
opportunities.append(f"{gap['competitor']} has {gap['count']} features we lack: {', '.join(gap['features_they_have'][:3])}")
our_unique = unique_features.get(our_product.get("name", ""), [])
if our_unique:
opportunities.append(f"We uniquely offer: {', '.join(our_unique[:5])}. Emphasize in positioning.")
return {
"summary": {
"total_competitors": len(competitors),
"total_features_in_market": len(all_features),
"our_feature_count": len(our_features),
"categories_in_market": list(all_categories),
"audiences_targeted": list(all_audiences),
},
"tiers": tiers,
"positioning": positioning,
"pricing": pricing,
"feature_gaps": sorted(feature_gaps, key=lambda x: x["count"], reverse=True),
"unique_features": unique_features,
"competitor_analysis": competitor_analysis,
"opportunities": opportunities,
}
def get_demo_data():
return {
"our_product": {
"name": "OurProduct",
"category": "analytics platform",
"target_audience": "product managers",
"features": ["funnel analysis", "cohort analysis", "real-time dashboard", "custom events", "api access", "slack integration"],
"starting_price": 99,
"pricing_model": "per_seat",
"key_differentiator": "No-code analytics for PMs",
"tagline": "Analytics without engineering",
},
"competitors": [
{"name": "Amplitude", "tier": "direct", "category": "product analytics", "target_audience": "product teams", "features": ["funnel analysis", "cohort analysis", "behavioral analytics", "experimentation", "cdp"], "starting_price": 0, "pricing_model": "usage", "strengths": ["market leader", "deep analytics"], "weaknesses": ["complex setup", "expensive at scale"]},
{"name": "Mixpanel", "tier": "direct", "category": "product analytics", "target_audience": "product teams", "features": ["funnel analysis", "cohort analysis", "a/b testing", "messaging"], "starting_price": 0, "pricing_model": "usage", "strengths": ["easy to use", "good free tier"], "weaknesses": ["limited enterprise features", "data governance"]},
{"name": "Spreadsheets", "tier": "status_quo", "category": "manual analysis", "target_audience": "everyone", "features": ["custom formulas", "charts"], "starting_price": 0, "pricing_model": "free", "strengths": ["familiar", "flexible"], "weaknesses": ["manual", "error-prone", "not real-time"]},
],
}
def format_report(analysis):
"""Format human-readable report."""
lines = []
lines.append("=" * 65)
lines.append("COMPETITIVE LANDSCAPE MAP")
lines.append("=" * 65)
s = analysis["summary"]
lines.append(f"Competitors: {s['total_competitors']}")
lines.append(f"Features in market: {s['total_features_in_market']}")
lines.append(f"Our features: {s['our_feature_count']}")
lines.append("")
# Tiers
lines.append("--- COMPETITIVE TIERS ---")
for tier, names in analysis["tiers"].items():
if names:
lines.append(f" {tier.title()}: {', '.join(names)}")
lines.append("")
# Positioning
lines.append("--- POSITIONING ---")
for p in analysis["positioning"]:
lines.append(f" {p['name']}: {p['category']} for {p['target_audience']}")
if p.get("key_differentiator"):
lines.append(f" Differentiator: {p['key_differentiator']}")
lines.append("")
# Pricing
if analysis["pricing"]:
lines.append("--- PRICING ---")
for p in analysis["pricing"]:
lines.append(f" {p['name']}: ${p['starting_price']}/mo ({p['pricing_model']})")
lines.append("")
# Feature gaps
if analysis["feature_gaps"]:
lines.append("--- FEATURE GAPS ---")
for gap in analysis["feature_gaps"][:5]:
lines.append(f" {gap['competitor']} has: {', '.join(gap['features_they_have'][:5])}")
lines.append("")
# Opportunities
if analysis["opportunities"]:
lines.append("--- OPPORTUNITIES ---")
for opp in analysis["opportunities"]:
lines.append(f" * {opp}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Map and analyze competitive positioning")
parser.add_argument("input", nargs="?", help="JSON file with competitive data")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
parser.add_argument("--demo", action="store_true", help="Run with demo data")
args = parser.parse_args()
if args.demo:
data = get_demo_data()
elif args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
analysis = analyze_landscape(data)
if args.json_output:
print(json.dumps(analysis, indent=2))
else:
print(format_report(analysis))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Context Completeness Checker - Audit marketing context document for gaps.
Checks a marketing context document for completeness across all required
sections, flags missing or thin sections, and scores overall readiness.
Usage:
python context_completeness_checker.py context.md
python context_completeness_checker.py context.json --json
"""
import argparse
import json
import re
import sys
REQUIRED_SECTIONS = {
"product_overview": {
"weight": 10,
"required_fields": ["description", "category", "product_type", "business_model", "pricing"],
"min_words": 50,
},
"target_audience": {
"weight": 10,
"required_fields": ["company_type", "decision_makers", "primary_use_case", "jobs_to_be_done"],
"min_words": 75,
},
"buyer_personas": {
"weight": 8,
"required_fields": ["title", "role_in_purchase", "what_they_care_about", "language_they_use"],
"min_words": 100,
},
"problems_pain_points": {
"weight": 9,
"required_fields": ["core_challenge", "why_current_solutions_fail", "cost_of_problem"],
"min_words": 75,
},
"competitive_landscape": {
"weight": 8,
"required_fields": ["direct_competitors", "secondary_competitors"],
"min_words": 75,
},
"differentiation": {
"weight": 9,
"required_fields": ["key_differentiators", "how_we_solve_differently", "why_customers_choose_us"],
"min_words": 50,
},
"objections": {
"weight": 7,
"required_fields": ["objection", "response"],
"min_words": 50,
},
"customer_language": {
"weight": 8,
"required_fields": ["how_they_describe_problem", "how_they_describe_solution"],
"min_words": 50,
},
"brand_voice": {
"weight": 7,
"required_fields": ["tone", "communication_style", "personality"],
"min_words": 40,
},
"proof_points": {
"weight": 7,
"required_fields": ["key_metrics", "testimonials"],
"min_words": 40,
},
"content_seo_context": {
"weight": 5,
"required_fields": ["target_keywords"],
"min_words": 30,
},
"goals": {
"weight": 5,
"required_fields": ["primary_goal", "key_conversion_action"],
"min_words": 30,
},
}
# Patterns to detect sections in markdown
SECTION_PATTERNS = {
"product_overview": [r"product\s+overview", r"what\s+(it|we)\s+(is|do|are)"],
"target_audience": [r"target\s+audience", r"ideal\s+customer", r"icp"],
"buyer_personas": [r"persona", r"buyer\s+profile"],
"problems_pain_points": [r"problems?", r"pain\s+points?", r"challenges?"],
"competitive_landscape": [r"competitive?\s+landscape", r"competitors?"],
"differentiation": [r"differenti", r"unique\s+(value|approach)"],
"objections": [r"objections?", r"anti.?persona"],
"customer_language": [r"customer\s+language", r"verbatim"],
"brand_voice": [r"brand\s+voice", r"tone\s+(and|&)\s+voice"],
"proof_points": [r"proof\s+points?", r"testimonial", r"social\s+proof"],
"content_seo_context": [r"content\s+(&|and)\s+seo", r"keywords?"],
"goals": [r"goals?", r"objectives?", r"kpis?"],
}
def check_markdown(filepath):
"""Check a markdown context document for completeness."""
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
sections_found = {}
content_lower = content.lower()
for section, patterns in SECTION_PATTERNS.items():
found = False
section_content = ""
for pattern in patterns:
match = re.search(pattern, content_lower)
if match:
found = True
# Extract content after the heading until next heading
pos = match.start()
next_heading = re.search(r"\n#{1,3}\s", content[pos + 10:])
if next_heading:
section_content = content[pos:pos + 10 + next_heading.start()]
else:
section_content = content[pos:]
break
word_count = len(section_content.split()) if section_content else 0
min_words = REQUIRED_SECTIONS[section]["min_words"]
sections_found[section] = {
"found": found,
"word_count": word_count,
"meets_minimum": word_count >= min_words,
"min_words": min_words,
}
return _compile_results(sections_found)
def check_json(filepath):
"""Check a JSON context document for completeness."""
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
sections_found = {}
data_lower = {k.lower().replace(" ", "_"): v for k, v in data.items()}
for section, config in REQUIRED_SECTIONS.items():
# Check if section key exists
section_data = data_lower.get(section, data_lower.get(section.replace("_", ""), None))
found = section_data is not None
if found and isinstance(section_data, dict):
word_count = sum(len(str(v).split()) for v in section_data.values())
elif found and isinstance(section_data, str):
word_count = len(section_data.split())
elif found and isinstance(section_data, list):
word_count = sum(len(str(item).split()) for item in section_data)
else:
word_count = 0
min_words = config["min_words"]
sections_found[section] = {
"found": found,
"word_count": word_count,
"meets_minimum": word_count >= min_words,
"min_words": min_words,
}
return _compile_results(sections_found)
def _compile_results(sections_found):
"""Compile analysis results from section findings."""
total_weight = sum(REQUIRED_SECTIONS[s]["weight"] for s in REQUIRED_SECTIONS)
earned_weight = 0
missing = []
thin = []
complete = []
for section, result in sections_found.items():
weight = REQUIRED_SECTIONS[section]["weight"]
if not result["found"]:
missing.append({"section": section, "weight": weight})
elif not result["meets_minimum"]:
earned_weight += weight * 0.5
thin.append({
"section": section,
"word_count": result["word_count"],
"min_words": result["min_words"],
"weight": weight,
})
else:
earned_weight += weight
complete.append({"section": section, "weight": weight})
score = (earned_weight / max(total_weight, 1)) * 100
# Readiness assessment
if score >= 80 and not missing:
readiness = "READY"
message = "Context is comprehensive. Ready for marketing execution."
elif score >= 60:
readiness = "MOSTLY READY"
message = "Core context exists. Fill gaps before major campaigns."
elif score >= 40:
readiness = "PARTIAL"
message = "Significant gaps remain. Complete critical sections first."
else:
readiness = "NOT READY"
message = "Major sections missing. Build context before marketing work."
# Priority order for missing sections (by weight)
priority_order = sorted(missing + thin, key=lambda x: x["weight"], reverse=True)
return {
"score": round(score, 1),
"readiness": readiness,
"message": message,
"sections": sections_found,
"complete": complete,
"thin": thin,
"missing": missing,
"priority_to_fill": [p["section"] for p in priority_order],
"stats": {
"total_sections": len(REQUIRED_SECTIONS),
"complete": len(complete),
"thin": len(thin),
"missing": len(missing),
},
}
def format_report(analysis):
"""Format human-readable report."""
lines = []
lines.append("=" * 60)
lines.append("MARKETING CONTEXT COMPLETENESS CHECK")
lines.append("=" * 60)
lines.append(f"Score: {analysis['score']:.0f}/100")
lines.append(f"Readiness: {analysis['readiness']}")
lines.append(f"Assessment: {analysis['message']}")
lines.append(f"Sections: {analysis['stats']['complete']}/{analysis['stats']['total_sections']} complete, "
f"{analysis['stats']['thin']} thin, {analysis['stats']['missing']} missing")
lines.append("")
# Section status
lines.append("--- SECTION STATUS ---")
for section, data in analysis["sections"].items():
label = section.replace("_", " ").title()
if not data["found"]:
lines.append(f" [MISSING] {label}")
elif not data["meets_minimum"]:
lines.append(f" [THIN] {label} ({data['word_count']}/{data['min_words']} words)")
else:
lines.append(f" [OK] {label} ({data['word_count']} words)")
lines.append("")
# Priority
if analysis["priority_to_fill"]:
lines.append("--- FILL PRIORITY ---")
for i, section in enumerate(analysis["priority_to_fill"], 1):
lines.append(f" {i}. {section.replace('_', ' ').title()}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Audit marketing context document for gaps")
parser.add_argument("input", help="Markdown (.md) or JSON (.json) context file")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
args = parser.parse_args()
try:
if args.input.endswith(".json"):
analysis = check_json(args.input)
else:
analysis = check_markdown(args.input)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
if args.json_output:
print(json.dumps(analysis, indent=2))
else:
print(format_report(analysis))
sys.exit(0 if analysis["readiness"] in ("READY", "MOSTLY READY") else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""ICP Fit Scorer - Score prospects against Ideal Customer Profile criteria.
Evaluates prospects on firmographic, technographic, and behavioral signals
to produce an A/B/C/D fit score.
Usage:
python icp_fit_scorer.py prospects.json --icp icp_config.json
python icp_fit_scorer.py prospects.json --json
python icp_fit_scorer.py --demo
"""
import argparse
import json
import sys
DEFAULT_ICP = {
"firmographics": {
"employees": {"min": 50, "max": 5000, "weight": 20},
"revenue": {"min": 5000000, "max": 500000000, "weight": 15},
"industry": {"values": ["saas", "technology", "fintech", "services"], "weight": 20},
"geography": {"values": ["us", "uk", "dach", "canada"], "weight": 10},
},
"technographics": {
"tech_stack": {"values": ["salesforce", "hubspot", "slack", "jira"], "weight": 10},
"maturity": {"values": ["growth", "scale"], "weight": 5},
},
"behavioral": {
"funding_stage": {"values": ["series_a", "series_b", "series_c", "growth"], "weight": 10},
"pain_level": {"min": 3, "max": 5, "weight": 10},
},
}
GRADE_THRESHOLDS = {
"A": 80,
"B": 60,
"C": 40,
"D": 0,
}
def score_prospect(prospect, icp_config=None):
"""Score a single prospect against ICP criteria."""
if icp_config is None:
icp_config = DEFAULT_ICP
total_score = 0
total_weight = 0
criteria_results = []
for category, criteria in icp_config.items():
for criterion, config in criteria.items():
weight = config.get("weight", 10)
total_weight += weight
prospect_value = prospect.get(criterion)
if prospect_value is None:
criteria_results.append({
"criterion": criterion,
"category": category,
"status": "missing",
"score": 0,
"weight": weight,
})
continue
score = 0
status = "no_match"
if "min" in config and "max" in config:
# Range check
val = float(prospect_value) if not isinstance(prospect_value, (int, float)) else prospect_value
if config["min"] <= val <= config["max"]:
# Score based on position in range (center = best)
mid = (config["min"] + config["max"]) / 2
range_size = config["max"] - config["min"]
distance = abs(val - mid) / (range_size / 2)
score = max(0, (1 - distance * 0.3)) * weight
status = "match"
elif val < config["min"]:
# Below range: partial credit
ratio = val / config["min"]
score = max(0, ratio * 0.5) * weight
status = "partial_below"
else:
# Above range: partial credit
ratio = config["max"] / val
score = max(0, ratio * 0.5) * weight
status = "partial_above"
elif "values" in config:
# List match
if isinstance(prospect_value, list):
matches = len(set(v.lower() for v in prospect_value) & set(v.lower() for v in config["values"]))
total_possible = len(config["values"])
score = (matches / max(total_possible, 1)) * weight
status = "match" if matches > 0 else "no_match"
else:
val_lower = str(prospect_value).lower()
if val_lower in [v.lower() for v in config["values"]]:
score = weight
status = "match"
total_score += score
criteria_results.append({
"criterion": criterion,
"category": category,
"value": prospect_value,
"score": round(score, 1),
"max_score": weight,
"status": status,
})
# Calculate percentage and grade
percentage = (total_score / max(total_weight, 1)) * 100
grade = "D"
for g, threshold in sorted(GRADE_THRESHOLDS.items()):
if percentage >= threshold:
grade = g
return {
"prospect": prospect.get("name", prospect.get("company", "Unknown")),
"score": round(total_score, 1),
"max_score": total_weight,
"percentage": round(percentage, 1),
"grade": grade,
"criteria": criteria_results,
"matched": len([c for c in criteria_results if c["status"] == "match"]),
"missing": len([c for c in criteria_results if c["status"] == "missing"]),
}
def get_demo_data():
return {
"prospects": [
{"name": "TechStart Inc", "employees": 120, "revenue": 15000000, "industry": "saas", "geography": "us", "tech_stack": ["salesforce", "slack"], "funding_stage": "series_a", "pain_level": 4},
{"name": "BigCorp Ltd", "employees": 8000, "revenue": 800000000, "industry": "manufacturing", "geography": "uk", "tech_stack": ["sap"], "funding_stage": "public", "pain_level": 2},
{"name": "GrowthCo", "employees": 250, "revenue": 30000000, "industry": "fintech", "geography": "us", "tech_stack": ["hubspot", "jira", "slack"], "funding_stage": "series_b", "pain_level": 5},
{"name": "SmallBiz", "employees": 15, "revenue": 500000, "industry": "retail", "geography": "canada", "tech_stack": [], "funding_stage": "seed", "pain_level": 3},
],
}
def format_report(results):
"""Format human-readable report."""
lines = []
lines.append("=" * 65)
lines.append("ICP FIT SCORING REPORT")
lines.append("=" * 65)
# Summary table
lines.append(f"{'Prospect':<25} {'Score':>8} {'Grade':>6} {'Matched':>8}")
lines.append("-" * 50)
for r in sorted(results, key=lambda x: x["percentage"], reverse=True):
lines.append(f"{r['prospect']:<25} {r['percentage']:>7.0f}% {r['grade']:>6} {r['matched']:>5}/{r['matched'] + r['missing']}")
lines.append("")
# Detailed per prospect
for r in sorted(results, key=lambda x: x["percentage"], reverse=True):
lines.append(f"--- {r['prospect']} (Grade: {r['grade']}, {r['percentage']:.0f}%) ---")
for c in r["criteria"]:
status_icon = {"match": "+", "partial_below": "~", "partial_above": "~", "no_match": "-", "missing": "?"}
icon = status_icon.get(c["status"], "?")
val = c.get("value", "N/A")
lines.append(f" [{icon}] {c['criterion']:<20} {str(val):<20} ({c['score']:.0f}/{c['max_score']})")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Score prospects against ICP criteria")
parser.add_argument("input", nargs="?", help="JSON file with prospect data")
parser.add_argument("--icp", help="Custom ICP config JSON file")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
parser.add_argument("--demo", action="store_true", help="Run with demo data")
args = parser.parse_args()
icp_config = DEFAULT_ICP
if args.icp:
with open(args.icp, "r") as f:
icp_config = json.load(f)
if args.demo:
data = get_demo_data()
elif args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
prospects = data.get("prospects", data) if isinstance(data, dict) else data
results = [score_prospect(p, icp_config) for p in prospects]
if args.json_output:
print(json.dumps(results, indent=2))
else:
print(format_report(results))
if __name__ == "__main__":
main()
Related skills
FAQ
How can the context be built?
Either auto-drafted from the codebase (README, landing pages, docs) or built through a guided section-by-section interview.
Why does it matter?
It is the foundational document that every other marketing skill reads before starting.