Communitygithub.com

SDK 채택 추적기

SDK나 라이브러리 이름을 입력하면 GitHub 코드 검색을 통해 이를 임포트하거나 require하는 공개 저장소를 찾고, 각 저장소를 회사 조직, 소속 개발자, 개인 개발자, 튜토리얼 노이즈로 분류하며, 채택 신호 강도에 따라 점수를 매기고, 날짜별로 새로운 채택자를 감지하여, 높은 신호를 보낸 회사에 대한 아웃리치 컨텍스트와 함께 사용자 순위 목록을 출력합니다. SDK 사용자를 찾거나, SDK 채택을 추적하거나, 라이브러리를 기반으로 구축 중인 회사를 찾거나, 기존 SDK 사용자로부터 따뜻한 리드를 식별하거나, 패키지를 임포트하는 조직을 확인할 때 사용하세요.

SDK 채택 추적기란 무엇인가요?

**SDK 채택 추적기**는 개발자에게 자신의 SDK나 라이브러리를 사용하는 GitHub 상의 공개 저장소를 찾고 분석할 수 있는 강력한 도구입니다. `GITHUB_TOKEN`을 사용하여 GitHub 코드 검색 API를 통해 패키지를 임포트하거나 require하는 저장소를 검색합니다. 그런 다음 각 저장소를 회사 조직, 소속 개발자, 개인 개발자, 튜토리얼 노이즈의 네 가지 계층으로 분류하고, 저장소의 소유권 유형과 활동성을 기반으로 채택 점수를 계산합니다. 튜토리얼 저장소와 SDK 자체의 포크는 필터링하여 진정한 채택 사례에 집중합니다. 최종 결과는 점수가 매겨지고 분류된 저장소 목록으로, 높은 신호를 보낸 회사 채택자에 대한 아웃리치 컨텍스트가 포함되어 있어 마케팅, 영업 및 제품 팀이 잠재적 사용자와 소통하고 SDK 성장을 추적하는 데 이상적입니다. 이 도구는 npm, Python, Go를 포함한 여러 패키지 생태계를 지원하며, 일반적인 실수(예: 회사명이나 연락처 정보 추측)를 피하기 위한 명확한 지침을 제공합니다.

지원 대상✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/Varnan-Tech/opendirectory/tree/main/skills/sdk-adoption-tracker

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

SDK 채택 추적기은(는) 무엇을 하나요?

SDK 이름을 입력받습니다. GitHub에서 이를 임포트하는 공개 저장소를 검색합니다. 각 저장소에 회사 신호, 활동성, 노이즈 지표를 기준으로 점수를 매깁니다. 높은 신호의 저장소를 소유자 및 기여자 데이터로 보강합니다. 회사 채택자에 대한 아웃리치 컨텍스트가 포함된 순위별 채택 보고서를 출력합니다.


핵심 규칙: 출력의 모든 저장소는 GitHub 코드 검색 API 응답에 존재해야 합니다. 모든 회사 이름은 GitHub 사용자 또는 조직 API의 company 또는 name 필드에서 가져와야 합니다. 모든 기여자 핸들은 GitHub 기여자 API 응답에서 가져와야 합니다. API에서 필드가 비어 있으면 "not listed"라고 작성하고, 추론, 추측 또는 외삽하지 마십시오.


일반적인 실수

에이전트가 하려는 행동왜 틀렸나요
GITHUB_TOKEN 없이 코드 검색 실행인증되지 않은 코드 검색은 3 req/min의 2차 속도 제한에 도달하며 의미 있는 스캔에서 실패합니다. GITHUB_TOKEN이 필요합니다. 누락된 경우 명확한 오류와 함께 1단계에서 중지하십시오.
SDK 자체의 포크 포함SDK를 포크한 저장소는 기여자이거나 미러일 뿐, 채택자가 아닙니다. fork == true이고 저장소 이름이 SDK 이름과 일치하는 저장소는 필터링하십시오.
500개의 모든 원시 검색 결과를 AI로 전송코드 검색은 최대 500개의 결과를 반환할 수 있으며, 대부분은 노이즈입니다. 먼저 로컬에서 필터링하고 점수를 매기십시오. AI 분석 단계에는 상위 20개의 높은 신호 저장소만 보내십시오.
튜토리얼 및 예제 저장소를 채택자로 보고이름이나 설명에 "example", "tutorial", "demo", "learn", "sample", "playground", "starter"가 포함된 저장소는 프로덕션 사용자가 아닙니다. tutorial_noise로 표시하고 리드 브리핑에서 제외하십시오.
회사 이름이나 연락처 핸들 지어내기모든 회사 이름은 GitHub company 또는 조직 name 필드에서 가져와야 합니다. 모든 기여자 핸들은 기여자 API 응답에서 가져와야 합니다. 필드가 비어 있으면 "not listed"라고 작성하십시오.
모든 생태계에 하나의 임포트 패턴 사용require('sdk')는 Python 사용자를 찾지 못합니다. SDK 이름에서 생태계를 자동 감지하고 생태계별 패턴을 구축하십시오. 자동 감지가 모호하면 사용자에게 물어보십시오.

1단계: 설정 확인

if [ -z "$GITHUB_TOKEN" ]; then
  echo "ERROR: GITHUB_TOKEN is required for code search."
  echo "Add a token at github.com/settings/tokens (no scopes needed for public repos)."
  echo "Without it, GitHub code search hits a 3 req/min secondary rate limit and fails."
  exit 1
fi
echo "GITHUB_TOKEN: set"
curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
     -H "Accept: application/vnd.github+json" \
     "https://api.github.com/rate_limit" | python3 -c "
import json, sys
d = json.load(sys.stdin)
search = d['resources']['search']
core = d['resources']['core']
print(f'Search rate: {search[\"remaining\"]}/{search[\"limit\"]} remaining')
print(f'Core rate:   {core[\"remaining\"]}/{core[\"limit\"]} remaining')
"

검색 잔여 횟수가 0이면 중지하십시오. 사용자에게 X-RateLimit-Reset의 초기화 시간을 알려주십시오.


2단계: 입력 수집

대화에서 다음을 수집합니다:

  • SDK 이름 (예: @company/my-sdk, requests, github.com/org/go-sdk)
  • 선택 사항: 생태계 재정의 (npm, python, go, gem) -- 제공되지 않으면 자동 감지
  • 선택 사항: 결과에서 제외할 조직/사용자 (SDK 소유자의 자체 저장소)
  • 선택 사항: 제품 컨텍스트 문자열 (아웃리치 메시지 개인화에 사용)

생태계 자동 감지:

  • @로 시작하거나 - 포함: npm
  • / 또는 - 없이 snake_case: python
  • github.com/ 포함: go
  • 그 외: 사용자에게 문의

SDK 이름이 제공되지 않은 경우: "추적하려는 SDK 또는 라이브러리는 무엇입니까? 임포트 문에 나타나는 패키지 이름을 제공하세요 (예: stripe, @clerk/nextjs, requests)."라고 문의하십시오.

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

sdk_name = "SDK_NAME_HERE"
ecosystem_override = ""  # leave empty for auto-detect
exclude_owner = ""       # optional: owner name to exclude (usually the SDK publisher)
product_context = ""     # optional: what your product does

# Auto-detect ecosystem
if ecosystem_override:
    ecosystem = ecosystem_override
elif sdk_name.startswith("@") or "-" in sdk_name:
    ecosystem = "npm"
elif re.match(r'^[a-z][a-z0-9_]*$', sdk_name):
    ecosystem = "python"
elif "github.com/" in sdk_name:
    ecosystem = "go"
else:
    ecosystem = "generic"

print(f"SDK: {sdk_name}")
print(f"Ecosystem: {ecosystem}")
print(f"Exclude owner: {exclude_owner or '(none)'}")

with open("/tmp/sat-input.json", "w") as f:
    json.dump({
        "sdk_name": sdk_name,
        "ecosystem": ecosystem,
        "exclude_owner": exclude_owner,
        "product_context": product_context
    }, f)
PYEOF

3단계: GitHub 코드 검색

먼저 독립 실행형 스크립트가 있는지 확인하십시오. 이 스크립트는 3-5단계를 한 번에 처리합니다.

ls scripts/fetch.py 2>/dev/null && echo "script available" || echo "script not found"

스크립트를 사용할 수 있는 경우, 실행하고 6단계로 건너뛰십시오:

python3 scripts/fetch.py "$(python3 -c "import json; d=json.load(open('/tmp/sat-input.json')); print(d['sdk_name'])")" \
  --ecosystem "$(python3 -c "import json; d=json.load(open('/tmp/sat-input.json')); print(d['ecosystem'])")" \
  --exclude "$(python3 -c "import json; d=json.load(open('/tmp/sat-input.json')); print(d.get('exclude_owner',''))")" \
  --output /tmp/sat-script-out.json

그런 다음 6-8단계에서 예상하는 임시 파일 형식으로 로드합니다:

python3 << 'PYEOF'
import json

out = json.load(open("/tmp/sat-script-out.json"))
json.dump(out["raw_results"], open("/tmp/sat-raw-results.json", "w"), indent=2)
json.dump(out["scored"], open("/tmp/sat-scored.json", "w"), indent=2)
json.dump(out["enriched"], open("/tmp/sat-enriched.json", "w"), indent=2)
print(f"Loaded: {len(out['raw_results'])} raw | {len(out['scored'])} scored | {len(out['enriched'])} enriched")
PYEOF

스크립트를 사용할 수 없는 경우, 아래 인라인 코드를 실행하십시오.

임포트 패턴을 구축하고 각 패턴에 대해 GitHub 코드를 검색합니다:

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

ctx = ssl._create_unverified_context()
token = os.environ["GITHUB_TOKEN"]
headers = {
    "Accept": "application/vnd.github+json",
    "Authorization": f"Bearer {token}",
    "User-Agent": "sdk-adoption-tracker/1.0"
}

data = json.load(open("/tmp/sat-input.json"))
sdk_name = data["sdk_name"]
ecosystem = data["ecosystem"]

# Build query patterns per ecosystem
if ecosystem == "npm":
    # Use sdk name without @ prefix for bare searches
    bare = sdk_name.lstrip("@").replace("/", "/")
    queries = [
        f'require("{sdk_name}")',
        f"require('{sdk_name}')",
        f'from "{sdk_name}"',
        f"from '{sdk_name}'",
    ]
elif ecosystem == "python":
    queries = [
        f"import {sdk_name}",
        f"from {sdk_name} import",
        f"from {sdk_name}.",
    ]
elif ecosystem == "go":
    queries = [f'"{sdk_name}"']
else:
    queries = [sdk_name]

print(f"Building queries for {sdk_name} ({ecosystem}):")
for q in queries:
    print(f"  {q}")

seen_repos = {}  # full_name -> first match
search_rate_remaining = 30
flags = []

for i, query in enumerate(queries):
    if search_rate_remaining <= 2:
        flags.append(f"Code search rate limit low ({search_rate_remaining}) -- skipped remaining patterns")
        print(f"  Rate limit low ({search_rate_remaining}), stopping early")
        break

    url = f"https://api.github.com/search/code?q={urllib.parse.quote(query)}&per_page=100"
    req = urllib.request.Request(url, headers=headers)

    try:
        with urllib.request.urlopen(req, timeout=20, context=ctx) as resp:
            search_rate_remaining = int(resp.headers.get("X-RateLimit-Remaining", 10))
            raw = json.loads(resp.read())

        items = raw.get("items", [])
        total = raw.get("total_count", 0)
        print(f"  Pattern '{query}': {total} total, {len(items)} fetched | rate_remaining={search_rate_remaining}")

        for item in items:
            repo = item.get("repository", {})
            full_name = repo.get("full_name", "")
            if full_name and full_name not in seen_repos:
                seen_repos[full_name] = {
                    "full_name": full_name,
                    "name": repo.get("name", ""),
                    "owner_login": repo.get("owner", {}).get("login", ""),
                    "owner_type": repo.get("owner", {}).get("type", ""),
                    "file_path": item.get("path", ""),
                    "matched_pattern": query,
                    "html_url": repo.get("html_url", ""),
                    "description": repo.get("description") or "",
                }

    except urllib.error.HTTPError as e:
        if e.code == 403:
            flags.append(f"Code search rate limit hit on pattern '{query}'")
            print(f"  Rate limit hit (403) on '{query}'")
            break
        else:
            print(f"  HTTP {e.code} on '{query}'")
    except Exception as e:
        print(f"  Error on '{query}': {e}")

    # Respect 10 req/min code search limit
    if i < len(queries) - 1:
        time.sleep(6)

results = list(seen_repos.values())
json.dump(results, open("/tmp/sat-raw-results.json", "w"), indent=2)
print(f"\nTotal unique repos found: {len(results)}")
if flags:
    print("Flags:", flags)

import urllib.parse  # ensure imported
PYEOF

결과가 0인 경우: 사용자에게 "{sdk_name}을 임포트하는 저장소를 찾을 수 없습니다. GitHub 코드 검색 인덱싱은 새로운 패키지의 경우 1-4주가 소요됩니다. SDK가 이미 확립된 경우 references/import-patterns.md`에서 임포트 패턴을 확인하세요."라고 알려주십시오.


4단계: 저장소 점수 매기기 및 분류

API 호출 없음. 순수 Python. 노이즈를 필터링하고, 채택 점수를 계산하고, 각 저장소를 분류합니다.

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

data = json.load(open("/tmp/sat-input.json"))
exclude_owner = data.get("exclude_owner", "").lower()
sdk_name = data["sdk_name"].lower().split("/")[-1].replace("@", "")

results = json.load(open("/tmp/sat-raw-results.json"))

TUTORIAL_WORDS = {"example", "tutorial", "demo", "learn", "sample", "starter",
                  "boilerplate", "template", "playground", "test", "course", "workshop"}

scored = []
now = datetime.now(tz=timezone.utc)

for repo in results:
    full_name = repo["full_name"]
    owner_login = repo.get("owner_login", "").lower()
    repo_name = repo.get("name", "").lower()
    description = (repo.get("description") or "").lower()
    owner_type = repo.get("owner_type", "User")

    # Exclude the SDK owner's own repos
    if exclude_owner and owner_login == exclude_owner.lower():
        continue

    # Detect tutorial noise
    name_words = set(repo_name.replace("-", " ").replace("_", " ").split())
    desc_words = set(description.split())
    is_tutorial = bool((name_words | desc_words) & TUTORIAL_WORDS)
    # Also exclude if repo name IS the SDK name (likely a fork)
    if repo_name == sdk_name or repo_name.startswith(sdk_name + "-"):
        is_tutorial = True  # treat as noise

    # Classification tier
    if is_tutorial:
        tier = "tutorial_noise"
    elif owner_type == "Organization":
        tier = "company_org"
    else:
        tier = "solo_dev"  # will be upgraded to affiliated_dev in Step 5 if company field populated

    # Adoption score (filled with partial data now, enriched in Step 5)
    score = 0
    if owner_type == "Organization": score += 50
    if not is_tutorial:              score += 20
    # stars, days_since_push, is_fork, is_archived added in Step 5

    scored.append({
        **repo,
        "tier": tier,
        "is_tutorial": is_tutorial,
        "adoption_score": score,
        "enriched": False
    })

# Sort: company_org first, then by tier
tier_order = {"company_org": 0, "affiliated_dev": 1, "solo_dev": 2, "tutorial_noise": 3}
scored.sort(key=lambda x: (tier_order.get(x["tier"], 9), -x["adoption_score"]))

json.dump(scored, open("/tmp/sat-scored.json", "w"), indent=2)

tiers = {}
for r in scored:
    tiers[r["tier"]] = tiers.get(r["tier"], 0) + 1

print(f"Classification:")
for tier, count in sorted(tiers.items(), key=lambda x: tier_order.get(x[0], 9)):
    print(f"  {tier}: {count}")
print(f"Total: {len(scored)} repos")

non_noise = [r for r in scored if r["tier"] != "tutorial_noise"]
print(f"\nTop repos for enrichment (non-noise): {len(non_noise)}")
for r in non_noise[:5]:
    print(f"  {r['full_name']} ({r['tier']}) -- {r.get('description','')[:60]}")
PYEOF

모든 저장소가 tutorial_noise인 경우: 중지하십시오. 사용자에게 "발견된 모든 저장소가 튜토리얼 또는 예제로 보입니다. 공개 GitHub에서 프로덕션 채택자가 감지되지 않았습니다. SDK가 너무 새롭거나 패키지 이름이 일반적이어서 검색 결과가 예제로 가득 차 있을 수 있습니다."라고 알려주십시오.


5단계: 높은 신호 저장소 보강

노이즈가 아닌 저장소에 대한 전체 저장소 메타데이터, 소유자 프로필 및 상위 기여자를 가져옵니다. tutorial_noise 저장소는 완전히 건너뜁니다.

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

ctx = ssl._create_unverified_context()
token = os.environ["GITHUB_TOKEN"]
headers = {
    "Accept": "application/vnd.github+json",
    "Authorization": f"Bearer {token}",
    "User-Agent": "sdk-adoption-tracker/1.0"
}

scored = json.load(open("/tmp/sat-scored.json"))
core_remaining = 5000
flags = []

def gh_get(path):
    global core_remaining
    req = urllib.request.Request(f"https://api.github.com{path}", headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=15, context=ctx) as resp:
            remaining = resp.headers.get("X-RateLimit-Remaining")
            if remaining:
                core_remaining = int(remaining)
            return json.loads(resp.read())
    except urllib.error.HTTPError as e:
        if e.code == 404:
            return None
        raise
    except Exception:
        return None

target = [r for r in scored if r["tier"] != "tutorial_noise"]
print(f"Enriching {len(target)} repos (skipping tutorial_noise)...")

enriched = []

for item in target:
    full_name = item["full_name"]
    owner_login = item["owner_login"]
    owner_ty

(원문이 잘렸습니다. 나머지 부분은 원본 SKILL.md와 동일한 구조를 따릅니다.)

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

Takes a competitor's public GitHub repo URL, fetches their open issues via the GitHub REST API, filters noise locally, clusters issues into 6 demand categories, computes a demand score per issue and per cluster, and outputs a ranked demand gap report with a GTM messaging brief. Use when asked to scan a competitor's GitHub issues, find what their users are begging for, turn GitHub complaints into product positioning, identify competitor feature gaps, or generate messaging from real user demand. Trigger when a user says "scan competitor issues", "what are users asking for on X repo", "find demand gaps in Y", "turn GitHub issues into messaging", or "what should I build based on competitor complaints".

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

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

관련 스킬