solo-github-outreach
/github-outreach
Competitive outreach pipeline. Scan a competitor's dependents, evaluate which repos would benefit from switching to your library, and draft personalized GitHub issues.
Works with any crate/package — not specific to any product.
Scripts
Use these instead of reimplementing from scratch:
scripts/init_jsonl.py <repos.txt> <out.jsonl>— convert repo list to JSONLscripts/enrich.py <jsonl> [--batch 30]— add stars/description viagh api, skip forks/archivedscripts/evaluate.py <jsonl> <owner/repo>— deep-evaluate a repo (README + Cargo.toml + feature detection)scripts/evaluate.py <jsonl> --next— pick next highest-star enriched reposcripts/status.py <jsonl> [--targets] [--csv]— show pipeline status table
Data Format
All data lives in data/outreach/{competitor}/ in the project directory:
data/outreach/{competitor}/
config.json # Competitor, our product, feature matrix
dependents.jsonl # One JSON object per repo (append-only)
progress.json # Cursor: last evaluated index, stats
dependents.jsonl schema
Each line is a JSON object:
{
"repo": "owner/name",
"stars": 1234,
"description": "...",
"language": "Rust",
"last_push": "2026-03-20T...",
"archived": false,
"phase": "raw|enriched|evaluated|drafted|posted|skipped",
"score": 0,
"features_used": ["streaming", "tools", "embeddings"],
"our_advantages": ["websocket", "structured_outputs"],
"verdict": "skip|maybe|target",
"verdict_reason": "fork of AppFlowy, not original",
"draft_title": "",
"draft_body": "",
"issue_url": "",
"evaluated_at": "",
"notes": ""
}
JSONL is append-friendly and grep/jq compatible. Render as table with status command.
Routing
| User says | Action |
|---|---|
/outreach setup |
Configure competitor + our product |
/outreach enrich |
Add stars/description via gh api |
/outreach next |
Evaluate next unevaluated repo |
/outreach evaluate [repo] |
Deep-evaluate a specific repo |
/outreach draft [repo] |
Generate issue text for a target |
/outreach status |
Show progress table |
/outreach batch [N] |
Evaluate next N repos (default 5) |
Setup
First-time configuration.
-
Ask or read from
$ARGUMENTS:- Competitor: crate name or
owner/repo(e.g.,async-openai) - Our product: crate name + repo URL
- Feature matrix file path (or build interactively)
- Competitor: crate name or
-
Create
data/outreach/{competitor}/config.json:
{
"competitor": "async-openai",
"competitor_repo": "64bit/async-openai",
"our_product": "openai-oxide",
"our_repo": "fortunto2/openai-oxide",
"feature_matrix": {
"persistent_websockets": {"us": true, "them": false, "impact": "high", "pitch": "..."},
"structured_outputs": {"us": true, "them": false, "impact": "high", "pitch": "..."}
}
}
- Check if
dependents.jsonlexists. If not, run scraper:
Convert to JSONL with phase=raw.scripts/scrape-dependents.sh {competitor_repo} 50 > /tmp/deps.txt
Enrich
Fast pass: add GitHub metadata to all phase=raw entries.
# For each raw entry, call gh api
gh api repos/{owner}/{repo} --jq '{
stars: .stargazers_count,
description: .description,
language: .language,
last_push: .pushed_at,
archived: .archived,
topics: .topics
}'
- Update phase to
enriched - Skip if
gh apireturns 404 (private/deleted) — set phase=skipped - Rate limit: batch 30 per minute (authenticated), save after each batch
- Sort by stars descending for evaluation priority
Evaluate (per repo)
Deep analysis of a single repo. This is where the agent thinks.
Step 1: Quick filter (skip obvious non-targets)
- Archived? Skip
- Fork with <5 stars? Skip (not the original)
- Last push >1 year ago? Skip (abandoned)
- Stars <3 and no meaningful description? Skip
Step 2: Read README
gh api repos/{owner}/{repo}/readme --jq '.content' | base64 -d
Understand: what does this project do? How do they use the competitor?
Step 3: Read Cargo.toml (find usage pattern)
gh api repos/{owner}/{repo}/contents/Cargo.toml --jq '.content' | base64 -d
Which features do they use? What else is in their dependency tree?
Step 3b: Check if they forked the competitor
If Cargo.toml references the competitor via git = "..." (not crates.io), they forked it — this is a HIGH SIGNAL:
# Find their fork
gh api repos/{fork_owner}/{competitor_name}/commits --jq '.[0:5] | .[] | "\(.sha[0:7]) \(.commit.message | split("\n")[0])"'
Compare their fork commits against upstream. If they added a feature we already have (e.g. schemars for structured outputs), that's our strongest pitch: "you can drop the fork and use us — we have that built-in." Record exactly what they added in notes.
Step 4: Grep for usage patterns (optional, for top targets)
If stars >50, clone shallow and grep:
git clone --depth 1 {url} /tmp/outreach-eval
grep -rn "async.openai\|ChatCompletion\|stream\|tool_call\|embedding" /tmp/outreach-eval/src/
rm -rf /tmp/outreach-eval
Map findings to feature signals (streaming, tools, structured, websocket, etc.)
Step 5: Score and verdict
Score based on:
- Stars (weight: 30%) — reach/impact
- Activity (weight: 20%) — will they actually migrate?
- Feature fit (weight: 30%) — do our advantages matter to them?
- Approachability (weight: 20%) — open to contributions? Has issues enabled?
Verdict:
- target (score >= 60) — worth creating an issue
- maybe (score 30-59) — revisit later
- skip (score < 30) — not worth effort
Update JSONL entry with phase=evaluated.
Step 6: Cleanup
Always rm -rf /tmp/outreach-eval after analysis.
Draft (per repo)
Generate a personalized GitHub issue for a target repo.
- Load config (feature matrix, pitches)
- Load evaluation data (features_used, our_advantages)
- Draft issue using
references/issue-templates.md - Key rules:
- Never generic — reference their specific use case
- Lead with their problem — not our solution
- Offer concrete benefit — "your agent loop would be 40% faster with persistent WebSockets"
- No hard sell — "you might find this useful" tone
- Include migration path — show how imports change
- Save draft to JSONL entry (phase=drafted)
- Output draft for user review before posting
Status
Show progress across all repos.
Outreach: async-openai → openai-oxide
Phase Count
─────────────────
raw 12
enriched 340
evaluated 180
→ target 8
→ maybe 47
→ skip 125
drafted 3
posted 1
skipped 59
─────────────────
Total 591
Top targets (not yet drafted):
1. fastrepl/char (8068★) — streaming + agent loop
2. risingwavelabs/risingwave (7000★) — embeddings
...
Read from JSONL, aggregate by phase/verdict.
Batch Mode
Evaluate next N repos efficiently.
- Load JSONL, filter phase=enriched, sort by stars desc
- For each (up to N):
- Run Evaluate flow
- Print one-line result
- Continue to next (no pause)
- Print batch summary
Critical Rules
- Never post issues without user approval — draft only, user reviews
- Never clone repos larger than 100MB — check size via
gh apifirst - Always cleanup —
rm -rf /tmp/outreach-evalafter every evaluation - Rate limit gh api — max 30 requests per batch, save progress
- Respect repos — if issues are disabled, skip. If they said no, mark as skipped
- One issue per repo — never spam
- Personalize everything — generic "try our lib" issues get ignored and damage reputation
- JSONL is append-only — update by rewriting the line (match by repo field)
Gotchas
- Forks dominate dependents — 50%+ of dependents are forks of big projects (AppFlowy, meilisearch). Filter by checking if the repo is a fork via
gh api.forkfield. Only evaluate originals. - gh api rate limit — 5000/h authenticated but large scans hit it. Use
--paginatesparingly. CheckX-RateLimit-Remainingheader. - README doesn't show actual usage — a repo may list async-openai in Cargo.toml but barely use it. Always check Cargo.toml features and grep source.
- Stale dependents — GitHub's dependency graph is delayed. Some repos may have already switched away. Check Cargo.lock if available.
- Issue tone matters enormously — "I noticed you use X, have you tried Y?" works. "X is slow, switch to Y" does not. See
references/issue-templates.md. - Competitor forks are the strongest signal — if a repo uses
git = "..."instead of crates.io, they forked the competitor because it's missing something. Check the fork diff (usually 1-3 commits). If they added a feature we already have, that's our #1 pitch — "drop your fork, we have it built-in." Example: fastrepl/char forked async-openai to add schemars → ourstructuredfeature does exactly that.
More from fortunto2/solo-factory
solo-research
Use when "research this idea", "find competitors", "check the market", "domain availability", "market size", "analyze opportunity", or need evidence before validation. Do NOT use for idea scoring (/validate) or SEO auditing (/seo-audit).
43solo-swarm
Use when "swarm research", "parallel research", "investigate fast", "3 agents", "team research", or want faster multi-angle alternative to /research. Do NOT use for solo research (/research) or idea scoring (/validate).
34solo-build
Use when "build it", "start building", "execute plan", "implement tasks", "ship it", track ID referenced, or plan tasks need execution. Do NOT use for planning (/plan) or scaffolding (/scaffold).
33solo-humanize
Use when "humanize this", "make it sound human", "strip AI patterns", "clean up the copy", or text reads like AI-generated output with em dashes and stock phrases.
33solo-audit
Use when "audit KB", "check frontmatter", "find broken links", "tag cleanup", "knowledge base quality", or docs need health check. Do NOT use for SEO audits (/seo-audit) or code reviews (/review).
31solo-scaffold
Use when "scaffold project", "create new project", "start new app", "bootstrap project", "set up from PRD", or need project from PRD + stack template. Do NOT use for planning features (/plan) or PRD generation (/validate).
30