with one click
vc-signals
// VC signal-to-thesis skill for Marathon-style deal discovery. Weekly all-sector radar, OSS radar, agent-native research workbench, theme drill-downs, company backtrace, and GitHub trending repos.
// VC signal-to-thesis skill for Marathon-style deal discovery. Weekly all-sector radar, OSS radar, agent-native research workbench, theme drill-downs, company backtrace, and GitHub trending repos.
| name | vc-signals |
| description | VC signal-to-thesis skill for Marathon-style deal discovery. Weekly all-sector radar, OSS radar, agent-native research workbench, theme drill-downs, company backtrace, and GitHub trending repos. |
| argument-hint | vc-signals radar all, vc-signals workbench docs/radar-runs/current, vc-signals oss ai-infra, vc-signals theme "agent evals", vc-signals setup |
| allowed-tools | Bash, Read, Write, WebSearch, AskUserQuestion |
| user-invocable | true |
Turn noisy public internet chatter into ranked, investor-oriented theme briefs with company mapping.
This is NOT a generic research tool. It is a signal-to-thesis skill built for a VC workflow.
Parse the user's input to determine the mode and arguments:
/vc-signals setup → Setup wizard mode/vc-signals radar <sector|all> [time] → Company-first weekly radar (sectors: devtools, cybersecurity, ai-infra, vertical-ai, data-infra, oss; all runs one unified brief with sector sections)/vc-signals weekly <sector> [time] → Alias for radar (kept for backward compatibility)/vc-signals theme "<topic>" [time] → Theme drill-down/vc-signals company "<name>" [time] → Company backtrace/vc-signals oss <sector> [time] → OSS-native radar using repo velocity, community signal, contributor quality, and company-formation likelihood/vc-signals github <sector> → GitHub trending repos (sectors: devtools, cybersecurity, ai-infra, vertical-ai, data-infra, oss, all)/vc-signals workbench <run-dir> → Agent-native research workbench for weak-signal synthesis without promoting unverified leads/vc-signals add-sector <name> → Add a new sector (guided)/vc-signals compare "<company1>" "<company2>" → Head-to-head comparison (stretch)Time window (optional): Users can append a time window like 7d, 14d, 30d, 60d, 90d to control how far back to search. Examples:
/vc-signals weekly devtools → default 14 days/vc-signals weekly devtools 7d → last 7 days only/vc-signals weekly devtools 30d → last 30 days/vc-signals theme "agent evals" 90d → last 90 days of evidenceDefaults by mode:
weekly: 14 days (focused on recent signal)theme: 30 days (broader context for deep analysis)company: 30 daysIf no arguments or unrecognized arguments, show this help and ask what they'd like to do.
Before anything else, detect whether you're running in a sandboxed web environment (Claude.ai, Co-Work web) or a local environment (Claude Code CLI, Co-Work desktop with terminal access).
Quick test: Try running a simple Bash command:
echo "env_check"
If Bash is not available, or if you get network errors (403 Forbidden) when running Python scripts, you are in a web sandbox. In this case:
Web sandbox mode:
Local mode:
Before executing ANY mode (including setup), check if this is a first run:
cat ~/.config/last30days/.env 2>/dev/null | grep -q "SETUP_COMPLETE=true" && echo "configured" || echo "not_configured"
If NOT configured — and the user did NOT explicitly run /vc-signals setup:
Say this to the user:
"Welcome to VC Signals! This is your first run. I need about 2 minutes to set things up — I'll install the research engine and ask you for a couple of optional API keys. You can skip any you don't have."
"Want me to run setup now, or would you rather jump straight in with basic web search? (Setup gives you Reddit, Hacker News, X/Twitter, YouTube, and GitHub trending coverage.)"
If they choose setup → run the Setup Wizard (below), then continue to their original command.
If they choose to skip → proceed with their command using WebSearch path. Note: "Running with basic web search. You can run /vc-signals setup anytime to unlock more sources."
Auto-install last30days during setup:
As part of setup Step 3, ALWAYS clone last30days if it's not already installed — don't ask the user. Just do it:
# Determine where to clone last30days
if [ -d ".claude/skills/vc-signals" ]; then
# Project-level install — clone to vendor/
mkdir -p vendor
git clone --quiet --depth 1 https://github.com/mvanhorn/last30days-skill.git vendor/last30days-skill 2>/dev/null || true
elif [ -d "$HOME/.claude/skills/vc-signals" ]; then
# Global install (Co-Work) — clone to ~/.claude/vendor/
mkdir -p "$HOME/.claude/vendor"
git clone --quiet --depth 1 https://github.com/mvanhorn/last30days-skill.git "$HOME/.claude/vendor/last30days-skill" 2>/dev/null || true
fi
Then install requests:
python3 -m pip install requests 2>/dev/null || python3 -m pip install --user requests 2>/dev/null || true
If either fails, continue — the skill works without them (WebSearch fallback, no GitHub trending). Tell the user what succeeded and what didn't.
Before running any script, determine the skill directory. Check these locations in order:
.claude/skills/vc-signals/ (relative to the current project root — check if it exists)~/.claude/skills/vc-signals/ (global installation for Co-Work)Once found, use that path for all script commands. For example:
.claude/skills/vc-signals/, run: python3 .claude/skills/vc-signals/scripts/<script>.py~/.claude/skills/vc-signals/, run: python3 ~/.claude/skills/vc-signals/scripts/<script>.pyStore the resolved path and reuse it for all script calls in this session.
Scripts:
<skill_dir>/scripts/persistence.py — save/load briefings, diffs, theme index<skill_dir>/scripts/github_trending.py — GitHub star velocity search<skill_dir>/scripts/last30days_adapter.py — last30days integration<skill_dir>/scripts/attio.py — read-only Attio CRM matching from ATTIO_ACCESS_TOKEN<skill_dir>/scripts/radar_run.py — weekly evidence collection, quality filtering, Attio merge, and partner-preview renderingConfig:
<skill_dir>/config/sectors.json — sector taxonomy<skill_dir>/config/company_aliases.json — company seed mapFor free-text identifiers like company names or theme topics, pass --name "Free Text" to save-markdown; it slugifies internally (lowercases, replaces non-alphanumeric runs with hyphens, strips leading/trailing hyphens). Use --slug only when the value is already a known-safe identifier (sector slug, etc.).
Trigger: /vc-signals setup
Walk the user through setup one step at a time. Use plain, non-technical language. Check what's already configured and skip completed steps.
python3 --version
If Python 3.12+ is available, say: "Python is ready." and move on. If not, say: "You need Python 3.12 or newer. Here's how to install it:" and provide instructions for macOS:
brew install python@3.13
pip install requests
Say: "Installed the one Python library we need (requests, for GitHub API calls)."
Check availability:
python3 <skill_dir>/scripts/last30days_adapter.py check
If not installed, tell the user:
"The last30days research engine gives us much better results by searching Reddit, Hacker News, X/Twitter, and YouTube independently. Without it, we'll use web search which still works but gives less detailed results."
"Want me to set it up? It takes about 5 minutes and requires a few API keys. Or we can skip this and use web search for now."
If they want to proceed:
git clone https://github.com/mvanhorn/last30days-skill.git vendor/last30days-skill
Then walk them through API keys one at a time:
ScrapeCreators API Key (required for last30days):
"ScrapeCreators lets us search TikTok, Instagram, and YouTube. It's the one required key for last30days."
"Here's how to get it:"
- Go to https://scrapecreators.com
- Sign up for an account
- Go to your dashboard and copy your API key
- Paste it here
Web Search API Key (pick one -- Brave recommended):
"We need a web search API for broader coverage. Brave Search is the easiest -- you get $5 in free credits each month, which covers about 1,000 searches. More than enough for weekly scans."
- Go to https://brave.com/search/api/
- Click "Get Started for Free"
- Create an account and get your API key
- Paste it here (or type 'skip' to skip this)
Reasoning Provider (pick one -- OpenAI or Gemini):
"last30days needs an AI provider for query planning and ranking. Either OpenAI or Gemini works."
OpenAI:
- Go to https://platform.openai.com/api-keys
- Create a new API key
- You'll need a paid account (usage-based, typically a few cents per query)
Gemini:
- Go to https://aistudio.google.com/apikey
- Create a new API key
- You get $5 free credits/month (~1,000 queries)
"Paste your key here, or type 'skip' to skip (web search mode only)."
OpenRouter API Key (optional -- for deep research):
"OpenRouter gives us access to Perplexity Sonar Pro for deep research. When you drill down into a specific theme, it produces a comprehensive report with 50+ citations. Costs about $0.90 per deep query."
- Go to https://openrouter.ai/keys
- Sign up or log in
- Click "Create Key"
- Copy the key (starts with
sk-or-)- Add credits under Billing (~$5 is enough for many queries)
"Paste your key here, or type 'skip'. Theme drill-downs still work without it -- just uses regular search instead of deep synthesis."
X/Twitter Auth Tokens (optional):
"To search X/Twitter for trending developer discussions, we need your browser auth tokens."
- Open X/Twitter in your browser and log in
- Open Developer Tools (Cmd+Option+I on Mac)
- Go to Application tab -> Cookies -> twitter.com
- Find the cookie named
auth_token-- copy its value- Find the cookie named
ct0-- copy its value"This is optional. Skip if you don't use X/Twitter."
Check if gh CLI is authenticated:
gh auth status 2>&1
If not, check for GITHUB_TOKEN env var. If neither works:
"For GitHub trending repos, we need a GitHub Personal Access Token."
- Go to https://github.com/settings/tokens
- Click "Generate new token (classic)"
- Give it a name like "vc-signals"
- Select scopes: just
public_repois enough- Generate and copy the token
Save all collected keys to ~/.config/last30days/.env:
mkdir -p ~/.config/last30days
Write the .env file with all provided keys. Also save GITHUB_TOKEN to a local config if provided.
Add SETUP_COMPLETE=true at the end.
Then lock down the file permissions:
chmod 600 ~/.config/last30days/.env
Setting secure file permissions so only your user can read the keys.
Run a quick test:
python3 <skill_dir>/scripts/last30days_adapter.py check
Print what's configured and what each unlocks:
Setup complete. Here's what's active:
- [x/skip] Web search (Brave) -- broad internet coverage
- [x/skip] GitHub API -- trending repo discovery
- [x/skip] last30days -- Reddit, HN, X/Twitter, YouTube
- [x/skip] Deep research (OpenRouter) -- Perplexity synthesis for theme drill-downs
- [x/skip] X/Twitter -- developer discussions
(Show [x] for configured items, [ ] for skipped items, based on what the user actually set up.)
You can run
/vc-signals setupagain anytime to add more capabilities. Try it out:/vc-signals radar all
Triggers: /vc-signals radar <sector|all> or /vc-signals weekly <sector|all> (alias)
Sectors: devtools, cybersecurity, ai-infra, vertical-ai, data-infra, oss. Use all for the default Marathon weekly artifact.
If sector is not recognized, say so and list valid sectors.
Default Marathon output: one unified all-sector brief delivered Monday 8:00 AM ET when scheduled. Put a cross-sector summary and top 10-15 companies first, then compact sections for each sector. Do not dump 50 rows into Slack; Slack gets the teaser/digest and a link or artifact for the full brief. If no Slack channel is configured yet, produce the Markdown artifact and include instructions to set the channel later.
Read the sector taxonomy:
cat <skill_dir>/config/sectors.json
Read the company alias map:
cat <skill_dir>/config/company_aliases.json
For deterministic weekly runs, prefer the orchestration helper before doing manual synthesis:
python3 <skill_dir>/scripts/radar_run.py weekly --sectors all --output-dir <output_dir>
This saves raw evidence JSON, normalized signals, scored candidates, and a partner preview. The default weekly command is the full-quality path: it uses the normal sector query budget and does not impose an artificial wrapper timeout on last30days. If a native grounded web key (Brave, Parallel, Serper, or Exa) is configured, the run includes grounded company discovery. If not, it automatically uses a stricter HN/GitHub fallback plus curated Reddit pain discovery instead of broad noisy company-web queries.
For a lightweight smoke test, and only when the user wants to try the flow quickly, run:
python3 <skill_dir>/scripts/radar_run.py weekly --sectors all --output-dir <output_dir> --first-pass
--first-pass uses one query per sector and a 45-second per-query cap. Do not use first-pass mode to judge final Marathon radar quality.
The weekly artifact must not silently omit sectors. If a sector has no qualified candidates, render a sector coverage note explaining whether the cause was no source evidence, source-not-candidate-eligible evidence, weak evidence, or missing grounded web/company enrichment.
For lower-level debugging, run collection and preview separately:
python3 <skill_dir>/scripts/radar_run.py collect --output-dir <output_dir>
This writes raw evidence JSON, filters obvious GitHub/reddit noise, uses the bundled Python 3.12 runtime for last30days when available, and keeps collection separate from investor judgment.
To build an automatic scored preview from saved evidence:
python3 <skill_dir>/scripts/radar_run.py preview --from-evidence <output_dir>/<YYYY-MM-DD>-raw-evidence.json --output <output_dir>/<YYYY-MM-DD>-auto-scored-preview.md
The automatic preview is intentionally conservative: it extracts candidate companies/projects, applies first-pass Investment Interest and Evidence Confidence scores, merges Attio context when ATTIO_ACCESS_TOKEN is present, filters low-interest candidates, and renders only Medium/High-interest rows. If it underfills the table, use Claude synthesis over the raw evidence plus external/web research to add higher-quality candidates rather than lowering the threshold.
The preview schema includes LinkedIn, Founders, and X columns. Fill them only from evidence, Attio/CRM fields, structured seed input, or explicit user-provided data. Do not invent LinkedIn or founder links. Without grounded web or LinkedIn-capable evidence, leave those cells blank and treat them as next diligence.
python3 <skill_dir>/scripts/persistence.py load-previous --sector <SECTOR> --before $(date +%Y-%m-%d)
If a previous briefing exists, save it for comparison in Step 7.
Also load the theme index to check for durable themes:
cat <skill_dir>/data/history/theme_index.json 2>/dev/null || echo "{}"
Use this to identify themes that have appeared in 3+ consecutive scans — these are candidates for the "Durable" section of the week-over-week comparison.
python3 <skill_dir>/scripts/last30days_adapter.py check
If installed is true -> use last30days path. The engine has useful zero-config free sources (reddit, hackernews, github, polymarket) even when no optional keys are configured.
Otherwise -> use WebSearch path.
Tell the user which path you're using:
/vc-signals setup."WebSearch path:
Generate 8-12 search queries from the taxonomy. Use the sector's discovery_queries plus queries built from subcategories seed_queries. Run each query using the WebSearch tool. Collect all results.
Example query generation for devtools:
discovery_queries directly (6 queries)For each query, use WebSearch. Collect titles, URLs, snippets.
Filtering noise: Check the sector's negative_terms list from the taxonomy config. Skip or deprioritize results that are clearly tutorial content, beginner guides, or consumer product reviews. These terms exist to reduce noise — use them when evaluating search results.
last30days path (if available):
Run 3-5 queries through the adapter with auto-resolve enabled. Auto-resolve discovers the right subreddits, X handles, and GitHub context automatically — no hardcoded lists needed.
Use --lookback-days to control the time window. Default for radar/weekly scans is 14 days. If the user appended a time window like 7d or 30d to the command, use that number's digits instead.
Curated subreddits from sectors.json are passed alongside --auto-resolve so the known-good list is always covered while auto-resolve discovers new sources.
# Set LOOKBACK_DAYS once for this scan, then reuse across all queries below.
LOOKBACK_DAYS=14 # radar/weekly default; override with the user's time-window digits if specified
# Read curated subreddits from the sector taxonomy.
SUBREDDITS=$(python3 -c "import json; print(','.join(json.load(open('<skill_dir>/config/sectors.json'))['<SECTOR>'].get('subreddits', [])))")
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<specific theme query>" --sources "reddit,hackernews,x,youtube,github,polymarket,grounding" --subreddits "${SUBREDDITS}" --auto-resolve --store --lookback-days ${LOOKBACK_DAYS}
Run each of the sector's hn_queries from the config, plus 2-3 discovery queries. Auto-resolve handles subreddit and handle targeting.
For vertical-ai and prosumer/creator-heavy workflows, add TikTok/Instagram only when they plausibly carry customer pull:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<vertical AI query>" --sources "reddit,hackernews,x,youtube,tiktok,instagram,github,grounding" --tiktok-hashtags "<tags>" --ig-creators "<handles>" --auto-resolve --store --lookback-days ${LOOKBACK_DAYS}
For infra-heavy sectors (devtools, cybersecurity, ai-infra, data-infra, oss), keep TikTok/Instagram off by default unless the user asks.
IMPORTANT: Query strategy matters. Hacker News search works best with specific, concise queries — NOT broad category dumps. Use 2-4 focused keywords per query.
Good queries (specific, get results):
Bad queries (too broad, return 0):
After collecting results, filter by engagement:
If auto-resolve fails or last30days results are thin, supplement with WebSearch using site: targeting:
"<topic> site:news.ycombinator.com""<topic> site:reddit.com/r/programming"python3 <skill_dir>/scripts/github_trending.py --sector <SECTOR> --limit 15
This runs in addition to the general retrieval. GitHub data feeds into momentum scoring and company mapping.
Now you have all the evidence. This is where your reasoning does the heavy work.
Extract candidate themes:
Cluster and deduplicate:
Filter low-yield themes (Phase 1 radar requirement):
After clustering, drop any theme that produces fewer than 3 mappable companies in Step 7. A theme without investable companies is signal noise — better to surface 6 themes × 8 companies than 12 themes × 2 companies.
Operationally: do a quick first pass through Step 7's company mapping for each candidate theme. If a theme yields fewer than 3 companies (across seed map, evidence, and GitHub data combined), drop it from the radar before scoring momentum. Note the dropped themes in the persistence record so future scans can detect when a previously-dropped theme starts producing companies.
Score each theme -- Momentum (1-10):
Assign a transparent momentum score. For each theme, weigh these factors:
| Factor | Weight | What to look for |
|---|---|---|
| Recency | High | Are discussions from the last 1-2 weeks? |
| Source diversity | High | Does it appear across multiple independent sources? |
| Repetition density | Medium | How many distinct mentions vs one viral post? |
| Engagement signals | Medium | High upvotes/comments/stars on related content? |
| Novelty | Medium | Is this a NEW conversation or evergreen background chatter? |
| Technical specificity | Low | Specific tools/approaches mentioned vs vague hand-waving? |
| GitHub velocity | Low | Are related repos showing star acceleration? |
Rubric:
You MUST explain how you arrived at each score in 1-2 sentences.
Rate confidence (low / medium / high):
Assess investment timing (early / mid / late):
Hype vs Durable verdict: One blunt sentence. Example: "Durable -- real pain point with multiple well-funded solutions." or "Likely hype -- single viral post driving most of the signal, unclear staying power."
Theme tags will be computed in Step 9, AFTER the theme index is updated (compute-tags reads post-update counts). Do not pre-compute tags here.
For each surviving theme (after Step 6 filtering), identify 8-12 relevant companies using three sources:
company_aliases.json — does any known company map to this theme?Target: 30-50 total companies across the radar. This is the centerpiece of the output, not an accessory. For all, spread coverage across sector sections and avoid letting AI infra crowd out every other sector.
Deduplicate across themes:
A company that appears in evidence for multiple themes (e.g., CodeRabbit in both "AI Code Review" and "Agentic Coding") must appear in the company list ONCE, not twice. Pick the strongest theme as primary_theme, list the others in secondary_themes. The output table shows only primary_theme.
To pick primary_theme:
direct_solver over beneficiaryFor each company, capture:
| Field | Source | Notes |
|---|---|---|
name | Display form | "MintMCP", "Anysphere (Cursor)" |
primary_theme | Picked above | The theme this company most clearly rides |
secondary_themes | Other themes the company touches | List of theme names |
why_on_radar | One sentence | The single most concrete reason this is investable. Specific signal, not generic. |
evidence_url | Best source | URL of the strongest piece of evidence |
role | direct_solver / beneficiary / adjacent / unclear | Same taxonomy as before |
confidence | confirmed / likely / inferred / speculative | Same as before |
stage | null | Phase 2 fills this; leave null for now |
raised | null | Phase 2 fills this |
headcount | null | Phase 2 fills this |
founders | null | Phase 2 fills this |
investment_interest | high / medium / low or 0-100 | How much Marathon should care if the evidence is true |
evidence_confidence | high / medium / low or 0-100 | How well-supported the claim is |
attio_status | no_match / stale / no_owner / active / passed / unknown | Phase 4 fills this; use unknown until Attio is connected |
action | assign owner / refresh note / monitor only / flag quietly / ignore | Use judgment; passed companies get flag quietly |
why_this_may_be_noise | One sentence | Skeptical counter-case |
source_links | List | 1-3 supporting URLs |
The why_on_radar field is critical. It's what Alex reads in the radar table. Bad: "AI testing company". Good: "AI-native test gen, launched 3 weeks ago, ex-Datadog founders". One sentence, specific signal.
Company tags will be computed in Step 9, AFTER the company index is updated. Do not pre-compute tags here.
Do NOT:
If ATTIO_ACCESS_TOKEN is available, enrich the company rows before formatting:
python3 <skill_dir>/scripts/attio.py check
Then pass the deduplicated company list:
printf '%s' '<companies-json>' | python3 <skill_dir>/scripts/attio.py enrich
Input shape:
{"companies":[{"name":"Cascade","domain":"runcascade.com"}]}
Use the returned fields:
attio_status: no_match, no_owner, active, stale, passed, or unknownattio_action: assign owner, refresh note, monitor only, flag quietly, etc.attio_lists: human-readable list namesattio_attributes: selected CRM fields such as status, last interaction, round, headcount, and amount raised when presentMatching rule: prefer company domain over name. The Attio client verifies stored domains before accepting a domain-based fuzzy search result, because Attio search can return similarly named but wrong companies. If no domain is known, use company name but mark weak matches conservatively.
Status rule:
no_match -> new discovery; action assign ownerno_owner -> already in Attio but needs assignment; action assign ownerstale -> old pipeline or stale interaction context; action refresh notepassed or Deprioritized -> action flag quietly and explain what changedactive -> avoid duplicate outreach; action monitor only or enrich existing contextOutput uses the radar format: themes are short headers, the company table is the centerpiece, week-over-week diff lives at the top.
Begin with the week-over-week diff (only if a previous briefing exists):
echo '{"current": <CURRENT_COMPANIES_JSON>, "previous": <PREV_COMPANIES_JSON>}' | \
python3 <skill_dir>/scripts/persistence.py company-diff
The output gives new_companies and faded_companies. Use these to populate the "New To Radar" and "Faded Off Radar" sections below.
Output template:
## VC Radar: {Sector Display Name} — Week of {YYYY-MM-DD}
### What's Moving
[For each theme, exactly 3 lines:]
- **{Theme Name}** — {TAG (Nw)}. {one-line why-now in <120 chars}.
Companies riding this: {N}
[6-8 themes max. TAG is one of NEW, ACCELERATING (#prev → #curr), PERSISTENT (Nw), or omitted if no tag. (Nw) means "N weeks active".]
### Company Radar ({N} companies)
| Company | Sector | Theme | Interest | Evidence | Attio | Action | Why On Radar | Why This May Be Noise | Sources |
|---------|--------|-------|----------|----------|-------|--------|--------------|------------------------|---------|
| MintMCP | AI Infra | MCP Infra | High | Medium | unknown | assign owner | First SOC2-compliant MCP gateway, picking up enterprise pilots | New category; buyer urgency unproven | [1](...) |
| CodeRabbit | Devtools | AI Code Review | Medium | High | unknown | monitor only | 2M repos, 13M PRs reviewed | Crowded category; likely consensus | [1](...) |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
[30-50 rows. For `all`, group by sector after the cross-sector top 10-15. Include NEW / RETURNING / PERSISTENT tags inside Why On Radar when useful instead of adding a separate tag column.]
### New To Radar This Week ({N} companies)
[Bulleted list, max 10. If more than 10 are new, show top 10 by best evidence and note "+N more in the table above".]
- {Company} — {primary_theme}. {why_on_radar}
- ...
### Faded Off Radar ({N} companies)
[Bulleted list, max 10. From company-diff faded_companies.]
- {Company} — last seen {date}, was in {primary_theme}.
### Theme Detail
[For each theme, 2-3 lines max. NOT the long-form analysis from v1. Pointer to deep-dive.]
- **{Theme}** ({company_count} companies) — {2-sentence context}.
Run `/vc-signals deep "{Theme}"` for full evidence and subthemes.
Sorting rules:
Length budget: the entire radar should fit in roughly 150-250 lines. If it's longer, the per-theme detail is too verbose — tighten it.
If the user asks to schedule or post to Slack:
To modify later: change the channel name in the scheduler/Slack connector configuration, not in the radar logic. If no channel is configured, save the briefing and print: "Slack channel not configured yet; set VC_SIGNALS_SLACK_CHANNEL or update the scheduler prompt with the desired channel."
Order matters. Snapshot the OLD indices, compute tags from the snapshots (so NEW tags fire correctly for first-time entries), THEN update the indices, THEN save. Computing tags after the update breaks NEW: the entry exists in the index by then.
Step 9a: Snapshot the OLD indices (before any updates)
OLD_THEME_INDEX=$(cat <skill_dir>/data/history/theme_index.json 2>/dev/null || echo "{}")
OLD_COMPANY_INDEX=$(python3 <skill_dir>/scripts/persistence.py load-company-index)
Step 9b: Compute tags from the OLD snapshots
echo '{"themes": <THEMES_JSON>, "companies": <COMPANIES_JSON>, "theme_index": '"$OLD_THEME_INDEX"', "company_index": '"$OLD_COMPANY_INDEX"'}' | \
python3 <skill_dir>/scripts/persistence.py compute-tags
The output enriches each theme and company with a tag field (NEW / ACCELERATING / RETURNING / PERSISTENT / null). Use these tags when rendering the markdown for Step 8 — re-render that section using the tagged data before saving.
Step 9c: Update the theme index
cat <<'THEMES_EOF' | python3 <skill_dir>/scripts/persistence.py update-index --sector <SECTOR> --date $(date +%Y-%m-%d)
[the JSON themes array goes here]
THEMES_EOF
Step 9d: Update the company index (NEW in Phase 1)
cat <<'COMPANIES_EOF' | python3 <skill_dir>/scripts/persistence.py update-company-index --sector <SECTOR> --date $(date +%Y-%m-%d)
[the JSON companies array goes here]
COMPANIES_EOF
Step 9e: Save the briefing JSON (themes + companies in one call)
cat <<'BRIEFING_EOF' | python3 <skill_dir>/scripts/persistence.py save-briefing --sector <SECTOR> --retrieval-path <websearch|last30days> --date $(date +%Y-%m-%d)
{"themes": [the tagged themes array], "companies": [the tagged companies array]}
BRIEFING_EOF
Step 9f: Save the rendered markdown
cat <<'MD_EOF' | python3 <skill_dir>/scripts/persistence.py save-markdown --subdir briefings --slug <SECTOR> --date $(date +%Y-%m-%d)
[the rendered radar markdown content goes here]
MD_EOF
If any persistence step fails, warn the user but still display the full briefing inline. Do not crash.
Trigger: /vc-signals workbench <run-dir>
Use this when the user wants Codex/Claude's own LLM judgment over a weekly run, especially when grounded company discovery is unavailable or the radar is OSS-heavy.
This mode is a verification workbench, not a canonical radar writer:
candidates.json.When the user types /vc-signals workbench <run-dir>, do the whole flow. Do not make the user run a second prompt.
First create the machine-readable pack:
python3 <skill_dir>/scripts/radar_run.py workbench --from-run <run-dir> --output-dir <run-dir>-workbench
Then read both generated files yourself:
<run-dir>-workbench/research-workbench-prompt.md<run-dir>-workbench/research-workbench-input.jsonUse the JSON as the only factual source and write <run-dir>-workbench/research-workbench.md with:
Finally tell the user where the summary was saved. If the requested run directory is missing, ask them to run /vc-signals radar all first.
If the user wants a lead promoted into the weekly radar, first run or request real verification searches that return credible company/product URLs.
Trigger: /vc-signals theme "<topic>"
Read sectors.json and company_aliases.json.
Check if the topic maps to a known subcategory. If yes, use its seed_queries as starting points. If no, generate queries from scratch.
Use the same retrieval path selection as weekly scan (check last30days availability).
Run 5-8 targeted queries about the specific theme. Include:
last30days path (if available):
Run 3-5 queries through the adapter with auto-resolve enabled. Default lookback for theme drill-downs is 30 days. If the user appended a time window like 7d or 30d to the command, use that number's digits instead.
# Set LOOKBACK_DAYS once for this drill-down, then reuse across queries below.
LOOKBACK_DAYS=30 # theme drill-down default; override with the user's time-window digits if specified
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<topic-specific query>" --sources "reddit,hackernews,x" --auto-resolve --lookback-days ${LOOKBACK_DAYS}
Auto-resolve discovers the right subreddits, X handles, and GitHub context automatically for the theme.
Deep research (if OPENROUTER_API_KEY is configured):
For theme drill-downs, use the deep research mode for comprehensive synthesis with 50+ citations:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<topic> emerging trends companies" --deep-research --auto-resolve --lookback-days ${LOOKBACK_DAYS}
This uses Perplexity Sonar Pro (~$0.90 per query) to produce a structured research report with citations. If deep research is not available (no OPENROUTER_API_KEY), fall back to the standard multi-query approach above.
Also run GitHub trending for related keywords:
python3 <skill_dir>/scripts/github_trending.py --sector all --limit 10
(Filter results to those matching the theme in post-processing.)
## Theme Deep-Dive: [Topic]
### What Is This?
[2-3 sentence explanation of the theme for someone unfamiliar]
### Why Now?
[What changed recently that made this theme emerge? Be specific -- new tech, new problem, regulatory shift, etc.]
### Key Subthemes
- **Subtheme A:** [1-2 sentences]
- **Subtheme B:** [1-2 sentences]
- **Subtheme C:** [1-2 sentences]
### Evidence
- [Source 1: title, URL, key insight]
- [Source 2: title, URL, key insight]
- [Source 3+]
### Adjacent Categories
- [Related theme 1 -- how it connects]
- [Related theme 2 -- how it connects]
### Companies Solving the Problem
| Name | What They Do | Confidence | Source |
|------|-------------|------------|--------|
### Companies Benefiting From the Trend
| Name | How They Benefit | Confidence |
|------|-----------------|------------|
### Relevant OSS Projects
| Project | Stars | Velocity | Commercial Entity |
|---------|-------|----------|------------------|
### Durable vs Hype
[Blunt, honest assessment. 2-3 sentences. What could make this fade? What would confirm it's real?]
### Investment Implications
- **Timing:** Early/Mid/Late
- **What to watch for:** [Specific signals that would confirm or invalidate this theme]
- **Biggest risk:** [One sentence]
cat <<'MD_EOF' | python3 <skill_dir>/scripts/persistence.py save-markdown --subdir themes --name "<topic>" --date $(date +%Y-%m-%d)
[the markdown content goes here]
MD_EOF
Trigger: /vc-signals company "<name>"
Read company_aliases.json. Check if the company exists. If yes, note its known themes, sectors, and OSS projects.
Check if last30days is available (same as weekly scan Step 3). If available, use it for source-specific searches in Step 2.
Run 4-6 queries:
last30days path (if available):
Search for the company across sources with auto-resolve. Default lookback for company backtrace is 30 days. If the user appended a time window like 7d or 30d to the command, use that number's digits instead.
# Set LOOKBACK_DAYS once for this backtrace, then reuse across queries below.
LOOKBACK_DAYS=30 # company backtrace default; override with the user's time-window digits if specified
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<company name>" --sources "hackernews,reddit,x" --auto-resolve --quick --lookback-days ${LOOKBACK_DAYS}
If the company has known OSS projects from the seed map, also search for those.
GitHub deep search (if company has known GitHub org or repos):
If the company has known OSS projects from the seed map, search for the repo directly:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<company name>" --github-repo "<owner/repo>" --auto-resolve --quick --lookback-days ${LOOKBACK_DAYS}
If you can identify a founder's GitHub username, search their activity:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<founder name>" --github-user "<username>" --quick
X/Twitter deep search:
If the company or founder has a known X handle, search their timeline:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<company name>" --x-handle "<handle>" --sources "x" --quick
Check GitHub:
python3 <skill_dir>/scripts/github_trending.py --sector all --limit 30
Filter for repos owned by or related to the company.
Cross-reference the evidence against:
## Company Backtrace: [Company Name]
### Overview
[What the company does, 2-3 sentences]
### Theme Exposure
| Rising Theme | Role | Confidence | Evidence |
|-------------|------|------------|----------|
| Theme A | Direct solver | Confirmed | [Brief evidence] |
| Theme B | Beneficiary | Likely | [Brief evidence] |
### OSS / Ecosystem Signals
- [Project 1: stars, velocity, relevance]
- [Project 2 if applicable]
### Competitive Context
[Brief note on who else operates in the same themes. NOT a full competitive analysis -- just enough for context.]
### Confidence Notes
[What you're confident about, what's uncertain, what you couldn't verify]
cat <<'MD_EOF' | python3 <skill_dir>/scripts/persistence.py save-markdown --subdir companies --name "<company name>" --date $(date +%Y-%m-%d)
[the markdown content goes here]
MD_EOF
Trigger: /vc-signals oss <sector> [time]
Use this when the user asks for OSS startup discovery, fast-growing repos, open-source companies, or GitHub-first market signal. This mode is conceptually inspired by Gokul Rajaram's OSS Startup Radar, with permission to reuse ideas/code, but VC Signals should adapt it to Marathon's Seed-to-Series-B workflow.
Valid sectors: ai-infra, devtools, cybersecurity, data-infra, vertical-ai, all. If no sector is provided, default to ai-infra for OSS radar.
Use GitHub trending and last30days together:
python3 <skill_dir>/scripts/github_trending.py --sector <SECTOR> --limit 50
Then search community signal:
python3 <skill_dir>/scripts/last30days_adapter.py query --topic "<repo or theme>" --sources "reddit,hackernews,x,youtube,github" --github-repo "<owner/repo>" --quick --store --lookback-days 30
Do not rank by total stars alone. Score these separately:
Use the Alex/Gokul OSS email as the output bar: top themes, top 25 fast-growing projects, 30/60/90 velocity, community signal, funding/stage label, founder/contributor profiles, and clear methodology. Improve it by adding Investment Interest, Evidence Confidence, action, and "Why This May Be Noise."
Repo type must be one of:
company-backedfounder-likelyecosystem signalportfolio-support relevanthigh-noiseAction must be one of:
watchcontact maintainermap ecosystemtrack company formationignore## OSS Radar: {Sector} -- {YYYY-MM-DD}
### Top Themes
- **{Theme}** — {2 sentence why-now}. Key projects: `{repo}`, `{repo}`, `{repo}`.
### Top 25 OSS Projects
| Repo | Type | Action | Interest | Evidence | +30d | +60d | +90d | Community | Why Now | Why This May Be Noise |
|------|------|--------|----------|----------|------|------|------|-----------|---------|------------------------|
| owner/repo | founder-likely | contact maintainer | High | Medium | +1.5k | +4.9k | +4.9k | 2r+3hn | Agent memory is emerging | Star spike may be launch hype |
### Founder & Contributor Profiles
- **owner/repo** — top contributors, GitHub profiles, LinkedIn if found, caveat that identity should be verified before outreach.
### Methodology
Explain star velocity windows, community source weighting, funding/stage labeling, filtering, and star-authenticity caveats.
Trigger: /vc-signals github <sector>
python3 <skill_dir>/scripts/github_trending.py --sector <SECTOR> --limit 15
If sector is all, run for each sector and merge results.
For each repo in the results:
company_aliases.json -- is the owner/org a known company?For each repo, identify which sector themes it relates to. Use the taxonomy subcategories and your judgment.
## GitHub Trending: [Sector] -- YYYY-MM-DD
Repos ranked by star velocity (recent growth rate, not absolute count).
| # | Repo | Stars | 7d Growth | 30d Growth | Language | Commercial Entity |
|---|------|-------|-----------|------------|----------|------------------|
| 1 | owner/name | 12,500 | +450 | +1,800 | Rust | Company Name (Confirmed) |
| 2 | ... | | | | | |
### Standout Repos
**[Repo 1: owner/name]**
- **Description:** [What it does]
- **Why it's interesting:** [1-2 sentences -- what's driving the growth?]
- **Theme mapping:** [Which sector themes this relates to]
- **Commercial entity:** [Company behind it, if any. Monetization status if known.]
**[Repo 2: owner/name]**
...
### Acceleration Alerts
[Repos with unusually high velocity relative to their size -- the "0 to 10k in a week" signals]
### Theme Patterns
[Do multiple trending repos point to the same emerging theme? Call it out.]
cat <<'MD_EOF' | python3 <skill_dir>/scripts/persistence.py save-markdown --subdir github --slug <SECTOR> --date $(date +%Y-%m-%d)
[the markdown content goes here]
MD_EOF
Trigger: /vc-signals add-sector <name> (e.g., /vc-signals add-sector fintech)
This mode lets users add a new sector without editing JSON manually.
Ask the user for:
Based on the sector name, propose 4-6 subcategories. For example, if the user says "fintech":
"Here are the subcategories I'd suggest for Fintech:
- Payments Infrastructure
- Neobanking & Digital Banking
- Lending & Credit Platforms
- Regtech & Compliance
- Embedded Finance
- Crypto & DeFi Infrastructure
Want to add, remove, or rename any of these?"
For each subcategory, generate:
name: display namealiases: 4-6 relevant search termsseed_queries: 2-3 search queriesAlso generate:
subreddits: 5-8 relevant subreddits for the sectorhn_queries: 4-6 HN-optimized short queriesdiscovery_queries: 4-6 broad discovery queriesnegative_terms: 3-5 noise filtersShow the user the complete sector JSON and ask for confirmation. Then:
cat <skill_dir>/config/sectors.json
Read the current config, add the new sector, and write it back:
python3 -c "
import json
from pathlib import Path
config_path = Path('<skill_dir>/config/sectors.json')
config = json.loads(config_path.read_text())
config['<sector_slug>'] = <new_sector_dict>
config_path.write_text(json.dumps(config, indent=2))
print('Sector added successfully')
"
"Added with subcategories. Try it out:
/vc-signals weekly <sector-slug>"
At every step, handle failures gracefully:
/vc-signals setup to configure one." Still run the rest of the scan.Never crash. Never pretend you have data you don't. Always tell the user what worked and what didn't.
[HINT] Download the complete skill directory including SKILL.md and all related files