# Researcher

> The single idea-generation pipeline for marketing content. Three input modes converging on Notion Status=Idea rows. Trend scan (X + LinkedIn creators + topic search) finds what's hot in the world. Performance scan (your own published rows + their engagement metrics) finds what's working for you so you can double down. Customer scan (HUMAN NOTES DB user-interview transcripts) finds the words, pain…

- **Type:** Skill
- **Install:** `agentstack add skill-timscheuerai-content-vault-researcher`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [timscheuerai](https://agentstack.voostack.com/s/timscheuerai)
- **Installs:** 0
- **Category:** [Data & Analytics](https://agentstack.voostack.com/c/data-and-analytics)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [timscheuerai](https://github.com/timscheuerai)
- **Source:** https://github.com/timscheuerai/content-vault/tree/main/skills/researcher

## Install

```sh
agentstack add skill-timscheuerai-content-vault-researcher
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# researcher

The single discovery pipeline. Three idea sources, one Notion output.
Use this instead of separate /performance-check or /listening skills ·
they're folded in as modes here.

## When to use

Trigger on:
- "Find me content ideas"
- "What's hot on X / LinkedIn"
- "What's working for me"
- "Pull from my user interviews"
- "What are customers saying"
- "Scan the GTM space"
- "Give me ten posts to write next week"
- User explicitly types `/researcher`
- Your Notion Content DB has ; do
  curl -s "https://${UNIPILE_DSN}/api/v1/users/${pid}/posts?account_id=${UNIPILE_ACCOUNT_ID}&limit=20" \
    -H "X-API-KEY: ${UNIPILE_API_KEY}" \
    -H "accept: application/json" \
    -o /tmp/researcher/li_${pid}.json
done
```

LinkedIn engagement score:
```
score = reaction_counter + 3*comment_counter + 5*share_counter
```

If a seed account in `seed-accounts.md` has profile ID `TBD`, resolve
it on first use:

```bash
curl -s "https://${UNIPILE_DSN}/api/v1/users?account_id=${UNIPILE_ACCOUNT_ID}&keyword=&limit=5" \
  -H "X-API-KEY: ${UNIPILE_API_KEY}"
```

Pick the right profile, copy the ID into `seed-accounts.md` (commit),
continue.

#### Performance scan · your own content

Step 1: query the Notion Content DB for your recent Published rows.

```
Use mcp__claude_ai_Notion__notion-search with:
  data_source_url: collection://
  query: ""
  page_size: 25
  filters: { created_date_range: { start_date: "" } }
```

Filter to rows where `Status = Published` and `Live URL` is non-empty.

Step 2: fetch metrics per Live URL.

For LinkedIn URLs (pattern `linkedin.com/posts/...`):

```bash
# Fetch your own posts (your profile ID is cached)
curl -s "https://${UNIPILE_DSN}/api/v1/users//posts?account_id=${UNIPILE_ACCOUNT_ID}&limit=50" \
  -H "X-API-KEY: ${UNIPILE_API_KEY}" \
  -H "accept: application/json" \
  -o /tmp/researcher/li_self.json
```

Match each Notion row's Live URL against the response · use the
post's `reaction_counter`, `comment_counter`, `share_counter`.

For X URLs (pattern `x.com//status/`):

```bash
# Extract tweet IDs from URLs
TWEET_IDS="1234567890,2345678901,..."  # comma-separated
curl -s --get "https://api.twitter.com/2/tweets" \
  --data-urlencode "ids=${TWEET_IDS}" \
  --data-urlencode "tweet.fields=public_metrics,created_at" \
  -H "Authorization: Bearer ${X_BEARER_TOKEN}"
```

If your X user ID is not yet cached in `seed-accounts.md`, the tweet
lookup-by-ID still works without it · we just can't filter to your
own without the ID. Note as soft-blocker if it limits the run.

Step 3: rank and pick.

Sort by engagement score (X formula above for tweets, LinkedIn
formula for LI posts). Top 5 are double-down candidates. Bottom 3
are flop diagnostics (what didn't work · note for avoidance, not for
ideation).

Step 4: generate double-down angles.

For each top-5 row:
- **Follow-up**: a continuation post answering "and then what
  happened?" or "the next step after X"
- **Deeper dive**: pick one bullet from the original and make it the
  whole post
- **Counter-take**: argue the opposite of the original (works if
  the original was a popular take · creates contrast)
- **Format flip**: original was text → carousel; original was post → thread; etc.

Pick whichever fits the source. Multi-angle per row is fine if rich
material exists.

#### Customer scan · HUMAN NOTES DB

Query the user-interview rows.

```
Use mcp__claude_ai_Notion__notion-search with:
  data_source_url: collection://
  query: ""
  page_size: 25
  filters: { created_date_range: { start_date: "" } }
```

Filter the response to `Category in ("User Interview Meeting",
"External Meeting")`. Skip Co-Founder Meeting, Dev Notes, Reading
List, Application categories · those aren't customer signal.

For each surviving row, fetch the page body via
`mcp__claude_ai_Notion__notion-fetch` with the row ID. The body
contains the transcript / notes.

For each interview, extract:
- **Pain points**: 2-4 bullets describing what's broken in the
  customer's world (in their words, not yours)
- **Direct quotes**: 1-3 quotable lines (verbatim · with attribution
  to interview date and customer if not under NDA)
- **Recurring themes**: themes that show up across multiple
  interviews (note which · a theme in 1 interview is anecdote, in
  3+ it's signal)

For each recurring theme (3+ occurrences), generate an angle:
- The pain stated as a question you have the answer to
- A framework that resolves the pain
- A contrarian take on common-but-wrong solutions in that space
- A customer-quote post (the verbatim line as the hook)

### 3. Filter (all modes)

- Drop posts older than the time window
- Drop banned-signal posts (see `seed-accounts.md` § "Banned signals")
- Drop URL-only / image-only posts with no text body
- Drop posts under the engagement floor (trend mode only · 50 likes
  on X · 100 reactions on LinkedIn)
- For customer scan: drop interviews under 5 minutes (likely no-shows
  or aborted) and skip the ones marked "private · do not use"

### 4. Cluster + angle (all modes)

Read all surviving inputs. Group by theme · expect 5-10. For each
theme, pick the strongest representative (highest engagement for
trends, top performer for performance, most-quoted for customer)
and write:

- **Angle**: a one-sentence hook you could use, in your voice (terse,
  contrarian, concrete, direct, no hype)
- **Why it's hot / working / real**: one sentence on why this matters
- **Suggested Pillar**: pick one of the six (`Building in Public`,
  `Educational / Tactical`, `Personal`, `Memes`, `Promotional`,
  `Trend Insights`)
- **Suggested Format + Channel**: usually `Text` on `LinkedIn` or `X`
  · sometimes `Long-form Article` for richer themes · sometimes
  `Lead Magnet` for customer-pain frameworks

### 5. Create Notion rows

For each idea card, call
`mcp__claude_ai_Notion__notion-create-pages` with data source
``. Properties:

- **Title**: the one-sentence angle (max ~80 chars)
- **Status**: `Idea`
- **Pillar**: suggested multi-select (single value usually)
- **Format**: suggested
- **Channel**: suggested

Page body, branched by mode:

#### Trend mode body
```
Source: 
Mode: trend
Author: 
Engagement: 
Window: 

— Original post —
> 

— Why this is hot —

— Your angle —

```

#### Performance mode body
```
Source: 
Mode: performance · double-down
Original metrics: 
Pillar of original: 

— Original post —
> 

— Why this performed —

— Double-down angle —

```

#### Customer mode body
```
Source: 
Mode: customer · pain-point
Interview date(s): 
Customer(s): 
Theme recurrence:  interviews

— Customer quote(s) —
> ""
> ""

— The pain —

— Your angle —

```

### 6. Report back

One-line summary by mode plus the Notion URLs of created rows.
Example:

```
Created 10 ideas (4 trend, 3 performance double-down, 3 customer-pain).
Pipeline view: 

Trend
  -  → notion.so/...
  -  → notion.so/...
  ...
Performance double-down
  -  (riff on "" · 47 reactions) → notion.so/...
  ...
Customer pain
  -  (3 interviews · A, B, C) → notion.so/...
  ...
```

If any mode soft-blocked (e.g. X user ID missing), mention it in the
report.

## Defaults

| Knob                        | Default            |
| --------------------------- | ------------------ |
| Mode                        | Hybrid             |
| Time window (trend)         | 7 days             |
| Time window (performance)   | 30 days            |
| Time window (customer)      | 60 days            |
| Cards to create             | 10                 |
| Trend / performance / customer ratio (hybrid) | 4 / 3 / 3 |
| X account scan              | 3 cached creators  |
| LinkedIn account scan       | Your profile + curated list as IDs are resolved |
| Topic seeds                 | from `seed-accounts.md` |
| Engagement floor (X trend)  | 50 likes           |
| Engagement floor (LI trend) | 100 reactions      |
| Customer category filter    | `User Interview Meeting` OR `External Meeting` |

## Don'ts

- **Don't paraphrase the original post into your voice as the Title.**
  The Title is the angle you would write · derived from the post, not
  copied. The original post text goes in the page body.
- **Don't surface 30 ideas.** Cap at 10-15 default. More creates
  fatigue, not optionality.
- **Don't skip the engagement floor on trend mode.** Low-engagement
  posts on these topics are usually slop, even from good accounts.
- **Don't auto-spawn drafts.** Status=Idea means just-an-idea.
  Drafting is a separate step.
- **Don't pollute the DB with duplicates.** Before creating, do a
  quick search of recent Idea rows for the same theme. If a similar
  idea exists, append the new source as another bullet in the
  existing row's body instead of creating a duplicate.
- **Don't fabricate engagement numbers.** If the API call fails or
  returns empty, say so · don't fill in plausible-looking metrics.
- **Don't quote customers under NDA verbatim.** If the interview
  notes mark a section private / off-record, paraphrase it in the
  pain framing and skip the direct quote. When in doubt, ask.
- **Don't crawl outside X + LinkedIn + Notion.** This skill stays
  scoped. For blog / podcast / YouTube discovery, use `WebSearch`
  separately.
- **Don't over-double-down on one performance winner.** If the same
  post drives 5 angles, you'll oversaturate one theme. Cap at 2
  double-downs per source row.

## See also

- Notion Content DB data source: ``
- Notion HUMAN NOTES DB data source: ``
- Seed accounts + topic list: [`seed-accounts.md`](./seed-accounts.md)
- API auth env: `./.env` · `` (X) · ``,
  ``, `` (LinkedIn)
- Downstream skills: `/linkedin-copywriter`, `/x-copywriter`,
  `/repurpose`, `/lead-magnet-creator`, `/long-form`,
  `/youtube-script`

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [timscheuerai](https://github.com/timscheuerai)
- **Source:** [timscheuerai/content-vault](https://github.com/timscheuerai/content-vault)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-timscheuerai-content-vault-researcher
- Seller: https://agentstack.voostack.com/s/timscheuerai
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
