Communitygithub.com

Varnan-Tech/opendirectory

获取竞争对手的公开 GitHub 仓库 URL,通过 GitHub REST API 获取其 open issues,本地过滤噪音,将 issues 聚类为 6 个需求类别,计算每个 issue 和每个聚类的需求分数,并输出排序后的需求差距报告及 GTM 信息简报。

opendirectory 是什么?

该技能允许 AI 代理(如 Claude Code、Cursor 或 Codex)分析竞争对手的公开 GitHub 仓库,自动识别用户最迫切的需求。它会获取最多 200 个 open issues,通过本地 Python 脚本进行智能过滤,剔除机器人、零互动和噪音 issue,仅保留高质量信号。之后,技能利用 AI 将过滤后的 issues 聚类到六个预定义的需求类别中(如功能缺口、Bug 模式、用户体验投诉等),并为每个 issue 计算需求分数(基于表情反应和评论数),为每个聚类计算总需求分数。最后,它会生成一份按需求分数排序的报告,展示最关键的集群和具体 issue,以及一份 GTM 信息简报,其中包含可引用的定位角度和 outreach hook。这个技能对于产品经理、增长团队和希望在竞品基础上进行差异化创新的初创公司创始人极具价值,能够将原始的 GitHub 投诉转化为可操作的产品策略和营销信息。

兼容平台✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/Varnan-Tech/opendirectory/tree/main/skills/gh-issue-to-demand-signal

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

GitHub Issue 需求信号

获取竞争对手的公开 GitHub 仓库。获取它们的 open issues。本地过滤噪音。聚类为 6 个需求类别。根据真实互动评分。输出排序后的需求差距报告和 GTM 信息简报。


关键规则: 输出中的每个 issue 标题必须直接来自 GitHub API 响应。每个集群主题名称必须从该集群中的实际 issue 标题中衍生。如果噪音过滤后剩余少于 10 个 issue,则停止并告知用户——该仓库对于可靠的聚类来说太小。任何地方都不得有编造的 issue 内容。


常见错误

代理可能会……为什么这是错误的
不经过滤就将所有 200 个原始 issues 发送给 AI机器人 issue、PR 和零互动噪音会夸大集群计数并浪费上下文。应首先在本地过滤。
使用评论数作为主要需求信号评论包括维护者回复、离题讨论和垃圾信息。reactions["+1"] 是最清晰的买家信号。
在总结集群时改写 issue 标题改写会丢失买家的确切措辞,而这正是重点。应使用原样的 issue 标题。
在过滤后剩余少于 10 个 issue 时继续执行步骤 4 之后的操作少于 10 个 issue 意味着仓库太小或给出的 URL 错误。在稀疏数据上进行聚类会产生无意义的类别。
在分析中包含 pull requestGitHub Issues 端点也会返回 PR。应通过检查 issue 对象上是否不存在 pull_request 键来过滤。
在未检查所有 3 个条件的情况下将 issue 标记为被忽略的需求所有三个条件都必须为真:reactions >= 10,age >= 180 天,没有 planned/in-progress/roadmap 标签。缺少一个条件则 disqualify 该 issue。

步骤 1:设置检查

echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-未设置,未认证的速率限制适用(60 次请求/小时)}"

如果 GITHUB_TOKEN 未设置: 继续。告知用户:“GITHUB_TOKEN 未设置。未认证的速率限制为 60 次请求/小时——足够在达到限制前获取 2 次。如需重复使用,请在 github.com/settings/tokens 添加一个令牌(公共仓库不需要任何权限范围)。”


步骤 2:收集输入

您需要:

从输入中解析 owner 和 repo:

python3 << 'PYEOF'
import re, sys, os

raw = "REPO_INPUT_HERE"

# 规范化为 owner/repo
if raw.startswith("http"):
    m = re.search(r"github\.com/([^/]+)/([^/?\s]+)", raw)
    if not m:
        print("错误:无法解析 GitHub URL。预期格式:https://github.com/owner/repo")
        sys.exit(1)
    owner, repo = m.group(1), m.group(2).rstrip("/")
elif "/" in raw:
    parts = raw.strip().split("/")
    owner, repo = parts[0], parts[1]
else:
    print("错误:输入必须是 GitHub URL 或 owner/repo 缩写(例如 vercel/next.js)")
    sys.exit(1)

print(f"Owner: {owner}")
print(f"Repo: {repo}")

with open("/tmp/ghd-target.txt", "w") as f:
    f.write(f"{owner}/{repo}")
PYEOF

如果解析失败: 停止。询问:“请提供 GitHub 仓库的 URL (https://github.com/owner/repo) 或 owner/repo 缩写 (例如 vercel/next.js)。”


步骤 3:从 GitHub REST API 获取 Issues

获取最多 200 个 issue(每页 100 个,共 2 页)。在第一次获取后检查速率限制。

python3 << 'PYEOF'
import json, urllib.request, os, sys
from datetime import datetime, timezone

target = open("/tmp/ghd-target.txt").read().strip()
owner_repo = target
token = os.environ.get("GITHUB_TOKEN", "")

headers = {"Accept": "application/vnd.github+json", "User-Agent": "gh-issue-demand-signal/1.0"}
if token:
    headers["Authorization"] = f"Bearer {token}"

all_issues = []
rate_limit_hit = False

for page in [1, 2]:
    url = f"https://api.github.com/repos/{owner_repo}/issues?state=open&per_page=100&page={page}"
    req = urllib.request.Request(url, headers=headers)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            # 在第一页后检查速率限制
            if page == 1:
                remaining = int(resp.headers.get("X-RateLimit-Remaining", 999))
                reset_ts = resp.headers.get("X-RateLimit-Reset", "")
                if remaining == 0:
                    reset_str = datetime.fromtimestamp(int(reset_ts), tz=timezone.utc).strftime("%H:%M UTC") if reset_ts else "未知"
                    print(f"错误:GitHub 速率限制已耗尽。重置时间为 {reset_str}。")
                    print("请将 GITHUB_TOKEN 添加到您的 .env 文件中,以获得 5000 次请求/小时。详见 github.com/settings/tokens(不需要任何权限范围)。")
                    sys.exit(1)
                print(f"剩余速率限制:{remaining}")

                # 检查 404/403
                status = resp.status
                if status == 404:
                    print(f"错误:仓库 '{owner_repo}' 未找到。请检查 URL 或缩写。")
                    sys.exit(1)

            page_data = json.loads(resp.read())
            if not page_data:
                print(f"第 {page} 页:为空,停止。")
                break
            all_issues.extend(page_data)
            print(f"第 {page} 页:已获取 {len(page_data)} 个 issue")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(f"错误:仓库 '{owner_repo}' 未找到 (404)。请检查 URL 或缩写。")
        elif e.code == 403:
            print(f"错误:访问被拒绝 (403)。仓库可能为私有或已达到速率限制。")
        else:
            print(f"错误:GitHub API 返回 HTTP {e.code}")
        sys.exit(1)
    except Exception as e:
        print(f"错误:无法获取第 {page} 页:{e}")
        sys.exit(1)

print(f"已获取的原始 issue 总数:{len(all_issues)}")
json.dump(all_issues, open("/tmp/ghd-raw-issues.json", "w"), indent=2)
PYEOF

如果 GitHub 返回 404: 停止。告知用户:“仓库未找到。请检查 URL 或缩写后重试。没有认证和明确的仓库权限范围时,无法访问私有仓库。”

如果 GitHub 返回 403 并带有速率限制头信息: 停止。显示重置时间并告知用户添加 GITHUB_TOKEN。


步骤 4:本地预处理——过滤、评分、检测被忽略的需求

无需 API 调用。纯 Python。在对 AI 进行任何操作之前运行。

python3 << 'PYEOF'
import json, re
from datetime import datetime, timezone

raw = json.load(open("/tmp/ghd-raw-issues.json"))
target = open("/tmp/ghd-target.txt").read().strip()
now = datetime.now(tz=timezone.utc)

noise_patterns = re.compile(
    r"^(chore|deps|bump|renovate|dependabot|release|ci|build|revert)[\s:\[]",
    re.IGNORECASE
)
pr_title_patterns = re.compile(
    r"^(feat|fix|refactor|docs|test|style|perf|chore)(\(.+\))?:",
    re.IGNORECASE
)

filtered = []
noise_count = 0
noise_reasons = {}

for issue in raw:
    # 跳过 pull requests(GitHub Issues 端点也会返回 PR)
    if "pull_request" in issue:
        noise_count += 1
        noise_reasons["pull_request"] = noise_reasons.get("pull_request", 0) + 1
        continue

    title = issue.get("title", "")
    reactions = issue.get("reactions", {}).get("+1", 0)
    comments = issue.get("comments", 0)
    user_type = (issue.get("user") or {}).get("type", "User")

    # 跳过机器人编写的 issue
    if user_type == "Bot":
        noise_count += 1
        noise_reasons["bot_author"] = noise_reasons.get("bot_author", 0) + 1
        continue

    # 跳过机器人模式的标题
    if noise_patterns.match(title):
        noise_count += 1
        noise_reasons["bot_title"] = noise_reasons.get("bot_title", 0) + 1
        continue

    # 跳过作为 issue 的 PR 标题
    if pr_title_patterns.match(title):
        noise_count += 1
        noise_reasons["pr_as_issue"] = noise_reasons.get("pr_as_issue", 0) + 1
        continue

    # 跳过零信号的 issue
    if reactions == 0 and comments == 0:
        noise_count += 1
        noise_reasons["zero_signal"] = noise_reasons.get("zero_signal", 0) + 1
        continue

    # 计算需求分数
    demand_score = (reactions * 2) + (comments * 0.5)

    # 检测被忽略的需求
    created_at = issue.get("created_at", "")
    if created_at:
        created = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
        age_days = (now - created).days
    else:
        age_days = 0

    labels = [l.get("name", "").lower() for l in issue.get("labels", [])]
    has_planned_label = any(
        kw in label for label in labels
        for kw in ["in-progress", "planned", "roadmap", "wip", "in progress"]
    )

    ignored_demand = (
        reactions >= 10 and
        age_days >= 180 and
        not has_planned_label
    )

    filtered.append({
        "number": issue["number"],
        "title": title,
        "url": issue.get("html_url", f"https://github.com/{target}/issues/{issue['number']}"),
        "reactions_plus1": reactions,
        "comments": comments,
        "demand_score": demand_score,
        "age_days": age_days,
        "labels": labels,
        "ignored_demand": ignored_demand,
        "body_snippet": (issue.get("body") or "")[:300]
    })

# 按需求分数降序排序
filtered.sort(key=lambda x: x["demand_score"], reverse=True)

print(f"原始 issue 数:{len(raw)}")
print(f"噪音过滤数:{noise_count} ({', '.join(f'{k}: {v}' for k, v in noise_reasons.items())})")
print(f"待分析的 issue 数:{len(filtered)}")

if len(filtered) < 10:
    print(f"错误:过滤后仅剩下 {len(filtered)} 个 issue。此仓库的活跃 issue 数量太少,无法进行可靠的聚类。")
    print("请尝试更大的仓库或社区参与度更高的仓库。")
    import sys; sys.exit(1)

# 被忽略的需求摘要
ignored = [i for i in filtered if i["ignored_demand"]]
print(f"被忽略的需求 issue 数:{len(ignored)}")

json.dump(filtered, open("/tmp/ghd-filtered-issues.json", "w"), indent=2)
print("预处理完成。")
PYEOF

如果过滤后剩余少于 10 个 issue: 停止。准确告知用户找到了多少个 issue、过滤了多少个,以及为什么该仓库对于可靠的需求聚类来说太小。


步骤 5:聚类 Issues

打印过滤后的 issues 以供分析:

python3 << 'PYEOF'
import json
filtered = json.load(open("/tmp/ghd-filtered-issues.json"))
target = open("/tmp/ghd-target.txt").read().strip()

issue_list = filtered[:150]
print(f"仓库:{target}")
print(f"待聚类的 issue 数:{len(issue_list)}")
print()
for i in issue_list:
    labels_str = f" [{', '.join(i['labels'][:3])}]" if i['labels'] else ""
    print(f"#{i['number']} [分数:{round(i['demand_score'],1)} 反应:{i['reactions_plus1']}] {i['title']}{labels_str}")
PYEOF

将上面打印的每个 issue 分类到以下 6 个类别之一: feature_gap, bug_pattern, ux_complaint, performance, integration_missing, docs_missing

规则:

  • 将每个 issue 分类到恰好一个类别
  • 使用标题中用户的确切措辞提取一个句子的痛点描述——不要改写
  • 确定 5-8 个集群主题:捕获所有 issues 中主导投诉模式的短短语(3-6 个词)
  • 不要使用破折号。不要使用营销语言。

将您的分析写入 /tmp/ghd-clusters.json,结构如下:

{
  "classified_issues": [
    {"number": 123, "category": "feature_gap", "pain_statement": "用户需要尚不存在的 X"}
  ],
  "cluster_themes": [
    {"theme_name": "缺失导出选项", "category": "feature_gap", "issue_numbers": [123, 456, 789]}
  ],
  "category_counts": {"feature_gap": 5, "bug_pattern": 3, "ux_complaint": 4, "performance": 2, "integration_missing": 6, "docs_missing": 1}
}

写入文件后,确认:

python3 -c "
import json
d = json.load(open('/tmp/ghd-clusters.json'))
print(f'已分类:{len(d[\"classified_issues\"])} 个 issue, {len(d[\"cluster_themes\"])} 个主题')
print('类别:', d['category_counts'])
"

步骤 6:信息简报

计算每个聚类的总需求分数并打印前 3 名:

python3 << 'PYEOF'
import json

filtered = json.load(open("/tmp/ghd-filtered-issues.json"))
clusters = json.load(open("/tmp/ghd-clusters.json"))
target = open("/tmp/ghd-target.txt").read().strip()

demand_by_issue = {i["number"]: i["demand_score"] for i in filtered}
issue_titles = {i["number"]: i["title"] for i in filtered}
issue_reactions = {i["number"]: i["reactions_plus1"] for i in filtered}

enriched_themes = []
for theme in clusters.get("cluster_themes", []):
    issue_nums = theme.get("issue_numbers", [])
    total_demand = sum(demand_by_issue.get(n, 0) for n in issue_nums)
    top_issues = sorted(issue_nums, key=lambda n: demand_by_issue.get(n, 0), reverse=True)[:3]
    enriched_themes.append({
        "theme_name": theme["theme_name"],
        "category": theme["category"],
        "issue_count": len(issue_nums),
        "total_demand_score": round(total_demand, 1),
        "top_issues": [
            {"number": n, "title": issue_titles.get(n, ""), "reactions": issue_reactions.get(n, 0)}
            for n in top_issues
        ]
    })

enriched_themes.sort(key=lambda x: x["total_demand_score"], reverse=True)
json.dump(enriched_themes, open("/tmp/ghd-enriched-themes.json", "w"), indent=2)

print(f"信息简报的前 3 个聚类(仓库:{target}):")
for t in enriched_themes[:3]:
    print(f"\n  {t['theme_name']} ({t['category']}) -- 总需求:{t['total_demand_score']}")
    for ti in t["top_issues"]:
        print(f"    #{ti['number']}: \"{ti['title']}\" ({ti['reactions']} 个反应)")
PYEOF

根据上面打印的前 3 个聚类生成 GTM 信息简报。

规则:

  • 每个定位角度必须引用其来源的具体集群
  • 每个外展 hook 必须引用一个原样的 issue 标题,并用引号括起来
  • 标题必须包含数字或特定的命名痛点——不能使用通用语句
  • 不要使用破折号。禁止使用的词语:powerful, robust, seamless, innovative, game-changing, streamline, leverage, transform

将您的简报写入 /tmp/ghd-brief.json,结构如下:

...

[已截断以进行翻译]

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

Varnan-Tech/opendirectory

Use when the user asks to generate a blog cover image, thumbnail, or article header. Automatically uses modern typography, brand logos, and Google Search grounding to create beautiful 16:9 images with Gemini 3.1 Flash Image Preview.

Varnan-Tech/opendirectory

World-class brand strategist and naming expert. Uses an interrogation-led discovery phase to extract your brand's DNA, then applies scientific naming frameworks (Phonosemantics) and automated multi-TLD domain checking.

Varnan-Tech/opendirectory

Use when the user asks to generate or update a project's CLAUDE or AGENTS context file from a codebase scan. Writes a focused file under 100 lines containing only the non-obvious build commands, conventions, and gotchas Claude Code needs.

Varnan-Tech/opendirectory

Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.co or the open-source Reacher engine.

Varnan-Tech/opendirectory

Competitive intelligence orchestrator tracking companies across 8+ platforms (GitHub, Twitter, Reddit, HN, PH, YC Jobs) with heat scores and AI briefings.

Varnan-Tech/opendirectory

Give it your product URL or description. It finds your top 5 competitors, runs three-track PR research across all of them (editorial, podcasts, communities), identifies which channels appear most frequently, looks up the journalist or host for each, and returns a tiered outreach list with story angles and ready-to-send cold pitch drafts tailored to your product. Use when asked to find PR opportunities, discover where competitors got featured, build a media outreach list, find which journalists cover my space, or get pitch templates for press coverage.

Varnan-Tech/opendirectory

Generate high-converting, deep-dive growth case studies in MDX format. Use this skill when asked to write a case study or blog post about a company's growth, tech stack, or product-led strategy. It handles the full pipeline (researching the company via Tavily, generating a 16:9 cover image, quality checking the draft, uploading assets to cloud storage, and pushing directly to the target repository).

Varnan-Tech/opendirectory

Scans your project for outdated npm, pip, Cargo, Go, or Ruby packages. Runs a CVE security audit. Fetches changelogs, summarizes breaking changes with Gemini, and opens one PR per risk group (patch, minor, major). Includes Diagnosis Mode for install conflicts. Use when asked to update dependencies, check for outdated packages, open dependency PRs, scan for package updates, audit for CVEs, or flag breaking changes in upgrades. Trigger when a user says "check for outdated packages", "update my dependencies", "open PRs for dependency updates", "scan for CVEs", or "which packages need upgrading".

Varnan-Tech/opendirectory

Generates and updates README.md and API reference docs by reading your codebase's functions, routes, types, schemas, and architecture. Uses graphify to build a knowledge graph first, then writes accurate docs from it. Use when asked to write docs, generate a README, document an API, update stale docs, create an API reference from code, add an architecture section, or document a project in any language. Trigger when a user says their docs are missing, outdated, or wants to document their codebase without writing it manually.

Varnan-Tech/opendirectory

Evaluates expired domain candidates against a target niche, scores them by topical relevance, historical activity level, and history cleanliness, then outputs a ranked shortlist with explainable reasoning and risk flags.

Varnan-Tech/opendirectory

Brutally honest developer-experience audit for a GitHub repo. Scores 10 DX dimensions (time-to-first-success, README clarity, visual proof, install, quick-start, docs, examples, community, trust, marketing), writes a shareable roast in the requested tone (brutal/honest/kind), produces a prioritized action plan ranked by impact × effort, and sketches an ideal README. Trigger when user says "roast my repo", "audit my README", "dx audit", "developer experience review", "score my GitHub project", "before launch checklist", or "make my repo shareable".

Varnan-Tech/opendirectory

Drafts and designs a complete HTML email newsletter from a topic or content brief. Outputs paste-ready HTML for Loops, Mailchimp, Beehiiv, Resend, or any standard email platform. Includes subject line options and plain-text fallback. Trigger when a user says "write a newsletter", "draft an email newsletter", "create an HTML email", "design an email for my subscribers", or "write a newsletter about [topic]".

Varnan-Tech/opendirectory

Takes a GitHub PR URL or the current branch and writes a plain-English explanation of what it does and why, then posts it as a PR comment. Use when asked to explain a PR, summarize a pull request, write a plain-English description of a PR, add a summary comment to a PR, or understand what a PR changes. Trigger when a user says "explain this PR", "summarize this pull request", "what does this PR do", "add a comment explaining the PR", or shares a GitHub PR URL and asks what it does.

Varnan-Tech/opendirectory

Audit how often LLMs recommend your brand vs competitors and generate a GEO action plan.

Varnan-Tech/opendirectory

Find recurring confusion in your repo's GitHub Discussions, rank it by urgency, and draft the actual docs fixes and content angles — with verbatim community quotes and source links as evidence.

Varnan-Tech/opendirectory

SEO keyword research workflow for blog generation using Google Trends data. Use when writing blog posts, planning content calendars, or optimizing articles for search engines. Finds breakout keywords, builds content structure, and generates SEO-optimized blog outlines targeting tech and developer audiences.

Varnan-Tech/opendirectory

Generates a professionally designed case study PDF for B2B SaaS sales and marketing. Supports 7 page layouts, 9 style presets, 1-4 page output. Trigger when user says "create a case study", "write a customer story", "make a case study PDF", "design a success story", "turn this customer win into a PDF".

Varnan-Tech/opendirectory

Generates data visualization charts (bar, line, area, pie, doughnut, scatter, radar, treemap) as PNG using Apache ECharts v6. 1080×1080px default, 5 style presets, highlight annotations. Trigger when user says "create a chart", "visualize data", "make a bar chart", "line graph", "pie chart", "data visualization", "chart this data", "plot", "graph", or "visualize these numbers".

Varnan-Tech/opendirectory

Creates professionally designed B2B SaaS e-books in HTML + CSS, exported as print-ready PDF. 3–10 pages, 9 style presets, 11 page layout types. Trigger when user says "create an ebook", "design a lead magnet", "make a PDF guide", "build a gated content piece", "write a B2B ebook", "design a white paper", "create a nurture asset", or "make a PDF report".

Varnan-Tech/opendirectory

Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities.

相关技能