UNPKG

skills-ws

Version:

87 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.

78 lines (60 loc) • 3.6 kB
## Contents - 6. Content Gap Analysis - Inputs - Runnable: keyword gap from CSV exports - Prioritization framework ## 6. Content Gap Analysis Find what competitors rank/get-cited for that you don't, then prioritize by winnability × value. This is an analyst workflow with real tools, not a black-box function. ### Inputs - **Your ranking keywords + positions** — Search Console (`search-console` skill) or Ahrefs/Semrush "Organic keywords". - **Competitor ranking keywords** — Ahrefs/Semrush "Site Explorer" per competitor; export to CSV. - **AI-answer gaps** — run the §3 query-class prompts for your money topics; log where a competitor is cited and you're absent. - **SERP feature gaps** — for your target terms, note which features (AIO, snippet, video, PAA) you're missing. ### Runnable: keyword gap from CSV exports This uses your own exported CSVs (no fictional API). Export "Organic keywords" for yourself and each competitor from your SEO tool, then: ```python #!/usr/bin/env python3 """Keyword gap analysis from Ahrefs/Semrush CSV exports. Usage: python gap.py ours.csv comp1.csv comp2.csv ... Each CSV must contain columns: Keyword, Volume, KD (Difficulty), Current position (theirs). Column names below match Ahrefs 'Organic keywords' export; adjust if using Semrush.""" import csv, sys KW, VOL, KD, POS = "Keyword", "Volume", "KD", "Current position" def load(path): with open(path, newline="", encoding="utf-8-sig") as f: return list(csv.DictReader(f)) ours = {r[KW].strip().lower() for r in load(sys.argv[1])} gaps = {} for comp_path in sys.argv[2:]: for r in load(comp_path): kw = r[KW].strip().lower() if kw in ours or not kw: continue try: pos = float(r.get(POS) or 999) except ValueError: pos = 999 if pos > 10: # only count where they actually rank continue vol = int((r.get(VOL) or "0").replace(",", "") or 0) kd = int((r.get(KD) or "100").replace(",", "") or 100) # winnability: high volume, low difficulty rises to the top score = round(vol / (kd + 1), 1) cur = gaps.get(kw) if not cur or score > cur["score"]: gaps[kw] = {"keyword": kw, "volume": vol, "kd": kd, "best_competitor_pos": pos, "score": score, "source": comp_path} for g in sorted(gaps.values(), key=lambda x: -x["score"])[:50]: print(f"{g['score']:>8} {g['keyword'][:45]:45} vol={g['volume']:<6} kd={g['kd']:<3} " f"theirpos={g['best_competitor_pos']:<4} ({g['source']})") ``` > The `score = volume / (kd+1)` heuristic is a *starting* sort, not gospel. Re-rank the top 50 by intent match and business value before committing — a KD-12 term with no commercial relevance loses to a KD-35 term that sells your product. ### Prioritization framework | Tier | Criteria | Action | |---|---|---| | **P1 (do now)** | Non-trivial demand · KD below your proven ceiling · clear intent you can satisfy · maps to a CTA · a competitor already ranks/cited | Brief this quarter | | **P2 (queue)** | Moderate demand/difficulty · mixed intent · tangential business fit | Next quarter / batch | | **P3 (later)** | Thin demand · KD above ceiling · unclear intent or weak business value | Park; revisit if authority grows | > Treat **AI-answer gaps** as their own P1 list: if you already rank but aren't *cited*, the fix is usually a §3 content edit (direct answer, original data, author signals), which is far cheaper than net-new content. ---