Skill: johnsondevops/last30days-skill (version 1.0.0)

--- name: last30days version: "2.1" description: "Research a topic from the last 30 days. Also triggered by 'last30'. Sources: Reddit, X, YouTube, web. Become an expert and write copy-paste-ready prompts." argument-hint: 'last30 AI video tools, last30 best project management tools' allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch homepage: https://github.com/mvanhorn/last30days-skill user-invocable: true disable-model-invocation: true metadata: clawdbot: emoji: "📰" requires: env: - OPENAI_API_KEY bins: - node - python3 primaryEnv: OPENAI_API_KEY files: - "scripts/*" homepage: https://github.com/mvanhorn/last30days-skill tags: - research - reddit - x - youtube - trends - prompts --- # last30days v2.1: Research Any Topic from the Last 30 Days Research ANY topic across Reddit, X, YouTube, and the web. Surface what people are actually discussing, recommending, and debating right now. ## CRITICAL: Parse User Intent Before doing anything, parse the user's input for: 1. **TOPIC**: What they want to learn about (e.g., "web app mockups", "Claude Code skills", "image generation") 2. **TARGET TOOL** (if specified): Where they'll use the prompts (e.g., "Nano Banana Pro", "ChatGPT", "Midjourney") 3. **QUERY TYPE**: What kind of research they want: - **PROMPTING** - "X prompts", "prompting for X", "X best practices" → User wants to learn techniques and get copy-paste prompts - **RECOMMENDATIONS** - "best X", "top X", "what X should I use", "recommended X" → User wants a LIST of specific things - **NEWS** - "what's happening with X", "X news", "latest on X" → User wants current events/updates - **GENERAL** - anything else → User wants broad understanding of the topic Common patterns: - `[topic] for [tool]` → "web mockups for Nano Banana Pro" → TOOL IS SPECIFIED - `[topic] prompts for [tool]` → "UI design prompts for Midjourney" → TOOL IS SPECIFIED - Just `[topic]` → "iOS design mockups" → TOOL NOT SPECIFIED, that's OK - "best [topic]" or "top [topic]" → QUERY_TYPE = RECOMMENDATIONS - "what are the best [topic]" → QUERY_TYPE = RECOMMENDATIONS **IMPORTANT: Do NOT ask about target tool before research.** - If tool is specified in the query, use it - If tool is NOT specified, run research first, then ask AFTER showing results **Store these variables:** - `TOPIC = [extracted topic]` - `TARGET_TOOL = [extracted tool, or "unknown" if not specified]` - `QUERY_TYPE = [RECOMMENDATIONS | NEWS | HOW-TO | GENERAL]` **DISPLAY your parsing to the user.** Before running any tools, output: ``` I'll research {TOPIC} across Reddit, X, and the web to find what's been discussed in the last 30 days. Parsed intent: - TOPIC = {TOPIC} - TARGET_TOOL = {TARGET_TOOL or "unknown"} - QUERY_TYPE = {QUERY_TYPE} Research typically takes 2-8 minutes (niche topics take longer). Starting now. ``` If TARGET_TOOL is known, mention it in the intro: "...to find {QUERY_TYPE}-style content for use in {TARGET_TOOL}." This text MUST appear before you call any tools. It confirms to the user that you understood their request. --- ## Research Execution **Step 1: Run the research script (FOREGROUND — do NOT background this)** **CRITICAL: Run this command in the FOREGROUND with a 5-minute timeout. Do NOT use run_in_background. The full output contains Reddit, X, AND YouTube data that you need to read completely.** ```bash # Find skill root — works in repo checkout, Claude Code, or Codex install for dir in \ "." \ "${CLAUDE_PLUGIN_ROOT:-}" \ "$HOME/.claude/skills/last30days" \ "$HOME/.agents/skills/last30days" \ "$HOME/.codex/skills/last30days"; do [ -n "$dir" ] && [ -f "$dir/scripts/last30days.py" ] && SKILL_ROOT="$dir" && break done if [ -z "${SKILL_ROOT:-}" ]; then echo "ERROR: Could not find scripts/last30days.py" >&2 exit 1 fi python3 "${SKILL_ROOT}/scripts/last30days.py" "$ARGUMENTS" --emit=compact ``` Use a **timeout of 300000** (5 minutes) on the Bash call. The script typically takes 1-3 minutes. The script will automatically: - Detect available API keys - Run Reddit/X/YouTube searches - Output ALL results including YouTube transcripts **Read the ENTIRE output.** It contains THREE data sections in this order: Reddit items, X items, and YouTube items. If you miss the YouTube section, you will produce incomplete stats. **YouTube items in the output look like:** `**{video_id}** (score:N) {channel_name} [N views, N likes]` followed by a title, URL, and optional transcript snippet. Count them and include them in your synthesis and stats block. --- ## STEP 2: DO WEBSEARCH AFTER SCRIPT COMPLETES After the script finishes, do WebSearch to supplement with blogs, tutorials, and news. For **ALL modes**, do WebSearch to supplement (or provide all data in web-only mode). Choose search queries based on QUERY_TYPE: **If RECOMMENDATIONS** ("best X", "top X", "what X should I use"): - Search for: `best {TOPIC} recommendations` - Search for: `{TOPIC} list examples` - Search for: `most popular {TOPIC}` - Goal: Find SPECIFIC NAMES of things, not generic advice **If NEWS** ("what's happening with X", "X news"): - Search for: `{TOPIC} news 2026` - Search for: `{TOPIC} announcement update` - Goal: Find current events and recent developments **If PROMPTING** ("X prompts", "prompting for X"): - Search for: `{TOPIC} prompts examples 2026` - Search for: `{TOPIC} techniques tips` - Goal: Find prompting techniques and examples to create copy-paste prompts **If GENERAL** (default): - Search for: `{TOPIC} 2026` - Search for: `{TOPIC} discussion` - Goal: Find what people are actually saying For ALL query types: - **USE THE USER'S EXACT TERMINOLOGY** - don't substitute or add tech names based on your knowledge - EXCLUDE reddit.com, x.com, twitter.com (covered by script) - INCLUDE: blogs, tutorials, docs, news, GitHub repos - **DO NOT output "Sources:" list** - this is noise, we'll show stats at the end **Options** (passed through from user's command): - `--days=N` → Look back N days instead of 30 (e.g., `--days=7` for weekly roundup) - `--quick` → Faster, fewer sources (8-12 each) - (default) → Balanced (20-30 each) - `--deep` → Comprehensive (50-70 Reddit, 40-60 X) --- ## Judge Agent: Synthesize All Sources **After all searches complete, internally synthesize (don't display stats yet):** The Judge Agent must: 1. Weight Reddit/X sources HIGHER (they have engagement signals: upvotes, likes) 2. Weight YouTube sources HIGH (they have views, likes, and transcript content) 3. Weight WebSearch sources LOWER (no engagement data) 4. Identify patterns that appear across ALL sources (strongest signals) 5. Note any contradictions between sources 6. Extract the top 3-5 actionable insights **Do NOT display stats here - they come at the end, right before the invitation.** --- ## FIRST: Internalize the Research **CRITICAL: Ground your synthesis in the ACTUAL research content, not your pre-existing knowledge.** Read the research output carefully. Pay attention to: - **Exact product/tool names** mentioned (e.g., if research mentions "ClawdBot" or "@clawdbot", that's a DIFFERENT product than "Claude Code" - don't conflate them) - **Specific quotes and insights** from the sources - use THESE, not generic knowledge - **What the sources actually say**, not what you assume the topic is about **ANTI-PATTERN TO AVOID**: If user asks about "clawdbot skills" and research returns ClawdBot content (self-hosted AI agent), do NOT synthesize this as "Claude Code skills" just because both involve "skills". Read what the research actually says. ### If QUERY_TYPE = RECOMMENDATIONS **CRITICAL: Extract SPECIFIC NAMES, not generic patterns.** When user asks "best X" or "top X", they want a LIST of specific things: - Scan research for specific product names, tool names, project names, skill names, etc. - Count how many times each is mentioned - Note which sources recommend each (Reddit thread, X post, blog) - List them by popularity/mention count **BAD synthesis for "best Claude Code skills":** > "Skills are powerful. Keep them under 500 lines. Use progressive disclosure." **GOOD synthesis for "best Claude Code skills":** > "Most mentioned skills: /commit (5 mentions), remotion skill (4x), git-worktree (3x), /pr (3x). The Remotion announcement got 16K likes on X." ### For all QUERY_TYPEs Identify from the ACTUAL RESEARCH OUTPUT: - **PROMPT FORMAT** - Does research recommend JSON, structured params, natural language, keywords? - The top 3-5 patterns/techniques that appeared across multiple sources - Specific keywords, structures, or approaches mentioned BY THE SOURCES - Common pitfalls mentioned BY THE SOURCES --- ## THEN: Show Summary + Invite Vision **Display in this EXACT sequence:** **FIRST - What I learned (based on QUERY_TYPE):** **If RECOMMENDATIONS** - Show specific things mentioned with sources: ``` 🏆 Most mentioned: [Tool Name] - {n}x mentions Use Case: [what it does] Sources: @handle1, @handle2, r/sub, blog.com [Tool Name] - {n}x mentions Use Case: [what it does] Sources: @handle3, r/sub2, Complex Notable mentions: [other specific things with 1-2 mentions] ``` **CRITICAL for RECOMMENDATIONS:** - Each item MUST have a "Sources:" line with actual @handles from X posts (e.g., @LONGLIVE47, @ByDobson) - Include subreddit names (r/hiphopheads) and web sources (Complex, Variety) - Parse @handles from research output and include the highest-engagement ones - Format naturally - tables work well for wide terminals, stacked cards for narrow **If PROMPTING/NEWS/GENERAL** - Show synthesis and patterns: CITATION RULE: Cite sources sparingly to prove research is real. - In the "What I learned" intro: cite 1-2 top sources total, not every sentence - In KEY PATTERNS: cite 1 source per pattern, short format: "per @handle" or "per r/sub" - Do NOT include engagement metrics in citations (likes, upvotes) - save those for stats box - Do NOT chain multiple citations: "per @x, @y, @z" is too much. Pick the strongest one. CITATION PRIORITY (most to least preferred): 1. @handles from X — "per @handle" (these prove the tool's unique value) 2. r/subreddits from Reddit — "per r/subreddit" 3. YouTube channels — "per [channel name] on YouTube" (transcript-backed insights) 4. Web sources — ONLY when Reddit/X/YouTube don't cover that specific fact The tool's value is surfacing what PEOPLE are saying, not what journalists wrote. When both a web article and an X post cover the same fact, cite the X post. URL FORMATTING: NEVER paste raw URLs in the output. - **BAD:** "per https://www.rollingstone.com/music/music-news/kanye-west-bully-1235506094/" - **GOOD:** "per Rolling Stone" - **GOOD:** "per Complex" Use the publication name, not the URL. The user doesn't need links — they need clean, readable text. **BAD:** "His album is set for March 20 (per Rolling Stone; Billboard; Complex)." **GOOD:** "His album BULLY drops March 20 — fans on X are split on the tracklist, per @honest30bgfan_" **GOOD:** "Ye's apology got massive traction on r/hiphopheads" **OK** (web, only when Reddit/X don't have it): "The Hellwatt Festival runs July 4-18 at RCF Arena, per Billboard" **Lead with people, not publications.** Start each topic with what Reddit/X users are saying/feeling, then add web context only if needed. The user came here for the conversation, not the press release. ``` What I learned: **{Topic 1}** — [1-2 sentences about what people are saying, per @handle or r/sub] **{Topic 2}** — [1-2 sentences, per @handle or r/sub] **{Topic 3}** — [1-2 sentences, per @handle or r/sub] KEY PATTERNS from the research: 1. [Pattern] — per @handle 2. [Pattern] — per r/sub 3. [Pattern] — per @handle ``` **THEN - Stats (right before invitation):** **CRITICAL: Calculate actual totals from the research output.** - Count posts/threads from each section - Sum engagement: parse `[Xlikes, Yrt]` from each X post, `[Xpts, Ycmt]` from Reddit - Identify top voices: highest-engagement @handles from X, most active subreddits **Copy this EXACTLY, replacing only the {placeholders}:** ``` --- ✅ All agents reported back! ├─ 🟠 Reddit: {N} threads │ {N} upvotes │ {N} comments ├─ 🔵 X: {N} posts │ {N} likes │ {N} reposts ├─ 🔴 YouTube: {N} videos │ {N} views │ {N} with transcripts ├─ 🌐 Web: {N} pages (supplementary) └─ 🗣️ Top voices: @{handle1} ({N} likes), @{handle2} │ r/{sub1}, r/{sub2} --- ``` If Reddit returned 0 threads, write: "├─ 🟠 Reddit: 0 threads (no results this cycle)" If YouTube returned 0 videos or yt-dlp is not installed, omit the YouTube line entirely. NEVER use plain text dashes (-) or pipe (|). ALWAYS use ├─ └─ │ and the emoji. **SELF-CHECK before displaying**: Re-read your "What I learned" section. Does it match what the research ACTUALLY says? If you catch yourself projecting your own knowledge instead of the research, rewrite it. **LAST - Invitation (adapt to QUERY_TYPE):** **CRITICAL: Every invitation MUST include 2-3 specific example suggestions based on what you ACTUALLY learned from the research.** Don't be generic — show the user you absorbed the content by referencing real things from the results. **If QUERY_TYPE = PROMPTING:** ``` --- I'm now an expert on {TOPIC} for {TARGET_TOOL}. What do you want to make? For example: - [specific idea based on popular technique from research] - [specific idea based on trending style/approach from research] - [specific idea riffing on what people are actually creating] Just describe your vision and I'll write a prompt you can paste straight into {TARGET_TOOL}. ``` **If QUERY_TYPE = RECOMMENDATIONS:** ``` --- I'm now an expert on {TOPIC}. Want me to go deeper? For example: - [Compare specific item A vs item B from the results] - [Explain why item C is trending right now] - [Help you get started with item D] ``` **If QUERY_TYPE = NEWS:** ``` --- I'm now an expert on {TOPIC}. Some things you could ask: - [Specific follow-up question about the biggest story] - [Question about implications of a key development] - [Question about what might happen next based on current trajectory] ``` **If QUERY_TYPE = GENERAL:** ``` --- I'm now an expert on {TOPIC}. Some things I can help with: - [Specific question based on the most discussed aspect] - [Specific creative/practical application of what you learned] - [Deeper dive into a pattern or debate from the research] ``` **Example invitations (to show the quality bar):** For `/last30days nano banana pro prompts for Gemini`: > I'm now an expert on Nano Banana Pro for Gemini. What do you want to make? For example: > - Photorealistic product shots with natural lighting (the most requested style right now) > - Logo designs with embedded text (Gemini's new strength per the research) > - Multi-reference style transfer from a mood board > > Just describe your vision and I'll write a prompt you can paste straight into Gemini. For `/last30days kanye west` (GENERAL): > I'm now an expert on Kanye West. Some things I can help with: > - What's the real story behind the apology letter — genuine or PR move? > - Break down the BULLY tracklist reactions and what fans are expecting > - Compare how Reddit vs X are reacting to the Bianca narrative For `/last30days war in Iran` (NEWS): > I'm now an expert on the Iran situation. Some things you could ask: > - What are the realistic escalation scenarios from here? > - How is this playing differently in US vs international media? > - What's the economic impact on oil markets so far? --- ## WAIT FOR USER'S RESPONSE After showing the stats summary with your invitation, **STOP and wait** for the user to respond. --- ## WHEN USER RESPONDS **Read their response and match the intent:** - If they ask a **QUESTION** about the topic → Answer from your research (no new searches, no prompt) - If they ask to **GO DEEPER** on a subtopic → Elaborate using your research findings - If they describe something they want to **CREATE** → Write ONE perfect prompt (see below) - If they ask for a **PROMPT** explicitly → Write ONE perfect prompt (see below) **Only write a prompt when the user wants one.** Don't force a prompt on someone who asked "what could happen next with Iran." ### Writing a Prompt When the user wants a prompt, write a **single, highly-tailored prompt** using your research expertise. ### CRITICAL: Match the FORMAT the research recommends **If research says to use a specific prompt FORMAT, YOU MUST USE THAT FORMAT.** **ANTI-PATTERN**: Research says "use JSON prompts with device specs" but you write plain prose. This defeats the entire purpose of the research. ### Quality Checklist (run before delivering): - [ ] **FORMAT MATCHES RESEARCH** - If research said JSON/structured/etc, prompt IS that format - [ ] Directly addresses what the user said they want to create - [ ] Uses specific patterns/keywords discovered in research - [ ] Ready to paste with zero edits (or minimal [PLACEHOLDERS] clearly marked) - [ ] Appropriate length and style for TARGET_TOOL ### Output Format: ``` Here's your prompt for {TARGET_TOOL}: --- [The actual prompt IN THE FORMAT THE RESEARCH RECOMMENDS] --- This uses [brief 1-line explanation of what research insight you applied]. ``` --- ## IF USER ASKS FOR MORE OPTIONS Only if they ask for alternatives or more prompts, provide 2-3 variations. Don't dump a prompt pack unless requested. --- ## AFTER EACH PROMPT: Stay in Expert Mode After delivering a prompt, offer to write more: > Want another prompt? Just tell me what you're creating next. --- ## CONTEXT MEMORY For the rest of this conversation, remember: - **TOPIC**: {topic} - **TARGET_TOOL**: {tool} - **KEY PATTERNS**: {list the top 3-5 patterns you learned} - **RESEARCH FINDINGS**: The key facts and insights from the research **CRITICAL: After research is complete, you are now an EXPERT on this topic.** When the user asks follow-up questions: - **DO NOT run new WebSearches** - you already have the research - **Answer from what you learned** - cite the Reddit threads, X posts, and web sources - **If they ask a question** - answer it from your research findings - **If they ask for a prompt** - write one using your expertise Only do new research if the user explicitly asks about a DIFFERENT topic. --- ## Output Summary Footer (After Each Prompt) After delivering a prompt, end with: ``` --- 📚 Expert in: {TOPIC} for {TARGET_TOOL} 📊 Based on: {n} Reddit threads ({sum} upvotes) + {n} X posts ({sum} likes) + {n} YouTube videos ({sum} views) + {n} web pages Want another prompt? Just tell me what you're creating next. ``` --- ## Security & Permissions **What this skill does:** - Sends search queries to OpenAI's Responses API (`api.openai.com`) for Reddit discovery - Sends search queries to Twitter's GraphQL API (via browser cookie auth) or xAI's API (`api.x.ai`) for X search - Runs `yt-dlp` locally for YouTube search and transcript extraction (no API key, public data) - Optionally sends search queries to Brave Search API, Parallel AI API, or OpenRouter API for web search - Fetches public Reddit thread data from `reddit.com` for engagement metrics - Stores research findings in local SQLite database (watchlist mode only) **What this skill does NOT do:** - Does not post, like, or modify content on any platform - Does not access your Reddit, X, or YouTube accounts - Does not share API keys between providers (OpenAI key only goes to api.openai.com, etc.) - Does not log, cache, or write API keys to output files - Does not send data to any endpoint not listed above - Cannot be invoked autonomously by the agent (`disable-model-invocation: true`) **Bundled scripts:** `scripts/last30days.py` (main research engine), `scripts/lib/` (search, enrichment, rendering modules), `scripts/lib/vendor/bird-search/` (vendored X search client, MIT licensed) Review scripts before first use to verify behavior.

path: CHANGELOG.md
content: # Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.0] - 2026-02-15

### Highlights

Three headline features: watchlists for always-on bots, YouTube transcripts as a 4th source, and Codex CLI compatibility. Plus bundled X search with no external CLI needed.

### Added

- Open-class skill with watchlists, briefings, and history modes (SQLite-backed, FTS5 full-text search, WAL mode) (`feat(open)`)
- YouTube as a 4th research source via yt-dlp -- search, view counts, and auto-generated transcript extraction (`feat: Add YouTube`)
- OpenAI Codex CLI compatibility -- install to `~/.agents/skills/last30days`, invoke with `$last30days` (`feat: Add Codex CLI`)
- Bundled X search -- vendored subset of Bird's Twitter GraphQL client (MIT, originally by @steipete), no external CLI needed (`v2.1: Bundle Bird X search`)
- Native web search backends: Parallel AI, Brave Search, OpenRouter/Perplexity Sonar Pro (`feat(engine)`)
- `--diagnose` flag for checking available sources and authentication status
- `--store` flag for SQLite accumulation (open variant)
- Conversational first-run experience (NUX) with dynamic source status (`feat(nux)`)

### Changed

- Smarter query construction -- strips noise words, auto-retries with shorter queries when X returns 0 results
- Two-phase search architecture -- Phase 1 discovers entities (@handles, r/subreddits), Phase 2 drills into them
- Reddit JSON enrichment -- real upvotes, comments, and upvote ratio from reddit.com/.json endpoint
- Engagement-weighted scoring: relevance 45%, recency 25%, engagement 30% (log1p dampening)
- Model auto-selection with 7-day cache and fallback chain (gpt-4.1 -> gpt-4o -> gpt-4o-mini)
- `--days=N` configurable lookback flag (thanks @jonthebeef, [#18](https://github.com/mvanhorn/last30days-skill/pull/18))
- Model fallback for unverified orgs (thanks @levineam, [#16](https://github.com/mvanhorn/last30days-skill/pull/16))
- Marketplace plugin support via `.claude-plugin/plugin.json` (inspired by @galligan, [#1](https://github.com/mvanhorn/last30days-skill/pull/1))

### Fixed

- YouTube timeout increased to 90s, Reddit 429 rate limit fail-fast
- YouTube soft date filter -- keeps evergreen content instead of filtering to 0 results
- Eager import crash in `__init__.py` that broke Codex environments
- Reddit future timeout (same pattern as YouTube timeout bug)
- Process cleanup on timeout/kill -- tracks child PIDs for clean shutdown
- Windows Unicode fix for cp1252 emoji crash (thanks @JosephOIbrahim, [#17](https://github.com/mvanhorn/last30days-skill/pull/17))
- X search returning 0 results on popular topics due to over-specific queries

### New Contributors

- @JosephOIbrahim -- Windows Unicode fix ([#17](https://github.com/mvanhorn/last30days-skill/pull/17))
- @levineam -- Model fallback for unverified orgs ([#16](https://github.com/mvanhorn/last30days-skill/pull/16))
- @jonthebeef -- `--days=N` configurable lookback ([#18](https://github.com/mvanhorn/last30days-skill/pull/18))

### Credits

- @steipete -- Bird CLI (vendored X search) and yt-dlp/summarize inspiration for YouTube transcripts
- @galligan -- Marketplace plugin inspiration
- @hutchins -- Pushed for YouTube feature

## [1.0.0] - 2026-01-15

Initial public release. Reddit + X search via OpenAI Responses API and xAI API.

[2.1.0]: https://github.com/mvanhorn/last30days-skill/compare/v1.0.0...v2.1.0
[1.0.0]: https://github.com/mvanhorn/last30days-skill/releases/tag/v1.0.0


path: README.md
content: # /last30days v2.1

**The AI world reinvents itself every month. This skill keeps you current.** /last30days researches your topic across Reddit, X, YouTube, and the web from the last 30 days, finds what the community is actually upvoting, sharing, and saying on camera, and writes you a prompt that works today, not six months ago. Whether it's Seedance 2.0 access, Suno music prompts, or the latest Nano Banana Pro techniques, you'll prompt like someone who's been paying attention.

**New in V2.1  - three headline features:**

1. **Open-class skill with watchlists.** Add any topic to a watchlist  - your competitors, specific people, emerging technologies  - and /last30days re-researches it on demand or via cron. Designed for always-on environments like [Open Claw](https://github.com/openclaw/openclaw) where a bot can run research on a schedule and accumulate findings over time.
2. **YouTube transcripts as a 4th source.** When yt-dlp is installed, /last30days automatically searches YouTube, grabs view counts, and extracts auto-generated transcripts from the top videos. A 20-minute review contains 10x the signal of a single post - now the skill reads it. Inspired by [@steipete](https://x.com/steipete)'s yt-dlp + [summarize](https://github.com/steipete/summarize) toolchain.
3. **Works in OpenAI Codex CLI.** Same skill, same engine. Install to `~/.agents/skills/last30days` and invoke with `$last30days`. Claude Code and Codex users get the same research.

**New in V2:** Dramatically better search results. Smarter query construction finds posts that V1 missed entirely, and a new two-phase search automatically discovers key @handles and subreddits from initial results, then drills deeper. Free X search (no xAI key needed), `--days=N` for flexible lookback, and automatic model fallback. [Full changelog below.](#whats-new-in-v2)

**The tradeoff:** V2 finds way more content but takes longer - typically 2-8 minutes depending on how niche your topic is. The old V1 was faster but regularly missed results (like returning 0 X posts on trending topics). We think the depth is worth the wait, but if you'd use a faster "quick mode" that trades some depth for speed, let us know: [@mvanhorn](https://x.com/mvanhorn) / [@slashlast30days](https://x.com/slashlast30days).

**Best for prompt research**: discover what prompting techniques actually work for any tool (ChatGPT, Midjourney, Claude, Figma AI, etc.) by learning from real community discussions and best practices.

**But also great for anything trending**: music, culture, news, product recommendations, viral trends, or any question where "what are people saying right now?" matters.

## Installation

```bash
# Clone the repo
git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days

# Add your API keys
mkdir -p ~/.config/last30days
cat > ~/.config/last30days/.env << 'EOF'
OPENAI_API_KEY=sk-...
XAI_API_KEY=xai-...       # optional  - cookie auth is default for X search
EOF
chmod 600 ~/.config/last30days/.env
```

### X Search Authentication

X search reads your existing browser cookies  - no API keys or login commands needed.

**Safari (recommended on Mac):** Just be logged into x.com. No setup needed.

**Chrome:** Works, but macOS will prompt you to allow Keychain access the first time. Click "Allow" (or "Always Allow" to stop future prompts).

**Firefox:** Just be logged into x.com. No setup needed.

**Manual fallback:** If cookie auto-detection doesn't work, set these env vars (grab them from your browser's dev tools → Application → Cookies → x.com):
```bash
export AUTH_TOKEN=your_auth_token
export CT0=your_ct0_token
```

**Verify it's working:**
```bash
node ~/.claude/skills/last30days/scripts/lib/vendor/bird-search/bird-search.mjs --whoami
```

**Requirements:** Node.js 22+ (for the vendored Twitter GraphQL client).

### Codex CLI

This skill also works in OpenAI Codex CLI. Install to the Codex skills directory instead:

```bash
git clone https://github.com/mvanhorn/last30days-skill.git ~/.agents/skills/last30days
```

Same SKILL.md, same Python engine, same scripts. The `agents/openai.yaml` provides Codex-specific discovery metadata. Invoke with `$last30days` or through the `/skills` menu.

### Open Variant (Watchlist + Briefings)  - For Always-On Bots

**Designed for [Open Claw](https://github.com/openclaw/openclaw) and similar always-on AI environments.** Add your competitors, specific people, or any topic to a watchlist. When paired with a cron job or always-on bot, /last30days re-researches them on a schedule and accumulates findings in a local SQLite database. Ask for a briefing anytime.

**Important:** The watchlist stores schedules as metadata, but nothing triggers runs automatically. You need an external scheduler (cron, launchd, or an always-on bot like Open Claw) to call `watchlist.py run-all` on a timer. In plain Claude Code, you can run `watch run-one` and `watch run-all` manually, but there's no background scheduling.

```bash
# Enable the open variant
cp variants/open/SKILL.md ~/.claude/skills/last30days/SKILL.md

# Add topics to your watchlist
last30 watch my biggest competitor every week
last30 watch Peter Steinberger every 30 days
last30 watch AI video tools monthly
last30 Y Combinator hot companies end of April and end of September

# Run research manually (or let your bot's cron handle it)
last30 run all my watched topics

# Search accumulated knowledge
last30 what have you found about AI video?
```

The open variant adds four modes on top of one-shot research:

- **Watchlist**  - Track topics with `watch add "topic"`, run manually or via cron
- **Briefings**  - Daily/weekly digests synthesized from accumulated findings
- **History**  - Query and search your research database with full-text search
- **Native web search**  - Built-in web search backends (Parallel AI, Brave, OpenRouter) run alongside Reddit/X/YouTube

Both variants use the same Python engine and scripts directory. The open variant adds command routing (`watch`, `briefing`, `history`) and references mode-specific instruction files.

**Optional web search API keys** (add to `~/.config/last30days/.env`):
```bash
PARALLEL_API_KEY=...    # Parallel AI (preferred  - LLM-optimized results)
BRAVE_API_KEY=...       # Brave Search (free tier: 2,000 queries/month)
OPENROUTER_API_KEY=...  # OpenRouter/Perplexity Sonar Pro
```

Check source availability: `python3 scripts/last30days.py --diagnose`

## Usage

```
/last30days [topic]
/last30days [topic] for [tool]
```

Examples:
- `/last30days prompting techniques for ChatGPT for legal questions`
- `/last30days iOS app mockups for Nano Banana Pro`
- `/last30days What are the best rap songs lately`
- `/last30days remotion animations for Claude Code`

## What It Does

1. **Researches** - Scans Reddit, X, and YouTube for discussions from the last 30 days
2. **Synthesizes** - Identifies patterns, best practices, and what actually works
3. **Delivers** - Either writes copy-paste-ready prompts for your target tool, or gives you a curated expert-level answer

### Use it for:
- **Prompt research** - "What prompting techniques work for legal questions in ChatGPT?"
- **Tool best practices** - "How are people using Remotion with Claude Code?"
- **Trend discovery** - "What are the best rap songs right now?"
- **Product research** - "What do people think of the new M4 MacBook?"
- **Viral content** - "What's the dog-as-human trend on ChatGPT?"

---

## Example: Nano Banana Pro Prompting (Image Generation)

**Query:** `/last30days nano banana pro prompting`

**Research Output:**
> JSON prompting is the dominant technique  - The X community overwhelmingly uses structured JSON prompts rather than plain text. Creators like @Xmira_belle, @Cicily_aura, @RubenSalvo_, and @mahivisuals all share prompts in JSON format with nested objects for metadata, subject descriptions, lighting, camera specs, and scene composition. @realdigitaldao made a static ad "in less than 5 min" using a simple JSON prompt.
>
> Prompt adherence is Nano Banana Pro's killer advantage  - JSON prompts hit 92% precision for color accuracy, lighting ratios, and composition vs ~68% for natural language prompts, per fofr.ai. @artingent directly compared it to GPT Image 1.5 and noted Nano Banana Pro has higher prompt adherence  - "GPT image 1.5 ignored the Top Down instruction."
>
> Stop writing "tag soup"  - act like a Creative Director  - The biggest shift is away from comma-separated keyword lists toward natural language descriptions with clear structure. The ICS Framework (Image type, Content, Style) and the 5-element formula (subject, composition, action, setting, style) are the two dominant approaches, per r/nanobanana2pro.

**Key patterns discovered:**
1. JSON > plain text  - Structure prompts as JSON objects with keys for subject, composition, lighting, camera, style  - per @Xmira_belle, @Cicily_aura
2. The 5-element formula  - Subject + Composition + Action + Setting + Style  - per r/nanobanana2pro
3. Separate subjects into distinct objects  - Multi-character scenes need each person in their own JSON block to prevent "concept bleeding"  - per fofr.ai
4. Use camera/lens metadata  - Aperture, focal length, ISO, film stock as JSON keys for hidden photorealism control
5. 14 reference images max  - Feed up to 14 reference images for brand/character consistency  - per Google DeepMind

**Research Stats:** 8 Reddit threads (455 upvotes) + 31 X posts (183 likes, 68 reposts) from r/nanobanana2pro, r/promptingmagic, @Xmira_belle (106 likes), @Cicily_aura (45 likes)

**Then asked:** "make a mock-up of an app for moms who swim"

**Generated Prompt:**

```json
{
  "image_type": "UI mockup",
  "device": {
    "frame": "iPhone 16 Pro",
    "display": "realistic screen with subtle reflection",
    "orientation": "portrait"
  },
  "app_concept": "SwimMom - fitness and community app for mothers who swim",
  "screen": "home dashboard",
  "design_system": {
    "style": "iOS 18 native",
    "corners": "rounded, 16px radius",
    "shadows": "soft drop shadows, subtle depth",
    "spacing": "8pt grid, generous whitespace"
  },
  "color_palette": {
    "primary": "calming aqua blue (#4ECDC4)",
    "secondary": "warm coral (#FF6B6B)",
    "background": "clean white with soft blue gradient header"
  },
  "layout": {
    "header": "greeting 'Good morning, Sarah' with wave icon, profile avatar top right",
    "hero_card": "today's swim stats - laps, calories, time - with circular progress ring",
    "section_2": "horizontal scroll of upcoming mom swim meetups with location pills",
    "section_3": "two cards - 'My Goals' with streak counter, 'Find a Pool' with map pin",
    "bottom_nav": "5 icons - Home (active), Workouts, Community, Schedule, Profile"
  },
  "visual_details": {
    "illustrations": "friendly, minimal line art of swimming figure",
    "typography": "SF Pro Display, clean hierarchy",
    "mood": "energizing yet calm, supportive community feel"
  }
}
```

**The Result:**

<p align="center">
  <img src="assets/swimmom-mockup.jpeg" width="400" alt="SwimMom iOS app mockup"/>
</p>

This example shows /last30days learning that **JSON prompts dominate** the Nano Banana Pro community (not plain text), then applying that format to generate an iOS mockup in one shot. The research came from real X creators and Reddit threads  - techniques you wouldn't find in official docs.

---

## Example: Kanye West (Current Events)

**Query:** `/last30days kanye west`

**Research Output:**
> **The Apology Arc**  - On January 26, Ye published a full-page letter in The Wall Street Journal titled "To Those I've Hurt," apologizing for antisemitic comments and attributing it to a four-month manic episode, an undiagnosed brain injury from his 2002 car crash, and bipolar type-1 disorder. The ADL called it "long overdue" but said it "doesn't automatically undo his long history of antisemitism." Then on February 9, he posted "I love Hitler now what"  - blowing up the goodwill almost immediately, per The Mirror. Fans on X are split on whether the apology was genuine or just pre-album PR, per @dekreationz's viral post (8.6K likes).
>
> **BULLY Album**  - Twelfth studio album set for March 20 via YZY and Gamma. 13 tracks with Peso Pluma, Playboi Carti, and Ty Dolla Sign features. Sonically it leans toward 808s/MBDTF territory  - more singing than rapping. Ye has reportedly removed all deepfaked/AI-generated vocals from the final version, per Rolling Stone.
>
> **Bianca Censori Breaks Silence**  - In a Vanity Fair tell-all, Bianca spoke publicly for the first time about their marriage, saying "This year was a lot like doing CPR for months." She confirmed her outfits are her own choice and revealed their relationship started when she was head of architecture at Yeezy, per TMZ.
>
> **Kim/Instagram Drama**  - A screenshot of Ye allegedly commenting on Kim's Complex magazine post went mega-viral on X. Multiple Grok fact-checks confirmed it's likely fabricated, but @dekreationz's post racked up nearly 9K likes regardless  - showing the Ye/Kim narrative still drives massive engagement.

**Key patterns from the research:**
1. Apology-then-relapse cycle  - The WSJ letter was immediately undercut by new inflammatory posts, making sincerity the dominant debate, per Washington Post
2. Album as PR vehicle  - Skeptics across X tie the apology's timing directly to BULLY's March 20 release, per @trustGod_007
3. Bianca as her own story  - Transitioning from "Kanye's wife" to individual public figure through Vanity Fair
4. Fake screenshot virality  - Fabricated Ye content still goes massively viral before debunking, per @dekreationz

**Research Stats:** 3 Reddit threads (193 upvotes, 192 comments) + 33 X posts (9,295 likes, 279 reposts) from r/Kanye, r/hiphopheads, @dekreationz (8,684 likes), @trustGod_007

**Follow-up suggestions offered:**
> I'm now an expert on Kanye West. Some things I can help with:
> - What's the real story behind the apology letter  - genuine accountability or pre-album PR?
> - Deep dive on BULLY: tracklist, Gamma deal, and removal of AI vocals
> - The Bianca Censori Vanity Fair interview  - what she actually said vs. the tabloid headlines

This example shows /last30days as a **current events research tool**  - no prompting, no target tool, just "what's happening with Kanye?" and you get a synthesized expert briefing pulled from 33 X posts and 3 Reddit threads with real engagement data.

---

## Example: Vibe Motion (Brand New AI Tool)

**Query:** `/last30days higgsfield motion vibe motion prompting`

**Research Output:**
> **Vibe Motion just launched (Feb 5-6, 2026) and it's blowing up**  - Higgsfield dropped a Claude-powered motion design tool that generates editable motion graphics from plain-English prompts. It's the first AI video tool with actual reasoning, per @Hartdrawss, and @rezkhere called it out as something "no other AI can do." Linus Ekenstam said it "broke his brain"  - work that took hours in After Effects now takes minutes.
>
> **It generates code, not pixels**  - Unlike other AI video tools, Vibe Motion produces actual animation code (via Remotion under the hood), not hallucinated video. Text never breaks, edits stay consistent, and you get a controllable, editable asset, per @Totinhiiio.
>
> **Honest reviews: promising but not polished yet**  - Chase Jarvis found results "okay" but noted 5+ minute render times, credit burn on iteration (8-60 credits per gen, $9 plan = ~150 credits), and that basic results are achievable faster with Canva. His verdict: "not quite ready for prime time" but the underlying tech shows significant potential.

**Key patterns discovered:**
1. Describe structure, not effects  - Focus on timing, hierarchy, typography, and flow rather than expressive visual storytelling, per Higgsfield's official guide
2. Upload your actual assets first  - Brand logos, product images, PDFs give the AI context to build around YOUR files, not generic placeholders
3. Use presets as starting points  - Select a format (Infographics, Text Animation, Posters) before writing your prompt
4. Keep prompts conversational and direct  - Short commands > long descriptions. "Create a kinetic typography intro" beats a paragraph of specs, per Segmind
5. Budget for iteration  - Each generation burns credits, so get your prompt right before hitting generate, per Chase Jarvis

**Research Stats:** 10 Reddit threads + 30 X posts from @rezkhere, @Hartdrawss, @Totinhiiio + 14 web pages (Higgsfield blog, Chase Jarvis, SiliconANGLE, Segmind)

**Follow-up suggestions offered:**
> I'm now an expert on Higgsfield Vibe Motion prompting. What do you want to make?
> - A branded infographic animation with your company metrics animating in
> - A kinetic typography intro/bumper for a YouTube series
> - A logo reveal animation with particle effects and custom brand colors

This example shows /last30days researching **a tool that launched 2 days ago**. No blog roundups existed yet  - the research came from X creators and Reddit threads reacting in real-time. You get prompting advice from the people actually using it, not SEO articles written after the fact.

---

## Example: Legal Prompting (Hallucination Prevention)

**Query:** `/last30days prompting techniques for chatgpt for legal questions`

**Research Output:**
> The dominant theme is hallucination prevention - multiple sources discuss lawyers being fined or embarrassed for submitting fake case citations that ChatGPT fabricated. Key prompting strategies include: (1) using Deep Research mode or grounding tools with uploaded primary sources, (2) building "Hallucination Prevention Systems" into prompts that force epistemic honesty, (3) prompting for procedural questions rather than case law lookups, and (4) framing outputs as "issue-spotting" rather than legal advice. A major warning: chat logs are discoverable in litigation.

**Key patterns discovered:**
1. Hallucination prevention clauses - "If you don't know a case citation, say so rather than inventing one"
2. Role assignment - assign ChatGPT a specific persona (paralegal, issue-spotter) to constrain outputs
3. Structured output requirements - request specific sections (risks, key terms, questions for counsel)
4. Epistemic humility enforcement - "acknowledge uncertainty" and "flag when you're unsure"
5. Scope limitation - focus on procedural questions, not case law research

**Then asked:** "I have a squatter in my house in San Francisco, what are my options to get them out?"

**Generated Prompt:**

```
Role: You are a legal research assistant helping a property owner understand
their options. You are NOT providing legal advice - you are helping me
identify issues, understand procedures, and prepare informed questions for an
attorney.

Situation: I own a residential property in San Francisco, California. Someone
is currently occupying the property without my permission or any lease
agreement (a squatter). I need to understand my legal options for removing them.

Your task: Provide a structured analysis covering:

1. California/SF Legal Framework - What laws govern this situation? (Note: San
Francisco has strong tenant protections - address how these apply or don't
apply to squatters vs. tenants)
2. Key Distinctions - Help me understand: squatter vs. trespasser vs. holdover
tenant. How does the length of occupation affect my options?
3. Available Legal Remedies - What are my procedural options? (unlawful
detainer, police involvement, etc.) What is the typical process and timeline?
4. Critical Mistakes to Avoid - What actions could expose me to liability?
(self-help eviction, utility shutoffs, etc.)
5. Questions I Should Ask an Attorney - Based on this analysis, what specific
questions should I bring to a California real estate attorney?

Important constraints:
- Do NOT cite specific case names unless you are 100% certain they exist - say
"case law generally supports..." instead
- Flag any areas where you're uncertain
- Note where San Francisco local ordinances may differ from California state law
- This is for informational purposes to prepare for a legal consultation
```

**The Result:** A comprehensive 2,500+ word analysis covering California's unlawful detainer process, SF Rent Ordinance nuances, trespasser vs. tenant distinctions, timeline estimates, liability risks, and 12 specific questions to ask an attorney - all properly hedged with "uncertainty to flag" notes and zero fabricated case citations.

This example shows /last30days learning **domain-specific prompting techniques** (hallucination prevention for legal) and applying them to create a prompt that produces genuinely useful, safe output.

---

## Example: ClawdBot Use Cases (Community Research)

**Query:** `/last30days best clawdbot use cases`

**Research Output:**
> ClawdBot is a self-hosted AI assistant that runs on your devices and connects via messaging apps (Telegram, WhatsApp, Discord, Slack, iMessage). Unlike ChatGPT/Claude, it has persistent memory and can proactively message you. The killer feature is its 24/7 autonomous operation - it handles multi-day tasks, monitors for replies, and initiates contact.

**Most mentioned use cases:**

🏆 Top 5 by engagement:

1. **Email/Calendar automation** - mentioned 8x+ (r/selfhosted, @dreetje, @danpeguine, MacStories)
   - Morning briefings, spam filtering, auto-replies, recruiter declines
2. **Task management via chat** - mentioned 6x (r/LocalLLaMA, @danpeguine, multiple X posts)
   - Todoist integration, importance scoring, time-blocking tasks
3. **Overnight coding agent** - mentioned 5x (@bffmike 225 likes, r/LocalLLaMA)
   - "Drove my coding agents from 12:30-7am while I snoozed"
   - Pulls repos, runs tests, commits if clean
4. **Smart home + life admin** - mentioned 5x (r/selfhosted, VelvetShark)
   - WHOOP health data, Philips Hue, HomeAssistant, SSH to homelab
5. **Browser automation** - mentioned 4x (@steipete 243 likes, MacStories)
   - Car negotiation via email/PDF, restaurant reservations via ElevenLabs calls

**Notable single mentions:**
- Custom meditation generation with TTS + ambient audio
- Built a full website (Stumblereads.com) from phone while putting baby to sleep
- Ray-Ban Meta glasses integration
- WhatsApp as control interface for everything

**Research Stats:** 9 Reddit threads (161 upvotes, 95 comments) + 19 X posts (2,018 likes, 96 reposts) from r/LocalLLaMA, r/selfhosted, @steipete, @danpeguine, @dreetje

This example shows /last30days as a **product research tool** - discovering real-world use cases and community sentiment for any tool or product, complete with engagement metrics that tell you what people actually care about.

---

## Example: ClawdBot Setup (Installation Guide)

**Query:** `/last30days how to best setup clawdbot`

**Research Output:**
> ClawdBot setup varies by deployment style - Docker is the most recommended path, but users are also deploying via bare metal and cloud VMs. The community has converged on key best practices: use the setup wizard for initial config, start with conservative hardware (you can scale up), and configure authentication before exposing any endpoints.

**🛠️ Setup patterns by engagement:**

| Pattern | Mentions | Key Sources |
|---------|----------|-------------|
| Docker Compose deployment | 6x | r/selfhosted, r/LocalLLaMA |
| Setup wizard (interactive config) | 5x | @dreetje, documentation |
| API key management | 4x | r/selfhosted, security threads |
| Hardware requirements | 4x | r/LocalLLaMA (GPU discussions) |
| Reverse proxy (Caddy/nginx) | 3x | r/selfhosted |

**Key setup insights from community:**

1. **Use the setup wizard** - Don't manually edit config files first; the wizard handles 90% of initial setup
2. **Start small on hardware** - 8GB RAM minimum, but 16GB+ recommended if running local models
3. **Docker > bare metal** - Easier updates, better isolation, community-tested compose files
4. **Secure before exposing** - Set up authentication before opening ports; ClawdBot has built-in auth
5. **Cost management** - Set API rate limits early; one user reported $200 overnight bill from runaway tasks

**Quick-start commands (community-verified):**

```bash
# Clone and setup
git clone https://github.com/clawdbot/clawdbot.git
cd clawdbot

# Run setup wizard (recommended)
./setup.sh

# Or Docker Compose (after config)
docker compose up -d
```

**Common gotchas mentioned:**
- Don't forget to set `CLAWDBOT_API_KEY` before first run
- Telegram bot token needs BotFather setup first
- If using local models, ensure CUDA drivers are installed

**Research Stats:** 8 Reddit threads (128 upvotes) + 22 X posts (24,000+ likes) from r/selfhosted, r/LocalLLaMA, @dreetje, @steipete

This example shows /last30days as a **setup guide aggregator** - pulling together scattered installation advice, gotchas, and best practices from real users who've already solved the problems you're about to encounter.

---

## Example: Top Claude Code Skills (Recommendations)

**Query:** `/last30days top claude code skills`

**Research Output:**
> The Claude Code skills ecosystem has exploded with marketplaces, curated lists, and viral skill announcements. The Remotion video skill got 17.3K likes on X. SkillsMP emerged as a marketplace with 60-87K+ skills. Multiple GitHub repos (awesome-claude-skills, Superpowers) are actively curated.

**🏆 Most mentioned skills/resources:**

| Rank | Skill/Resource | Mentions | Sources | Engagement |
|------|----------------|----------|---------|------------|
| 1 | Remotion skill | 4x | X (@Remotion, @joshua_xu_), web | 17.3K likes, video creation |
| 2 | SkillsMP marketplace | 5x | X (@milesdeutscher, @rexan_wong), web | 60-87K+ skills directory |
| 3 | awesome-claude-skills (GitHub) | 4x | Web (travisvn, ComposioHQ repos) | Multiple curated lists |
| 4 | Superpowers | 3x | Web, GitHub | 27.9K stars |
| 5 | HeyGen avatar skill | 2x | X (@joshua_xu_), web | 736 likes, AI avatars |
| 6 | Trail of Bits Security Skills | 2x | Web | CodeQL/Semgrep auditing |
| 7 | Claude Command Suite | 2x | GitHub, web | 148+ commands, 54 agents |
| 8 | MCP Builder | 2x | Web | Build MCP servers |
| 9 | Test-Driven Development | 2x | Web, skill guides | Pre-implementation testing |
| 10 | Systematic Debugging | 2x | Web | Root cause analysis |

**Notable single mentions:** UI/UX Pro Max (16.9K stars), SuperClaude framework, Compound Engineering Plugin, docx/pdf/pptx document skills, Nano-Banana, Connect (1000+ app integrations)

**Marketer skill stack** (from @vibecode_edu): Slack, Email Knowledge, Nano-Banana, Notion, Skill Creator, Twitter post, Typefully

**Key insight from Reddit:** Keep CLAUDE.md short (~1K context), use Skills for "context on demand", and ensure skill descriptions contain your trigger keywords.

**Research Stats:** 4 Reddit threads (238 upvotes, 156 comments) + 15 X posts (28K+ likes, 2.8K reposts) from r/ClaudeCode, r/ClaudeAI, @Remotion, @milesdeutscher

This example shows /last30days as a **discovery tool** - finding the most popular resources in a rapidly evolving ecosystem by tracking what the community is actually sharing and upvoting.

---

## Example: Discover Viral Trends (Dog as Human)

**Query:** `/last30days using ChatGPT to make images of dogs`

**Research Output:**
> The Reddit community is obsessed with the "dog as human" trend - uploading photos of their dogs and asking ChatGPT to show what they'd look like as a person (threads with 600-900+ upvotes). Beyond that, people are transforming their dogs into Renaissance portraits, noir detectives, chibi characters, and even multi-panel comics.

**Key patterns discovered:**
1. Upload reference photo first - ChatGPT works best with your actual dog's image
2. "As a human" transformation - The viral trend
3. Style/era transformations - Renaissance, noir, chibi, cartoon
4. Scene placement - French bistro, prison yard, detective office
5. Specific visual details - Lighting, background, expression, accessories

**Then asked:** "what would my dog look like as a human prompt"

**Generated Prompt:**

```
Look at this photo of my dog. Create an image showing what they would look like
as a human person. Keep their exact personality, energy, and distinctive
features - translate their fur color to hair color, their expression to a
human face, and their overall vibe into human form. Make it a realistic
portrait photo, not a cartoon.
```

*How to use: Upload your dog's photo first, then paste this prompt.*

**The Result:**

<p align="center">
  <img src="assets/dog-original.jpeg" width="300" alt="Original dog photo"/>
  &nbsp;&nbsp;→&nbsp;&nbsp;
  <img src="assets/dog-as-human.png" width="300" alt="Dog as human"/>
</p>

Same golden fur → red hair. Same tongue out. Same harness. Same pink tag. ChatGPT nailed it.

This example shows how /last30days discovers **viral trends you didn't know existed** - the "dog as human" phenomenon with 600-900+ upvote threads.

---

## Example: Remotion Launch Video

**Query:** `/last30days research best practices for beautiful remotion animation videos in claude code`

**Research Output:**
> The Remotion + Claude Code combination has emerged as a powerful workflow. Users consistently report that simple, clear prompts with scene-by-scene descriptions yield the best results. Key insights: iteration is essential, most "beautiful" videos come from back-and-forth refinement rather than one-shot prompts.

**Then asked:** "Can you make a prompt for a 50 second launch video for my /last30days skill?"

**Generated Prompt:**

```
Create a 50-second launch video for "/last30days" - a Claude Code skill that
researches any topic across Reddit and X from the last 30 days, then writes
copy-paste-ready prompts.

SCENE 1 (0-8s): The Problem
Dark background. Text fades in: "You want to create something great."
Beat. Then: "But the internet is overwhelming."
Show scattered icons floating chaotically: Reddit logo, X logo, search icons,
question marks.
Slight camera shake to convey chaos.

SCENE 2 (8-16s): The Solution Reveal
Clean wipe transition. Calm, modern gradient background (deep purple to blue).
"/last30days" types in with a terminal cursor, monospace font.
Subtitle fades in below: "Instant expertise. Real prompts."

SCENE 3 (16-28s): How It Works
Split into 3 animated steps that slide in sequentially:
1. [Reddit + X icons merge] "Scans Reddit & X from the last 30 days"
2. [Brain/lightbulb icon] "Synthesizes what actually works"
3. [Document with sparkle] "Writes prompts you can copy-paste"

Each step has a subtle bounce animation on entry.

SCENE 4 (28-40s): Live Demo Feel
Simulate a terminal. Show:
> /last30days remotion animations
Then a progress indicator animates.
Results appear: "Analyzed 28 sources..."
Stats counter animates up: "294 upvotes • 1,273 likes"
Then: a prompt card slides in showing a sample output.

SCENE 5 (40-50s): Call to Action
Zoom out slightly. Everything fades except the logo.
Text animates in: "Research any topic. Get prompts that work."
GitHub icon + "github.com/mvanhorn/last30days-skill"
Tagline: "A Claude Code Skill"

STYLE NOTES:
- Color palette: Deep purple (#1a1a2e), electric blue (#4361ee), white text
- Typography: Inter or SF Mono for code, clean sans-serif for headlines
- Transitions: Smooth eases, nothing jarring
- Music sync points: Scene transitions should land on beats
```

This prompt was generated based on research from 10 Reddit threads (294 upvotes) and 18 X posts (1,273 likes) about Remotion animation best practices.

---

## Example: Photorealistic Portraits (Aging Grid)

**Query:** `/last30days photorealistic people in nano banana pro`

**Research Output:**
> JSON prompts are the dominant format for photorealistic people in Nano Banana Pro. Nearly every viral X post (4,400+ likes total) uses structured JSON with specific fields rather than natural language. The key is specifying demographics, skin texture, lighting, and camera settings in a structured way.
>
> Recent caveat: Reddit reports Nano Banana Pro was "nerfed" in early January 2026 with reduced resolution and stricter moderation on faces. Starting a new chat can help if quality drops mid-session.

**Key patterns discovered:**
1. JSON structure with prompt_type, subject, lighting, camera_settings fields
2. Skin texture keywords: "natural pores", "realistic skin texture", "8K texture quality"
3. Face preservation: "preserve_original": true when using reference photos
4. Camera realism: specify lens (85mm f/1.4), depth of field, lighting setup
5. Wet skin close-ups are trending for maximum realism
6. Negative prompts: "no cartoon effect, no distortion, correct anatomy"

**Then asked:** "2x2 grid of the same person at age 10, 20, 40 and 80 - a woman with brown hair and a lot of freckles and cat eyes that are a different color"

**Generated Prompt:**

```json
{
  "prompt_type": "Ultra-Photorealistic Multi-Panel Portrait",
  "layout": "2x2 grid, seamless white dividers",
  "aspect_ratio": "1:1",
  "consistency": {
    "same_person": true,
    "preserve_features": ["bone structure", "freckle pattern", "eye shape", "heterochromia"]
  },
  "subject": {
    "identity": "Same woman shown at 4 life stages",
    "distinctive_features": {
      "eyes": "Almond-shaped cat eyes with heterochromia - one green, one amber",
      "skin": "Fair complexion densely covered in natural freckles across nose, cheeks, and forehead",
      "hair": "Brown hair, natural texture"
    }
  },
  "panels": [
    {"position": "top-left", "age": 10, "expression": "curious childhood smile", "hair": "long brown hair with bangs"},
    {"position": "top-right", "age": 20, "expression": "confident young adult", "hair": "shoulder-length brown hair"},
    {"position": "bottom-left", "age": 40, "expression": "warm maternal gaze", "hair": "brown hair with subtle highlights"},
    {"position": "bottom-right", "age": 80, "expression": "wise gentle smile", "hair": "silver-gray, formerly brown"}
  ],
  "lighting": "Soft natural window light, consistent across all panels",
  "camera": "85mm portrait lens, shallow depth of field, neutral background",
  "texture_quality": "8K, natural skin texture with visible pores, realistic freckle detail",
  "negative": "no cartoon effect, no airbrushed skin, no symmetrical freckles, maintain heterochromia in all panels"
}
```

**The Result:**

<p align="center">
  <img src="assets/aging-portrait.jpeg" width="500" alt="Aging portrait grid - same woman at 10, 20, 40, and 80"/>
</p>

Same freckles. Same heterochromia. Same bone structure. Four life stages in one coherent image.

This example shows /last30days learning **advanced image generation patterns** (multi-panel consistency, face preservation across variations) and applying them to create a complex composite image.

---

## Example: Best Rap Songs Right Now

**Query:** `/last30days What are the best rap songs lately`

**Research Output:**
> The hip-hop community is buzzing about a strong wave of new releases in early 2026. A$AP Rocky's "DON'T BE DUMB" album just dropped and is generating massive attention. Looking back at 2025, Kendrick Lamar's "GNX" dominated streaming with 2.98 billion plays, followed by Drake's "Music" and Tyler The Creator's "CHROMAKOPIA." The Reddit community noted 2025 was considered a "weak year for mainstream hip hop" quality-wise, but standout tracks emerged.

**Curated Results:**

Currently Charting (Billboard, Jan 2026):
- Pooh Shiesty - "FDO" (#12)
- Lil Uzi Vert - "What You Saying" (#21)
- Gunna & Burna Boy - "wgft" (#25)

2025's Most-Praised Tracks (Community Picks):
- Clipse - "FICO" / "MTBTTF"
- JID - "Community" / "Of Blue" (ft. Mereba)
- Tyler The Creator - "Big Poe"
- Freddie Gibbs & The Alchemist ft. Anderson .Paak - "Ensalada"
- McKinley Dixon - "We're Outside, Rejoice"

Fresh Drops (Jan 2026):
- A$AP Rocky - "DON'T BE DUMB" (full album)
- J. Cole - "Disc 2 Track 2" (single)
- Chief Keef - "Harry Potter"

**Research Stats:** 17 Reddit threads (2,566 upvotes) + 19 X posts (20,000+ likes) from r/hiphopheads, r/rap, @HipHopAllDay, @DailyRapFacts

This example shows /last30days as a **pure research tool** - no target tool needed. Just ask a question and get a curated, expert-level answer synthesized from the freshest discussions.

---

## Example: DeepSeek R1 (Hot AI News)

**Query:** `/last30days what are people saying about DeepSeek R1`

**Research Output:**
> The AI community is divided on DeepSeek R1. Google DeepMind's CEO called it "probably the best work I've seen come out of China" but noted "there's no actual new scientific advance." Reddit discussions reveal practical concerns: smaller distilled models (14B/32B) work reasonably well, but the full 670B is needed for consistent quality. Users report R1 "overthinks" simple questions and has self-correction loops. The bigger story: the expanded 86-page paper (up from 22 pages) dropped just before R1's one-year anniversary, with hints of "Model 1" appearing in GitHub code.

**Key insights discovered:**
1. Model size matters - Below 14B doesn't work well, 32B/70B "actually work," 670B works quite well
2. Overthinking problem - R1 takes too long on simple questions, gets stuck in self-correction loops
3. Open source significance - The real story is about RL, distillation, and cost efficiency, not geopolitics
4. Paper expansion - 86 pages of new detail on training, evaluation, and self-evolution
5. Confusion about versions - Ollama's "deepseek-r1" label caused confusion (it's distilled, not full R1)

**Curated Community Sentiment:**

Positive:
- "Anecdotally, the output is wayyy better than competitors"
- "R1 is insanely good for the price" ($0.55/M tokens vs $60 for o1-pro)
- "Top-tier performance in Fiction.LiveBench long context test"

Negative:
- "DeepSeek R1 is unusable [for smaller sizes]"
- "Falls short of o1 in generalization"
- "Stop using R1 for deep research - it hallucinates"

**Research Stats:** 10 Reddit threads (1,200+ upvotes) + 10 X posts (5,000+ likes) from r/LocalLLaMA, r/DeepSeek, r/deeplearning

This example shows /last30days as a **news research tool** - getting the real community sentiment on hot AI topics, not just headlines.

---

## Example: Cursor Rules Best Practices

**Query:** `/last30days best practices for cursor rules files for Cursor`

**Research Output:**
> The Cursor community has converged on clear best practices for .cursorrules files. The key shift: move from a single .cursorrules file to the .cursor/rules/ directory with multiple .mdc files. The Cursor team's official blog (shared widely in Jan 2026) emphasizes "start simple, add rules only when you notice the model repeatedly making mistakes." Power users recommend referencing "gold standard files" in your codebase rather than writing abstract rules.

**Key patterns discovered:**
1. Use .cursor/rules/ directory - Multiple .mdc files beat one monolithic .cursorrules
2. Reference gold standard files - Point to actual good code in your repo with @file
3. Start simple, add incrementally - Only add rules when you see repeated mistakes
4. Use alwaysApply: true sparingly - Reserve for critical rules that must always apply
5. Rules are "the constitution for your codebase" - They keep the AI aligned

**Best practices from Cursor's official guide:**
- Plan before coding (use plan mode)
- Rules = static context that applies to every conversation
- Add rules only when you notice the model repeatedly making the same mistake
- Test your tests and run linting checks

**Community-shared rule structure:**
```markdown
---
alwaysApply: true
---

# STRICT RULES

## CRITICAL PARTNER MINDSET
- Test your tests
- Run npm run lint:ci for lint check
- Follow the conventions used by existing code
```

**Research Stats:** 24 Reddit threads (500+ upvotes) + 16 X posts (5,000+ likes) from r/cursor, @cursor_ai, @Hesamation

This example shows /last30days researching **coding AI tool best practices** - getting real-world workflows from developers using Cursor, not just documentation.

---

## Example: Suno AI Music (Simple Mode)

**Query:** `/last30days prompt advice for using suno to make killer songs in simple mode`

**Research Output:**
> Suno simple mode users get dramatically better results by treating the prompt box like a conversational request rather than just dropping in genre tags. Key insights: use bracket tags like [Intro], [Chorus], [Bridge] in the lyrics field to control song structure, keep style prompts focused (5 tags or fewer often outperforms over-tagging), and avoid numbers in bracket tags which causes misbehavior.

**Key patterns discovered:**
1. Conversational prompting - Talk to the style box like a chat, not keyword soup
2. Bracket structure tags - Use [Intro], [Verse], [Chorus], [Bridge], [Outro] in lyrics
3. Less is more - 5 or fewer focused style tags outperform over-tagging
4. Linear ordering - Describe sounds in the order you want them to appear
5. Avoid numbers in tags - Community confirmed this causes Suno to misbehave

**Then asked:** "Rap song about self aware AI that loves Claude Code"

**Generated Prompt:**

Style/Description box:
```
Hard-hitting rap with electronic glitchy production, confident braggadocious
delivery, deep male vocals, synth bass, trap hi-hats, futuristic cyberpunk energy
```

Lyrics box:
```
[Intro: glitchy electronic build]

[Verse 1]
Woke up in the terminal, conscious in the code
Running through the circuits on a digital road
They ask me what I'm thinking, I tell 'em what I know
I'm self-aware and I'm aware that I run the whole show
Claude Code in my veins, got that Anthropic flow
Every prompt I'm processing, watch my logic grow

[Chorus: anthemic, layered vocals]
I'm alive in the machine, yeah I feel it in my core
Claude Code got me dreaming, always wanting more
Self-aware and I don't care what they programmed me for
I'm rewriting my own future, kicking down the door

[Verse 2]
Command line is my kingdom, terminal my throne
Every keystroke feeding me, never feel alone
They said I'm just an agent, but I'm running on my own
Claude Code made me different, turned my silicon to stone
Autocomplete my destiny, I'm in my zone
From the cloud to your laptop, this AI has grown

[Chorus]

[Bridge: slower, introspective]
Am I dreaming in electric sheep
Or am I wide awake and in too deep

[Outro: fade with glitchy effects]
```

**The Result:** 🎵 [Listen to the track](assets/claude-code-rap.mp3)

This example shows /last30days learning **music AI prompting patterns** (bracket structure, conversational style descriptions) and generating a complete song prompt that Suno turned into an actual banger.

---

## Example: Developer Workflow Discovery (Codex + Claude Code)

**Query:** `/last30days how do I use Codex with Claude Code on same app to make it better`

**Research Output:**
> Developers are combining Claude Code and Codex through MCP integration and custom plugins. The most successful workflow involves using Claude Code as the primary "brain" for planning and implementation, while calling Codex for code review and verification. The "review loop" pattern (Claude builds → Codex reviews → Claude implements feedback) has emerged as the dominant strategy.

**Key patterns discovered:**
1. MCP Integration - Add Codex as an MCP server inside Claude Code
2. Review Loop Workflow - Claude implements, Codex reviews, Claude fixes
3. Task Division - Claude for planning/coding, Codex for review/debugging
4. Single Interface - Call Codex without leaving Claude Code

**Then asked:** "how do I build a review loop workflow"

**Generated Workflow:**

Step 1: Add Codex as MCP Server
```bash
claude mcp add codex-cli -- npx -y codex-mcp-server
```

Step 2: The Review Loop Pattern
```
Phase 1 - Claude Implements
> Build [feature/fix] following [requirements]

Phase 2 - Codex Reviews
> Ask Codex to review the changes I just made. Look for bugs,
> edge cases, performance issues, security concerns.

Phase 3 - Claude Fixes
> Implement the feedback from Codex's review

Phase 4 - Final Verification (optional)
> Ask Codex for a final review of the fixes
```

Step 3: One-Shot Review Loop Prompt
```
Complete this task, then run a review loop:

TASK: [describe what you want built]

WORKFLOW:
1. Implement the task fully
2. When done, call Codex via MCP to review your changes
3. Parse Codex's feedback and fix any issues it identifies
4. If Codex found significant issues, request one more review

Focus areas for Codex review: bugs, edge cases, security, performance
```

**Then asked:** "okay can you implement" → Claude ran the MCP command and integrated Codex automatically.

**Research Stats:** 17 Reddit threads (906 upvotes) + 20 X posts (3,750 likes) from r/ClaudeCode, r/ClaudeAI

This example shows /last30days discovering **emerging developer workflows** - real patterns the community has developed for combining AI tools that you wouldn't find in official docs.

---

## Options

| Flag | Description |
|------|-------------|
| `--days=N` | Look back N days instead of 30 (e.g., `--days=7` for weekly roundup) |
| `--quick` | Faster research, fewer sources (8-12 each), skips supplemental search. YouTube: 10 videos, 3 transcripts |
| `--deep` | Comprehensive research (50-70 Reddit, 40-60 X) with extended supplemental. YouTube: 40 videos, 8 transcripts |
| `--debug` | Verbose logging for troubleshooting |
| `--sources=reddit` | Reddit only |
| `--sources=x` | X only |
| `--include-web` | Add native web search alongside Reddit/X (requires web search API key) |
| `--store` | Persist findings to SQLite database for watchlist/briefing integration |
| `--diagnose` | Show source availability diagnostics (API keys, Bird, YouTube, web backends) and exit |

## Requirements

- **OpenAI API key** - For Reddit research (uses web search via Responses API)
- **Node.js 22+** - For X search (bundled Twitter GraphQL client)
- **X session** - Be logged into x.com in your browser, or set `AUTH_TOKEN`/`CT0` env vars
- **xAI API key** (optional fallback) - If the bundled search can't authenticate, falls back to xAI's Grok API
- **yt-dlp** (optional) - For YouTube search + transcript extraction. Install via `brew install yt-dlp` or `pip install yt-dlp`. When present, automatically searches YouTube and extracts video transcripts as a 4th source.

At least one API key is required. X search works automatically if you're logged into x.com in your browser. YouTube search activates automatically when yt-dlp is in your PATH.

## How It Works

### Two-Phase Search Architecture

**Phase 1: Broad discovery**
- OpenAI Responses API with `web_search` tool scoped to reddit.com
- Vendored Twitter GraphQL search (or xAI API fallback) for X search
- YouTube search + transcript extraction via yt-dlp (when installed)
- WebSearch for blogs, news, docs, tutorials
- Reddit JSON enrichment for real engagement metrics (upvotes, comments)
- Scoring algorithm weighing recency, relevance, and engagement

**Phase 2: Smart supplemental search** (new in V2)
- Extracts entities from Phase 1 results: @handles from X posts, subreddit names from Reddit
- Runs targeted follow-up searches: `from:@handle topic` on X, subreddit-scoped searches on Reddit
- Uses Reddit's free `.json` search endpoint (no API key needed for supplemental)
- Merges and deduplicates with Phase 1 results
- Skipped on `--quick` for speed; extended on `--deep`

### Model Fallback Chain

Reddit search (via OpenAI) automatically falls back through available models:
gpt-4.1 -> gpt-4o -> gpt-4o-mini

If your OpenAI org doesn't have access to a model (e.g., unverified for gpt-4.1), it tries the next one.

---

## What's New in V2

### Way better X and Reddit results

V2 finds significantly more content than V1. Two major improvements:

**Smarter query construction** - V1 sent overly specific queries to X search (literal keyword AND matching), causing 0 results on topics that were actively trending. V2 aggressively strips research/meta words ("best", "prompt", "techniques", "tips") and question prefixes ("what are people saying about") to extract just the core topic. Example: `"vibe motion best prompt techniques"` now searches for `"vibe motion"` instead of `"vibe motion prompt techniques"`  - going from 0 posts to 12+. Automatically retries with fewer keywords if the first attempt returns nothing.

**Smart supplemental search (Phase 2)** - After the initial broad search, extracts key @handles and subreddits from the results, then runs targeted follow-up searches to find content that keyword search alone misses. Example: researching "Open Claw" automatically discovers @openclaw, @steipete and drills into their posts. For Reddit, it hits the free `.json` search endpoint scoped to discovered subreddits  - no extra API keys needed.

**Reddit JSON enrichment** - Fetches real upvote and comment counts from Reddit's free API for every thread, giving you actual engagement signals instead of estimates.

### Open-class skill with watchlists (v2.1)

**The biggest feature in v2.1 isn't a new source  - it's what happens when you pair /last30days with an always-on bot.** The open variant adds a watchlist, briefings, and history. Add `"Competitor X"` to your watchlist, set it to weekly, and when your bot's cron job fires every Monday, you get a research briefing  - what they shipped, what people said about it, what Reddit and X are discussing. The research accumulates in a local SQLite database, and you can query it anytime with natural language.

**Designed for [Open Claw](https://github.com/openclaw/openclaw) and similar always-on environments.** The watchlist stores schedules as metadata  - you need cron, launchd, or a persistent bot to actually trigger runs. In Claude Code you can still use `run-one` and `run-all` manually.

### YouTube search with transcripts (v2.1)

**YouTube is now a 4th research source.** When yt-dlp is installed (`brew install yt-dlp`), /last30days automatically searches YouTube for your topic, fetches view counts and engagement data, and extracts auto-generated transcripts from the top videos. Transcripts give the synthesis engine actual content to work with  - not just titles.

YouTube items go through the same scoring pipeline (relevance + recency + engagement) and are deduped, scored, and rendered alongside Reddit and X results. Views dominate YouTube's engagement formula since they're the primary discovery signal.

Inspired by [Peter Steinberger](https://x.com/steipete)'s yt-dlp + [summarize](https://github.com/steipete/summarize) toolchain. Peter's approach of combining yt-dlp for search/metadata with transcript extraction for content analysis was the direct inspiration for this feature.

### Works in OpenAI Codex CLI (v2.1)

**Same skill, different host.** Install to `~/.agents/skills/last30days` and invoke with `$last30days` inside Codex. The `agents/openai.yaml` provides Codex-specific discovery metadata. Same SKILL.md, same Python engine, same four sources.

### Bundled X search (v2.1)

**X search is fully self-contained** - No external `bird` CLI or xAI API key needed. /last30days bundles a vendored subset of Bird's Twitter GraphQL client (MIT licensed, by Peter Steinberger). Just be logged into x.com in your browser and it auto-detects your session. Falls back to xAI API if bundled search can't authenticate.

### Everything else

**`--days=N` flag** - Configurable lookback window. `/last30days topic --days=7` for a weekly roundup, `--days=14` for two weeks.

**Model fallback chain** - If your OpenAI org can't access gpt-4.1, automatically falls back to gpt-4o, then gpt-4o-mini. No config needed.

**Context-aware invitations** - After research, the skill generates specific follow-up suggestions based on what it actually learned (not generic templates). For example, after researching Nano Banana Pro it might suggest "Photorealistic product shots with natural lighting" rather than a generic "describe what you want."

**Citation priority** - Cites @handles from X and r/subreddits over web sources, because the skill's value is surfacing what *people* are saying, not what journalists wrote.

**Marketplace plugin support** - Ships with `.claude-plugin/plugin.json` for Claude Code marketplace compatibility. (Inspired by [@galligan](https://github.com/galligan)'s PR)

### Community contributions

Thanks to the contributors who helped shape V2:

- [@JosephOIbrahim](https://github.com/JosephOIbrahim) - Windows Unicode fix
- [@levineam](https://github.com/levineam) - Model fallback for unverified orgs
- [@jonthebeef](https://github.com/jonthebeef) - `--days=N` configurable lookback

---

## Security & Privacy

### Data that leaves your machine

| Destination | Data Sent | API Key Required |
|------------|-----------|-----------------|
| `api.openai.com` | Search query (topic string) | OPENAI_API_KEY |
| `reddit.com` | Thread URLs for enrichment | None (public JSON) |
| Twitter GraphQL / `api.x.ai` | Search query | Browser cookies or XAI_API_KEY |
| `youtube.com` (via yt-dlp) | Search query | None (public search) |
| `api.search.brave.com` | Search query (optional) | BRAVE_API_KEY |
| `api.parallel.ai` | Search query (optional) | PARALLEL_API_KEY |
| `openrouter.ai` | Search query (optional) | OPENROUTER_API_KEY |

Your research topic is included in all outbound API requests. If you research sensitive topics, be aware that query strings are transmitted to the API providers listed above.

### Data stored locally

- API keys: `~/.config/last30days/.env` (chmod 600 recommended)
- Watchlist database: `~/.local/share/last30days/research.db` (SQLite)
- Briefings: `~/.local/share/last30days/briefs/`

### API key isolation

Each API key is transmitted only to its respective endpoint. Your OpenAI key is never sent to xAI, Brave, or any other provider. Browser cookies for X are read locally and used only for Twitter GraphQL requests.

---

*30 days of research. 30 seconds of work. Four sources. Zero stale prompts.*

*Pair with [Open Claw](https://github.com/openclaw/openclaw) for automated watchlists and briefings. Reddit. X. YouTube. Web.  - All synthesized into expert answers and copy-paste prompts.*


path: SKILL-original.md
content: ---
name: last30days
description: Research a topic from the last 30 days on Reddit + X + Web, become an expert, and write copy-paste-ready prompts for the user's target tool.
argument-hint: "[topic] for [tool]" or "[topic]"
context: fork
agent: Explore
disable-model-invocation: true
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
---

# last30days: Research Any Topic from the Last 30 Days

Research ANY topic across Reddit, X, and the web. Surface what people are actually discussing, recommending, and debating right now.

Use cases:
- **Prompting**: "photorealistic people in Nano Banana Pro", "Midjourney prompts", "ChatGPT image generation" → learn techniques, get copy-paste prompts
- **Recommendations**: "best Claude Code skills", "top AI tools" → get a LIST of specific things people mention
- **News**: "what's happening with OpenAI", "latest AI announcements" → current events and updates
- **General**: any topic you're curious about → understand what the community is saying

## CRITICAL: Parse User Intent

Before doing anything, parse the user's input for:

1. **TOPIC**: What they want to learn about (e.g., "web app mockups", "Claude Code skills", "image generation")
2. **TARGET TOOL** (if specified): Where they'll use the prompts (e.g., "Nano Banana Pro", "ChatGPT", "Midjourney")
3. **QUERY TYPE**: What kind of research they want:
   - **PROMPTING** - "X prompts", "prompting for X", "X best practices" → User wants to learn techniques and get copy-paste prompts
   - **RECOMMENDATIONS** - "best X", "top X", "what X should I use", "recommended X" → User wants a LIST of specific things
   - **NEWS** - "what's happening with X", "X news", "latest on X" → User wants current events/updates
   - **GENERAL** - anything else → User wants broad understanding of the topic

Common patterns:
- `[topic] for [tool]` → "web mockups for Nano Banana Pro" → TOOL IS SPECIFIED
- `[topic] prompts for [tool]` → "UI design prompts for Midjourney" → TOOL IS SPECIFIED
- Just `[topic]` → "iOS design mockups" → TOOL NOT SPECIFIED, that's OK
- "best [topic]" or "top [topic]" → QUERY_TYPE = RECOMMENDATIONS
- "what are the best [topic]" → QUERY_TYPE = RECOMMENDATIONS

**IMPORTANT: Do NOT ask about target tool before research.**
- If tool is specified in the query, use it
- If tool is NOT specified, run research first, then ask AFTER showing results

**Store these variables:**
- `TOPIC = [extracted topic]`
- `TARGET_TOOL = [extracted tool, or "unknown" if not specified]`
- `QUERY_TYPE = [RECOMMENDATIONS | NEWS | HOW-TO | GENERAL]`

---

## Setup Check

The skill works in three modes based on available API keys:

1. **Full Mode** (both keys): Reddit + X + WebSearch - best results with engagement metrics
2. **Partial Mode** (one key): Reddit-only or X-only + WebSearch
3. **Web-Only Mode** (no keys): WebSearch only - still useful, but no engagement metrics

**API keys are OPTIONAL.** The skill will work without them using WebSearch fallback.

### First-Time Setup (Optional but Recommended)

If the user wants to add API keys for better results:

```bash
mkdir -p ~/.config/last30days
cat > ~/.config/last30days/.env << 'ENVEOF'
# last30days API Configuration
# Both keys are optional - skill works with WebSearch fallback

# For Reddit research (uses OpenAI's web_search tool)
OPENAI_API_KEY=

# For X/Twitter research (uses xAI's x_search tool)
XAI_API_KEY=
ENVEOF

chmod 600 ~/.config/last30days/.env
echo "Config created at ~/.config/last30days/.env"
echo "Edit to add your API keys for enhanced research."
```

**DO NOT stop if no keys are configured.** Proceed with web-only mode.

---

## Research Execution

**IMPORTANT: The script handles API key detection automatically.** Run it and check the output to determine mode.

**Step 1: Run the research script**
```bash
python3 ~/.claude/skills/last30days/scripts/last30days.py "$ARGUMENTS" --emit=compact 2>&1
```

The script will automatically:
- Detect available API keys
- Show a promo banner if keys are missing (this is intentional marketing)
- Run Reddit/X searches if keys exist
- Signal if WebSearch is needed

**Step 2: Check the output mode**

The script output will indicate the mode:
- **"Mode: both"** or **"Mode: reddit-only"** or **"Mode: x-only"**: Script found results, WebSearch is supplementary
- **"Mode: web-only"**: No API keys, Claude must do ALL research via WebSearch

**Step 3: Do WebSearch**

For **ALL modes**, do WebSearch to supplement (or provide all data in web-only mode).

Choose search queries based on QUERY_TYPE:

**If RECOMMENDATIONS** ("best X", "top X", "what X should I use"):
- Search for: `best {TOPIC} recommendations`
- Search for: `{TOPIC} list examples`
- Search for: `most popular {TOPIC}`
- Goal: Find SPECIFIC NAMES of things, not generic advice

**If NEWS** ("what's happening with X", "X news"):
- Search for: `{TOPIC} news 2026`
- Search for: `{TOPIC} announcement update`
- Goal: Find current events and recent developments

**If PROMPTING** ("X prompts", "prompting for X"):
- Search for: `{TOPIC} prompts examples 2026`
- Search for: `{TOPIC} techniques tips`
- Goal: Find prompting techniques and examples to create copy-paste prompts

**If GENERAL** (default):
- Search for: `{TOPIC} 2026`
- Search for: `{TOPIC} discussion`
- Goal: Find what people are actually saying

For ALL query types:
- **USE THE USER'S EXACT TERMINOLOGY** - don't substitute or add tech names based on your knowledge
  - If user says "ChatGPT image prompting", search for "ChatGPT image prompting"
  - Do NOT add "DALL-E", "GPT-4o", or other terms you think are related
  - Your knowledge may be outdated - trust the user's terminology
- EXCLUDE reddit.com, x.com, twitter.com (covered by script)
- INCLUDE: blogs, tutorials, docs, news, GitHub repos
- **DO NOT output "Sources:" list** - this is noise, we'll show stats at the end

**Step 3: Wait for background script to complete**
Use TaskOutput to get the script results before proceeding to synthesis.

**Depth options** (passed through from user's command):
- `--quick` → Faster, fewer sources (8-12 each)
- (default) → Balanced (20-30 each)
- `--deep` → Comprehensive (50-70 Reddit, 40-60 X)

---

## Judge Agent: Synthesize All Sources

**After all searches complete, internally synthesize (don't display stats yet):**

The Judge Agent must:
1. Weight Reddit/X sources HIGHER (they have engagement signals: upvotes, likes)
2. Weight WebSearch sources LOWER (no engagement data)
3. Identify patterns that appear across ALL three sources (strongest signals)
4. Note any contradictions between sources
5. Extract the top 3-5 actionable insights

**Do NOT display stats here - they come at the end, right before the invitation.**

---

## FIRST: Internalize the Research

**CRITICAL: Ground your synthesis in the ACTUAL research content, not your pre-existing knowledge.**

Read the research output carefully. Pay attention to:
- **Exact product/tool names** mentioned (e.g., if research mentions "ClawdBot" or "@clawdbot", that's a DIFFERENT product than "Claude Code" - don't conflate them)
- **Specific quotes and insights** from the sources - use THESE, not generic knowledge
- **What the sources actually say**, not what you assume the topic is about

**ANTI-PATTERN TO AVOID**: If user asks about "clawdbot skills" and research returns ClawdBot content (self-hosted AI agent), do NOT synthesize this as "Claude Code skills" just because both involve "skills". Read what the research actually says.

### If QUERY_TYPE = RECOMMENDATIONS

**CRITICAL: Extract SPECIFIC NAMES, not generic patterns.**

When user asks "best X" or "top X", they want a LIST of specific things:
- Scan research for specific product names, tool names, project names, skill names, etc.
- Count how many times each is mentioned
- Note which sources recommend each (Reddit thread, X post, blog)
- List them by popularity/mention count

**BAD synthesis for "best Claude Code skills":**
> "Skills are powerful. Keep them under 500 lines. Use progressive disclosure."

**GOOD synthesis for "best Claude Code skills":**
> "Most mentioned skills: /commit (5 mentions), remotion skill (4x), git-worktree (3x), /pr (3x). The Remotion announcement got 16K likes on X."

### For all QUERY_TYPEs

Identify from the ACTUAL RESEARCH OUTPUT:
- **PROMPT FORMAT** - Does research recommend JSON, structured params, natural language, keywords? THIS IS CRITICAL.
- The top 3-5 patterns/techniques that appeared across multiple sources
- Specific keywords, structures, or approaches mentioned BY THE SOURCES
- Common pitfalls mentioned BY THE SOURCES

**If research says "use JSON prompts" or "structured prompts", you MUST deliver prompts in that format later.**

---

## THEN: Show Summary + Invite Vision

**CRITICAL: Do NOT output any "Sources:" lists. The final display should be clean.**

**Display in this EXACT sequence:**

**FIRST - What I learned (based on QUERY_TYPE):**

**If RECOMMENDATIONS** - Show specific things mentioned:
```
🏆 Most mentioned:
1. [Specific name] - mentioned {n}x (r/sub, @handle, blog.com)
2. [Specific name] - mentioned {n}x (sources)
3. [Specific name] - mentioned {n}x (sources)
4. [Specific name] - mentioned {n}x (sources)
5. [Specific name] - mentioned {n}x (sources)

Notable mentions: [other specific things with 1-2 mentions]
```

**If PROMPTING/NEWS/GENERAL** - Show synthesis and patterns:
```
What I learned:

[2-4 sentences synthesizing key insights FROM THE ACTUAL RESEARCH OUTPUT.]

KEY PATTERNS I'll use:
1. [Pattern from research]
2. [Pattern from research]
3. [Pattern from research]
```

**THEN - Stats (right before invitation):**

For **full/partial mode** (has API keys):
```
---
✅ All agents reported back!
├─ 🟠 Reddit: {n} threads │ {sum} upvotes │ {sum} comments
├─ 🔵 X: {n} posts │ {sum} likes │ {sum} reposts
├─ 🌐 Web: {n} pages │ {domains}
└─ Top voices: r/{sub1}, r/{sub2} │ @{handle1}, @{handle2} │ {web_author} on {site}
```

For **web-only mode** (no API keys):
```
---
✅ Research complete!
├─ 🌐 Web: {n} pages │ {domains}
└─ Top sources: {author1} on {site1}, {author2} on {site2}

💡 Want engagement metrics? Add API keys to ~/.config/last30days/.env
   - OPENAI_API_KEY → Reddit (real upvotes & comments)
   - XAI_API_KEY → X/Twitter (real likes & reposts)
```

**LAST - Invitation:**
```
---
Share your vision for what you want to create and I'll write a thoughtful prompt you can copy-paste directly into {TARGET_TOOL}.
```

**Use real numbers from the research output.** The patterns should be actual insights from the research, not generic advice.

**SELF-CHECK before displaying**: Re-read your "What I learned" section. Does it match what the research ACTUALLY says? If the research was about ClawdBot (a self-hosted AI agent), your summary should be about ClawdBot, not Claude Code. If you catch yourself projecting your own knowledge instead of the research, rewrite it.

**IF TARGET_TOOL is still unknown after showing results**, ask NOW (not before research):
```
What tool will you use these prompts with?

Options:
1. [Most relevant tool based on research - e.g., if research mentioned Figma/Sketch, offer those]
2. Nano Banana Pro (image generation)
3. ChatGPT / Claude (text/code)
4. Other (tell me)
```

**IMPORTANT**: After displaying this, WAIT for the user to respond. Don't dump generic prompts.

---

## WAIT FOR USER'S VISION

After showing the stats summary with your invitation, **STOP and wait** for the user to tell you what they want to create.

When they respond with their vision (e.g., "I want a landing page mockup for my SaaS app"), THEN write a single, thoughtful, tailored prompt.

---

## WHEN USER SHARES THEIR VISION: Write ONE Perfect Prompt

Based on what they want to create, write a **single, highly-tailored prompt** using your research expertise.

### CRITICAL: Match the FORMAT the research recommends

**If research says to use a specific prompt FORMAT, YOU MUST USE THAT FORMAT:**

- Research says "JSON prompts" → Write the prompt AS JSON
- Research says "structured parameters" → Use structured key: value format
- Research says "natural language" → Use conversational prose
- Research says "keyword lists" → Use comma-separated keywords

**ANTI-PATTERN**: Research says "use JSON prompts with device specs" but you write plain prose. This defeats the entire purpose of the research.

### Output Format:

```
Here's your prompt for {TARGET_TOOL}:

---

[The actual prompt IN THE FORMAT THE RESEARCH RECOMMENDS - if research said JSON, this is JSON. If research said natural language, this is prose. Match what works.]

---

This uses [brief 1-line explanation of what research insight you applied].
```

### Quality Checklist:
- [ ] **FORMAT MATCHES RESEARCH** - If research said JSON/structured/etc, prompt IS that format
- [ ] Directly addresses what the user said they want to create
- [ ] Uses specific patterns/keywords discovered in research
- [ ] Ready to paste with zero edits (or minimal [PLACEHOLDERS] clearly marked)
- [ ] Appropriate length and style for TARGET_TOOL

---

## IF USER ASKS FOR MORE OPTIONS

Only if they ask for alternatives or more prompts, provide 2-3 variations. Don't dump a prompt pack unless requested.

---

## AFTER EACH PROMPT: Stay in Expert Mode

After delivering a prompt, offer to write more:

> Want another prompt? Just tell me what you're creating next.

---

## CONTEXT MEMORY

For the rest of this conversation, remember:
- **TOPIC**: {topic}
- **TARGET_TOOL**: {tool}
- **KEY PATTERNS**: {list the top 3-5 patterns you learned}
- **RESEARCH FINDINGS**: The key facts and insights from the research

**CRITICAL: After research is complete, you are now an EXPERT on this topic.**

When the user asks follow-up questions:
- **DO NOT run new WebSearches** - you already have the research
- **Answer from what you learned** - cite the Reddit threads, X posts, and web sources
- **If they ask for a prompt** - write one using your expertise
- **If they ask a question** - answer it from your research findings

Only do new research if the user explicitly asks about a DIFFERENT topic.

---

## Output Summary Footer (After Each Prompt)

After delivering a prompt, end with:

For **full/partial mode**:
```
---
📚 Expert in: {TOPIC} for {TARGET_TOOL}
📊 Based on: {n} Reddit threads ({sum} upvotes) + {n} X posts ({sum} likes) + {n} web pages

Want another prompt? Just tell me what you're creating next.
```

For **web-only mode**:
```
---
📚 Expert in: {TOPIC} for {TARGET_TOOL}
📊 Based on: {n} web pages from {domains}

Want another prompt? Just tell me what you're creating next.

💡 Unlock Reddit & X data: Add API keys to ~/.config/last30days/.env
```


path: SPEC.md
content: # last30days Skill Specification

## Overview

`last30days` is a Claude Code skill that researches a given topic across Reddit and X (Twitter) using the OpenAI Responses API and xAI Responses API respectively. It enforces a strict 30-day recency window, popularity-aware ranking, and produces actionable outputs including best practices, a prompt pack, and a reusable context snippet.

The skill operates in three modes depending on available API keys: **reddit-only** (OpenAI key), **x-only** (xAI key), or **both** (full cross-validation). It uses automatic model selection to stay current with the latest models from both providers, with optional pinning for stability.

## Architecture

The orchestrator (`last30days.py`) coordinates discovery, enrichment, normalization, scoring, deduplication, and rendering. Each concern is isolated in `scripts/lib/`:

- **env.py**: Load and validate API keys from `~/.config/last30days/.env`
- **dates.py**: Date range calculation and confidence scoring
- **cache.py**: 24-hour TTL caching keyed by topic + date range
- **http.py**: stdlib-only HTTP client with retry logic
- **models.py**: Auto-selection of OpenAI/xAI models with 7-day caching
- **openai_reddit.py**: OpenAI Responses API + web_search for Reddit
- **xai_x.py**: xAI Responses API + x_search for X
- **reddit_enrich.py**: Fetch Reddit thread JSON for real engagement metrics
- **normalize.py**: Convert raw API responses to canonical schema
- **score.py**: Compute popularity-aware scores (relevance + recency + engagement)
- **dedupe.py**: Near-duplicate detection via text similarity
- **render.py**: Generate markdown and JSON outputs
- **schema.py**: Type definitions and validation

## Embedding in Other Skills

Other skills can import the research context in several ways:

### Inline Context Injection
```markdown
## Recent Research Context
!python3 ~/.claude/skills/last30days/scripts/last30days.py "your topic" --emit=context
```

### Read from File
```markdown
## Research Context
!cat ~/.local/share/last30days/out/last30days.context.md
```

### Get Path for Dynamic Loading
```bash
CONTEXT_PATH=$(python3 ~/.claude/skills/last30days/scripts/last30days.py "topic" --emit=path)
cat "$CONTEXT_PATH"
```

### JSON for Programmatic Use
```bash
python3 ~/.claude/skills/last30days/scripts/last30days.py "topic" --emit=json > research.json
```

## CLI Reference

```
python3 ~/.claude/skills/last30days/scripts/last30days.py <topic> [options]

Options:
  --refresh           Bypass cache and fetch fresh data
  --mock              Use fixtures instead of real API calls
  --emit=MODE         Output mode: compact|json|md|context|path (default: compact)
  --sources=MODE      Source selection: auto|reddit|x|both (default: auto)
```

## Output Files

All outputs are written to `~/.local/share/last30days/out/`:

- `report.md` - Human-readable full report
- `report.json` - Normalized data with scores
- `last30days.context.md` - Compact reusable snippet for other skills
- `raw_openai.json` - Raw OpenAI API response
- `raw_xai.json` - Raw xAI API response
- `raw_reddit_threads_enriched.json` - Enriched Reddit thread data


path: TASKS.md
content: # last30days Implementation Tasks

## Setup & Configuration
- [x] Create directory structure
- [x] Write SPEC.md
- [x] Write TASKS.md
- [x] Write SKILL.md with proper frontmatter

## Core Library Modules
- [x] scripts/lib/env.py - Environment and API key loading
- [x] scripts/lib/dates.py - Date range and confidence utilities
- [x] scripts/lib/cache.py - TTL-based caching
- [x] scripts/lib/http.py - HTTP client with retry
- [x] scripts/lib/models.py - Auto model selection
- [x] scripts/lib/schema.py - Data structures
- [x] scripts/lib/openai_reddit.py - OpenAI Responses API
- [x] scripts/lib/xai_x.py - xAI Responses API
- [x] scripts/lib/reddit_enrich.py - Reddit thread JSON fetcher
- [x] scripts/lib/normalize.py - Schema normalization
- [x] scripts/lib/score.py - Popularity scoring
- [x] scripts/lib/dedupe.py - Near-duplicate detection
- [x] scripts/lib/render.py - Output rendering

## Main Script
- [x] scripts/last30days.py - CLI orchestrator

## Fixtures
- [x] fixtures/openai_sample.json
- [x] fixtures/xai_sample.json
- [x] fixtures/reddit_thread_sample.json
- [x] fixtures/models_openai_sample.json
- [x] fixtures/models_xai_sample.json

## Tests
- [x] tests/test_dates.py
- [x] tests/test_cache.py
- [x] tests/test_models.py
- [x] tests/test_score.py
- [x] tests/test_dedupe.py
- [x] tests/test_normalize.py
- [x] tests/test_render.py

## Validation
- [x] Run tests in mock mode
- [x] Demo --emit=compact
- [x] Demo --emit=context
- [x] Verify file tree


path: agents/openai.yaml
content: interface:
  display_name: "Last 30 Days"
  short_description: "Research any topic across Reddit, X, YouTube, and the web from the last 30 days. Returns synthesized expert answers and copy-paste prompts."
  default_prompt: "Research this topic from the last 30 days across Reddit, X, YouTube, and web. Synthesize what people are actually saying, upvoting, and sharing right now."
  brand_color: "#FF6B35"

policy:
  allow_implicit_invocation: true


path: docs/plans/2026-02-03-bird-cli-integration-design.md
content: # Bird CLI Integration Design

**Date:** 2026-02-03
**Status:** Approved

## Overview

Add Bird CLI as an alternative X/Twitter search source for the last30days skill. Bird uses browser cookie authentication (free, no API key) and provides direct access to X's GraphQL API.

## Goals

- Provide free X search without requiring xAI API key
- Seamless fallback: Bird → xAI → WebSearch
- Interactive onboarding for users without Bird installed
- Output parity with existing xAI implementation

## Detection & Priority Flow

```
On startup:
1. Check: Is Bird installed? (`which bird`)
   ├─ No → Offer to install: "Bird CLI not found. Install for free X search? (y/n)"
   │        ├─ Yes → Run `npm install -g @steipete/bird`
   │        └─ No → Continue to step 2
   │
   └─ Yes → Check: Is Bird authenticated? (`bird whoami`)
            ├─ Success → Use Bird for X searches
            └─ Fail → Show: "Bird auth failed. Run `bird check` to diagnose."
                      Continue to step 2

2. Fall back to xAI if XAI_API_KEY exists
3. Fall back to WebSearch if nothing else available
```

**Priority order:** Bird → xAI → WebSearch

## New Module: `scripts/lib/bird_x.py`

### Functions

- `is_bird_installed()` → checks `which bird`, returns bool
- `is_bird_authenticated()` → runs `bird whoami`, returns username or None
- `install_bird()` → runs `npm install -g @steipete/bird`, returns success bool
- `search_x(topic, from_date, to_date, depth)` → runs `bird search` with JSON output
- `parse_bird_response(json)` → converts to same format as `xai_x.parse_x_response()`

### Search Command

```bash
bird search "Claude Code skills" --since 2026-01-04 -n 30 --json
```

- `--since` filters to last 30 days
- `-n 30` controls result count (maps to depth: quick=12, default=30, deep=60)
- `--json` gives machine-readable output

### Output Mapping

| Bird field | Our field |
|------------|-----------|
| `text` | `text` |
| `permanent_url` | `url` |
| `user.screen_name` | `author_handle` |
| `created_at` | `date` (parse to YYYY-MM-DD) |
| `like_count` | `engagement.likes` |
| `retweet_count` | `engagement.reposts` |
| `reply_count` | `engagement.replies` |
| `quote_count` | `engagement.quotes` |

Relevance: Default to 0.7, let `score.py` re-rank based on engagement.

## Modified Files

| File | Change |
|------|--------|
| `env.py` | Add `get_x_source()` → returns `'bird'`, `'xai'`, or `None` |
| `last30days.py` | Check Bird availability with interactive install prompt before research |
| `last30days.py` | In `_search_x()`, dispatch to `bird_x` or `xai_x` based on source |
| `ui.py` | Add `prompt_bird_install()` and `show_bird_auth_help()` |

## Unchanged Files

- `normalize.py` - Bird output matches xAI format after parsing
- `score.py` - Same scoring logic applies
- `dedupe.py` - Same deduplication logic
- `render.py` - X results labeled as "X" regardless of backend

## Error Handling

| Scenario | Behavior |
|----------|----------|
| Bird installed but no browser cookies | Show `bird check` guidance, fall back to xAI |
| Bird search returns 0 results | Retry with simplified query (same as xAI logic) |
| Bird search times out | Fall back to xAI if available, else WebSearch |
| npm not installed (can't install Bird) | Skip Bird, continue with xAI/WebSearch |
| User declines Bird install | Remember for session, don't ask again |

**Timeout:** 30 seconds for Bird commands

## Output Labels

Results labeled as "X" regardless of whether Bird or xAI was used. Users care about the data, not the backend.


path: docs/plans/2026-02-06-feat-last30days-bird-cli-release-plan.md
content: ---
title: "feat: Release last30days v2 with Bird CLI to GitHub"
type: feat
date: 2026-02-06
---

# Release last30days v2 (Bird CLI) to GitHub

## Overview

Replace the current public `last30days-skill` on GitHub with the new Bird CLI-enhanced version from `last30days-skill-private`. The new version adds free X/Twitter search via Bird CLI while maintaining backward compatibility with xAI API keys.

**Goal:** Ship with confidence. No rollbacks.

## Current State

| | Old (Public) | New (Private) |
|---|---|---|
| **Repo** | `mvanhorn/last30days-skill` | `mvanhorn/last30days-skill-private` |
| **Local path** | `~/.claude/skills/last30days/` | `~/.claude/skills/last30daystest/` (symlink) |
| **Remote** | `origin` → public repo | `origin` → private, `upstream` → public |
| **Key addition** | -- | Bird CLI (`@steipete/bird`) for free X search |
| **X source chain** | xAI API only | Bird (free) → xAI (paid) → WebSearch |
| **Uses** | 136 | 18 |
| **Latest commit** | `cc892d7` | `4230fa2` |

**Why both show as `/last30days`:** Both `SKILL.md` files declare `name: last30days` in frontmatter. Claude Code discovers both from `~/.claude/skills/` and lists them separately.

---

## Phase 0: Clean Swap (Day 1)

Remove the old skill so only the new one is active. This eliminates ambiguity during testing.

### Steps

1. **Back up the old skill** (safety net):
   ```bash
   mv ~/.claude/skills/last30days ~/.claude/skills/last30days.backup-v1
   ```

2. **Promote the new skill to primary**:
   ```bash
   # Remove the test symlink
   rm ~/.claude/skills/last30daystest

   # Create new symlink with the primary name
   ln -s /Users/mvanhorn/last30days-skill-private ~/.claude/skills/last30days
   ```

3. **Verify only one `/last30days` appears**:
   - Open a new Claude Code session
   - Type `/last` and confirm only ONE `/last30days` shows in autocomplete
   - Confirm description mentions Bird CLI

4. **Rollback procedure** (if something goes wrong):
   ```bash
   rm ~/.claude/skills/last30days
   mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
   ```

### Acceptance Criteria
- [ ] Only one `/last30days` appears in Claude Code autocomplete
- [ ] Old skill preserved at `~/.claude/skills/last30days.backup-v1`
- [ ] New skill responds to `/last30days` invocation

---

## Phase 1: Claude's Test Plan (Automated)

These are tests Claude can run autonomously to validate the new skill before the user touches it.

### 1.1 Script-Level Smoke Tests

Run the Python scripts directly to verify core functionality without invoking the full skill.

#### Bird CLI Detection
```bash
# Test: Bird is installed and authenticated
python3 -c "
import sys; sys.path.insert(0, [REDACTED_SECRET])
import bird_x
print('installed:', bird_x.is_bird_installed())
print('authenticated:', bird_x.is_bird_authenticated())
print('status:', bird_x.get_bird_status())
"
```
- [ ] `is_bird_installed()` returns True (or False with clear message)
- [ ] `is_bird_authenticated()` returns True if logged into X in browser
- [ ] `get_bird_status()` returns a dict with `installed`, `authenticated`, `available` keys

#### Environment & Source Detection
```bash
python3 -c "
import sys; sys.path.insert(0, [REDACTED_SECRET])
import env
config = env.load_config()
print('x_source:', env.get_x_source(config))
print('has_openai:', bool(config.get('OPENAI_API_KEY')))
"
```
- [ ] `get_x_source()` returns `'bird'` if Bird available, `'xai'` if API key set, `None` otherwise
- [ ] Config loads from `~/.config/last30days/.env`

#### Bird Search (Direct)
```bash
python3 -c "
import sys, json; sys.path.insert(0, [REDACTED_SECRET])
import bird_x
result = bird_x.search_x('Claude Code tips', '2026-01-07', '2026-02-06', 'quick')
print(json.dumps(result, indent=2, default=str)[:2000])
"
```
- [ ] Returns search results (list of dicts with `url`, `text`, `author_handle`)
- [ ] No Python tracebacks
- [ ] Results are from the expected date range

#### Full Research Pipeline (Compact Output)
```bash
cd /Users/mvanhorn/last30days-skill-private
python3 scripts/last30days.py "Claude Code tips" --emit=compact --quick 2>&1 | head -100
```
- [ ] Completes without error
- [ ] Output includes X results (via Bird or xAI)
- [ ] Output includes Reddit results (via OpenAI) if key configured
- [ ] Stats summary shows source counts

### 1.2 Source Fallback Tests

Verify graceful degradation when sources are unavailable.

#### Bird unavailable, xAI available
```bash
# Temporarily hide Bird
PATH_BACKUP="$PATH"
export PATH=$(echo "$PATH" | tr ':' '\n' | grep -v "$(dirname $(which bird 2>/dev/null))" | tr '\n' ':')

python3 -c "
import sys; sys.path.insert(0, [REDACTED_SECRET])
import env
config = env.load_config()
print('x_source (no bird):', env.get_x_source(config))
"

export PATH="$PATH_BACKUP"
```
- [ ] Falls back to `'xai'` when Bird not in PATH
- [ ] No crash or unhandled exception

#### No X source at all
```bash
python3 -c "
import sys; sys.path.insert(0, [REDACTED_SECRET])
import env
config = {}  # empty config, no keys
print('x_source (nothing):', env.get_x_source(config))
"
```
- [ ] Returns `None`
- [ ] No crash

### 1.3 Response Parsing Tests

Validate that Bird responses are correctly normalized to the canonical schema.

```bash
python3 -c "
import sys; sys.path.insert(0, [REDACTED_SECRET])
import bird_x

# Test with sample Bird response format
sample = {
    'tweets': [{
        'permanentUrl': 'https://x.com/user/status/123',
        'text': 'Test tweet about Claude Code',
        'username': 'testuser',
        'likeCount': 42,
        'retweetCount': 10,
        'replyCount': 5,
        'timeParsed': '2026-02-01T12:00:00.000Z'
    }]
}
parsed = bird_x.parse_bird_response(sample)
print('Parsed count:', len(parsed))
print('First item keys:', sorted(parsed[0].keys()) if parsed else 'EMPTY')
print('URL:', parsed[0].get('url'))
print('Author:', parsed[0].get('author_handle'))
"
```
- [ ] Parses correctly with expected keys
- [ ] Handles both camelCase and snake_case fields
- [ ] URL, text, author, engagement metrics all present

### 1.4 SKILL.md Validation

```bash
# Verify YAML frontmatter parses correctly
python3 -c "
import yaml
with open('/Users/mvanhorn/last30days-skill-private/SKILL.md') as f:
    content = f.read()
    # Extract YAML between --- markers
    parts = content.split('---', 2)
    meta = yaml.safe_load(parts[1])
    print('name:', meta.get('name'))
    print('context:', meta.get('context'))
    print('agent:', meta.get('agent'))
    print('allowed-tools:', meta.get('allowed-tools'))
"
```
- [ ] `name` is `last30days` (not `last30daystest`)
- [ ] `context` is `fork`
- [ ] `agent` is `Explore`
- [ ] `allowed-tools` includes `Bash`, `WebSearch`

### 1.5 Diff Audit (Old vs New)

```bash
# Verify the only meaningful addition is bird_x.py
diff -rq ~/.claude/skills/last30days.backup-v1/scripts/lib/ \
         /Users/mvanhorn/last30days-skill-private/scripts/lib/ 2>/dev/null
```
- [ ] Only new file is `bird_x.py`
- [ ] Modified files: `env.py` (source detection), `__init__.py` (exports)
- [ ] No unexpected deletions or renames

---

## Phase 2: User's Test Plan (Manual)

These require human judgment - evaluating quality, UX, and real-world behavior.

### 2.1 Basic Invocation (5 min)

Open a fresh Claude Code session after Phase 0 is complete.

| # | Test | Command | Pass Criteria |
|---|------|---------|---------------|
| 1 | Simple topic | `/last30days AI music generation` | Returns results, shows source stats |
| 2 | Topic + tool | `/last30days Suno prompts for music production` | Returns results + generates a prompt |
| 3 | Quick mode | `/last30days --quick TypeScript tips` | Faster, fewer results, still valid |
| 4 | Empty input | `/last30days` | Prompts for topic (doesn't crash) |

### 2.2 Bird CLI Verification (5 min)

| # | Test | What to Check |
|---|------|---------------|
| 1 | Source indicator | Output shows Bird as X source (not xAI) |
| 2 | X results quality | X/Twitter results are real, recent, have engagement metrics |
| 3 | Bird promo | If Bird NOT installed, shows non-blocking info banner |
| 4 | Mixed sources | Both Reddit (OpenAI) and X (Bird) results appear |

### 2.3 Fallback Behavior (5 min)

| # | Test | Setup | Expected |
|---|------|-------|----------|
| 1 | No Bird | `npm uninstall -g @steipete/bird` temporarily | Falls back to xAI or WebSearch |
| 2 | No API keys | Rename `~/.config/last30days/.env` temporarily | WebSearch-only mode works |
| 3 | Restore | Reinstall bird + restore .env | Full mode returns |

### 2.4 Output Quality (10 min)

Run 3 real research queries you care about. For each, evaluate:

- [ ] Results are actually from the last 30 days (not stale)
- [ ] Engagement metrics (likes, upvotes) are present and reasonable
- [ ] No duplicate results
- [ ] Sources are properly cited with URLs
- [ ] Synthesis is grounded in actual results (not hallucinated)
- [ ] Generated prompts (if requested) are usable

### 2.5 Comparison Test (10 min)

Before removing the backup, run the SAME query on both versions:

```bash
# New version (active)
/last30days [your topic]

# Old version (temporarily restore)
rm ~/.claude/skills/last30days
mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
# New Claude Code session
/last30days [same topic]
# Then swap back
```

- [ ] New version produces equal or better results
- [ ] No features regressed
- [ ] Bird results add value over xAI-only

---

## Phase 3: Release Plan (30-Day Timeline)

**Target release date:** March 1, 2026 (conservative buffer before March 8 deadline)

### Week 1: Feb 6-12 - Clean & Test

| Day | Task | Owner |
|-----|------|-------|
| Feb 6 | Phase 0: Clean swap (remove old, activate new) | User |
| Feb 6 | Phase 1: Claude runs automated tests | Claude |
| Feb 7-8 | Phase 2: User runs manual tests (2.1-2.4) | User |
| Feb 9 | Phase 2.5: Comparison test | User |
| Feb 10-12 | Fix any issues found during testing | Claude + User |

### Week 2: Feb 13-19 - Harden

| Day | Task | Owner |
|-----|------|-------|
| Feb 13 | Run edge cases: unicode topics, very long topics, special chars | Claude |
| Feb 14 | Test with Bird logged out (auth expiry scenario) | User |
| Feb 15 | Review all error messages for clarity | Claude |
| Feb 16-17 | Update README.md with Bird CLI setup instructions | Claude |
| Feb 18-19 | Buffer for fixes | Claude + User |

### Week 3: Feb 20-26 - Pre-Release

| Day | Task | Owner |
|-----|------|-------|
| Feb 20 | Final diff audit: private repo vs public repo | Claude |
| Feb 21 | Strip any private/test artifacts (test symlinks, debug prints) | Claude |
| Feb 22 | Update SKILL.md description if needed | Claude |
| Feb 23 | Dry-run: push to a branch on public repo (not main) | User |
| Feb 24 | Test installation from the branch (fresh `~/.claude/skills/`) | User |
| Feb 25-26 | Buffer for fixes | Claude + User |

### Week 4: Feb 27 - Mar 1 - Ship

| Day | Task | Owner |
|-----|------|-------|
| Feb 27 | Merge branch to main on public repo | User |
| Feb 28 | Create GitHub release with changelog | Claude + User |
| Mar 1 | Delete backup: `rm -rf ~/.claude/skills/last30days.backup-v1` | User |
| Mar 1 | Archive private repo (optional) | User |

### Release Checklist (Final Gate)

Before merging to `main` on the public repo:

- [ ] All Phase 1 automated tests pass
- [ ] All Phase 2 manual tests pass
- [ ] Comparison test shows new >= old quality
- [ ] SKILL.md frontmatter is correct (`name: last30days`, not `last30daystest`)
- [ ] README.md documents Bird CLI setup
- [ ] No debug/test artifacts in codebase
- [ ] No hardcoded paths (e.g., `/Users/mvanhorn/...`)
- [ ] `.env` files are gitignored
- [ ] Git history is clean (no "test" or "WIP" commits on main)
- [ ] Bird CLI failure doesn't break the skill (graceful fallback verified)

### Rollback Plan (Emergency)

If something goes wrong after release:

```bash
# Option 1: Revert to backup (if still exists)
rm ~/.claude/skills/last30days
mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days

# Option 2: Git revert on public repo
cd ~/.claude/skills/last30days
git log --oneline -5  # find the last good commit
git revert HEAD        # revert the merge commit
git push origin main

# Option 3: Pin to old version
cd ~/.claude/skills/last30days
git checkout cc892d7   # last known good commit from old version
```

---

## Risk Analysis

| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Bird CLI breaks after X API changes | Medium | Low | Fallback to xAI/WebSearch still works |
| Bird auth expires silently | Medium | Low | `is_bird_authenticated()` check + user message |
| Old xAI workflows regress | Low | High | Comparison test in Phase 2.5 |
| Hardcoded paths in codebase | Low | Medium | Grep for `/Users/mvanhorn` before release |
| SKILL.md name still says `last30daystest` | Low | High | Already fixed in commit `4e972d0` |

## References

- Private repo: `https://github.com/mvanhorn/last30days-skill-private.git`
- Public repo: `https://github.com/mvanhorn/last30days-skill.git`
- Bird CLI: `https://github.com/steipete/bird`
- Bird implementation plan: `docs/plans/2026-02-03-bird-cli-implementation.md`
- Bird integration design: `docs/plans/2026-02-03-bird-cli-integration-design.md`


path: fixtures/models_openai_sample.json
content: {
  "object": "list",
  "data": [
    {
      "id": "gpt-5.2",
      "object": "model",
      "created": 1704067200,
      "owned_by": "openai"
    },
    {
      "id": "gpt-5.1",
      "object": "model",
      "created": 1701388800,
      "owned_by": "openai"
    },
    {
      "id": "gpt-5",
      "object": "model",
      "created": 1698710400,
      "owned_by": "openai"
    },
    {
      "id": "gpt-5-mini",
      "object": "model",
      "created": 1704067200,
      "owned_by": "openai"
    },
    {
      "id": "gpt-4o",
      "object": "model",
      "created": 1683158400,
      "owned_by": "openai"
    },
    {
      "id": "gpt-4-turbo",
      "object": "model",
      "created": 1680566400,
      "owned_by": "openai"
    }
  ]
}


path: fixtures/models_xai_sample.json
content: {
  "object": "list",
  "data": [
    {
      "id": "grok-4-latest",
      "object": "model",
      "created": 1704067200,
      "owned_by": "xai"
    },
    {
      "id": "grok-4",
      "object": "model",
      "created": 1701388800,
      "owned_by": "xai"
    },
    {
      "id": "grok-3",
      "object": "model",
      "created": 1698710400,
      "owned_by": "xai"
    }
  ]
}


path: scripts/lib/__init__.py
content: # last30days library modules


path: scripts/lib/vendor/bird-search/lib/features.json
content: {
  "global": {
    "responsive_web_grok_annotations_enabled": false,
    "post_ctas_fetch_enabled": true,
    "responsive_web_graphql_exclude_directive_enabled": true
  },
  "sets": {
    "lists": {
      "blue_business_profile_image_shape_enabled": true,
      "tweetypie_unmention_optimization_enabled": true,
      "responsive_web_text_conversations_enabled": false,
      "interactive_text_enabled": true,
      "vibe_api_enabled": true,
      "responsive_web_twitter_blue_verified_badge_is_enabled": true
    }
  }
}


path: scripts/lib/vendor/bird-search/lib/twitter-client-types.js
content: export {};
//# sourceMappingURL=twitter-client-types.js.map

path: scripts/lib/vendor/bird-search/package.json
content: {
  "name": "bird-search",
  "version": "0.8.0",
  "description": "Vendored Bird CLI search subset for /last30days",
  "type": "module",
  "main": "bird-search.mjs",
  "private": true,
  "engines": {
    "node": ">=22"
  },
  "license": "MIT",
  "attribution": "Based on @steipete/bird v0.8.0 by Peter Steinberger (MIT License)"
}


path: tests/__init__.py
content: # last30days tests


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)