mvanhorn/last30days-skillMIT135 files

Last30days

Research what people actually say about any topic in the last 30 days. Pulls posts and engagement from Reddit, X, YouTube, TikTok, Hacker News, Polymarket, GitHub, and the web. Includes a doctor health check to diagnose broken or missing sources.

Specification
Skill ID
mvanhorn/last30days-skill/last30days
Publisher
mvanhorn
Repository
last30days-skill
Installs
268
Files
135
License
MIT
Synced
Sep 16, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

mvanhorn/last30days-skill/last30daysInstalls these files
  • .skillignore
  • SKILL.md
  • agents/openai.yaml
  • assets/aging-portrait.jpeg
  • assets/claude-code-rap.mp3
  • assets/dog-as-human.png
  • assets/dog-original.jpeg
  • assets/swimmom-mockup.jpeg
  • references/save-html-brief.md
  • scripts/box_chrome_login.py
  • scripts/briefing.py
  • scripts/build-skill.sh
  • scripts/compare.sh
  • scripts/evaluate_search_quality.py
  • scripts/last30days.py
  • scripts/lib/__init__.py
  • scripts/lib/agentcookie.py
  • scripts/lib/amazon.py
  • scripts/lib/arxiv.py
  • scripts/lib/backends.py
  • scripts/lib/bird_x.py
  • scripts/lib/bluesky.py
  • scripts/lib/brightdata.py
  • scripts/lib/categories.py
  • scripts/lib/chrome_cdp.py
  • scripts/lib/chrome_cookies.py
  • scripts/lib/cjk.py
  • scripts/lib/cluster.py
  • scripts/lib/competitors.py
  • scripts/lib/cookie_extract.py
  • scripts/lib/corpus.py
  • scripts/lib/dates.py
  • scripts/lib/dedupe.py
  • scripts/lib/digg.py
  • scripts/lib/discovery_handoff.py
  • scripts/lib/doctor.py
  • scripts/lib/dripstack.py
  • scripts/lib/entity_extract.py
  • scripts/lib/env.py
  • scripts/lib/fanout.py
  • scripts/lib/feed.py
  • scripts/lib/freshness.py
  • scripts/lib/fusion.py
  • scripts/lib/github.py
  • scripts/lib/grok_x.py
  • scripts/lib/grounding.py
  • scripts/lib/hackernews.py
  • scripts/lib/health.py
  • scripts/lib/hiring_signals.py
  • scripts/lib/hosted.py
  • scripts/lib/html_publish.py
  • scripts/lib/html_render.py
  • scripts/lib/http.py
  • scripts/lib/instagram.py
  • scripts/lib/jobs.py
  • scripts/lib/library.py
  • scripts/lib/library_index.py
  • scripts/lib/linkedin.py
  • scripts/lib/log.py
  • scripts/lib/meta_ads.py
  • scripts/lib/normalize.py
  • scripts/lib/parallel_mcp.py
  • scripts/lib/permission_preflight.py
  • scripts/lib/perplexity.py
  • scripts/lib/pinterest.py
  • scripts/lib/pipeline.py
  • scripts/lib/planner.py
  • scripts/lib/polymarket.py
  • scripts/lib/preflight.py
  • scripts/lib/prescriptions.py
  • scripts/lib/providers.py
  • scripts/lib/quality_nudge.py
  • scripts/lib/query.py
  • scripts/lib/reddit.py
  • scripts/lib/reddit_arctic.py
  • scripts/lib/reddit_enrich.py
  • scripts/lib/reddit_keyless.py
  • scripts/lib/reddit_listing.py
  • scripts/lib/reddit_public.py
  • scripts/lib/reddit_rss.py
  • scripts/lib/reddit_shreddit.py
  • scripts/lib/registers.py
  • scripts/lib/relevance.py
  • scripts/lib/render.py
  • scripts/lib/rerank.py
  • scripts/lib/resolve.py
  • scripts/lib/safari_cookies.py
  • scripts/lib/schema.py
  • scripts/lib/setup_wizard.py
  • scripts/lib/signals.py
  • scripts/lib/skill_meta.py
  • scripts/lib/snippet.py
  • scripts/lib/stocktwits.py
  • scripts/lib/subproc.py
  • scripts/lib/techmeme.py
  • scripts/lib/telegram.py
  • scripts/lib/threads.py
  • scripts/lib/tiktok.py
  • scripts/lib/topic_shape.py
  • scripts/lib/transcribe.py
  • scripts/lib/trustpilot.py
  • scripts/lib/truthsocial.py
  • scripts/lib/ui.py
  • scripts/lib/vendor/bird-search/LICENSE
  • scripts/lib/vendor/bird-search/bird-search.mjs
  • scripts/lib/vendor/bird-search/lib/cookies.js
  • scripts/lib/vendor/bird-search/lib/features.json
  • scripts/lib/vendor/bird-search/lib/paginate-cursor.js
  • scripts/lib/vendor/bird-search/lib/query-ids.json
  • scripts/lib/vendor/bird-search/lib/runtime-features.js
  • scripts/lib/vendor/bird-search/lib/runtime-query-ids.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-base.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-constants.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-features.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-search.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-types.js
  • scripts/lib/vendor/bird-search/lib/twitter-client-utils.js
  • scripts/lib/vendor/bird-search/package.json
  • scripts/lib/web_fetch_keyless.py
  • scripts/lib/web_search_keyless.py
  • scripts/lib/x_api.py
  • scripts/lib/x_envelope.py
  • scripts/lib/x_judge.py
  • scripts/lib/xai_x.py
  • scripts/lib/xiaohongshu_api.py
  • scripts/lib/xquik.py
  • scripts/lib/xurl_x.py
  • scripts/lib/youtube_yt.py
  • scripts/setup-keychain.sh
  • scripts/setup-pass.sh
  • scripts/store.py
  • scripts/test-v1-vs-v2.sh
  • scripts/test_device_auth.py
  • scripts/verify_v3.py
  • scripts/watchlist.py

What this skill tells the agent

STEP 0: STALE-CLONE SELF-CHECK — RUN BEFORE READING BELOW

Before reading anything else in this file, check whether you loaded SKILL.md from the one known stale-clone location: Claude Code's marketplaces directory.

Run this check:

CLAUDE_CACHE_LATEST=$(find "$HOME/.claude/plugins/cache/last30days-skill/last30days" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -1)
# Two cache layouts ship in the wild — nested ({version}/skills/last30days/SKILL.md)
# and flat ({version}/SKILL.md). Resolve to whichever shape actually exists.
CLAUDE_CACHE_SKILL_MD=""
if [ -n "$CLAUDE_CACHE_LATEST" ]; then
  if [ -f "$CLAUDE_CACHE_LATEST/skills/last30days/SKILL.md" ]; then
    CLAUDE_CACHE_SKILL_MD="$CLAUDE_CACHE_LATEST/skills/last30days/SKILL.md"
  elif [ -f "$CLAUDE_CACHE_LATEST/SKILL.md" ]; then
    CLAUDE_CACHE_SKILL_MD="$CLAUDE_CACHE_LATEST/SKILL.md"
  fi
fi
echo "CLAUDE_CACHE_SKILL_MD=$CLAUDE_CACHE_SKILL_MD"

If the SKILL.md path you just Read contains /.claude/plugins/marketplaces/ AND $CLAUDE_CACHE_SKILL_MD is non-empty, STOP and re-read $CLAUDE_CACHE_SKILL_MD before proceeding. Otherwise the SKILL.md you have is fine — continue.

Why this specific check: ~/.claude/plugins/marketplaces/last30days-skill/ is a git clone Claude Code auto-restores to origin/main on session start. It can lag the versioned cache by one or more releases. Three 2026-04-22 test runs (Linear, Coinbase) loaded SKILL.md from marketplaces/, ran --help from the same stale path, did not see the --competitors flag that existed in the cache, and fell back to a manual comparison plan. Result: 2 of 3 windows never invoked the feature they were asked to test. STEP 0 defends against that one Claude Code-specific bug.

Other install paths are fine: ~/.codex/skills/, ~/.agents/skills/, an npx skills add install dir, or a repo checkout are all valid load points - the resolver in Step 1 picks them up. Do NOT abort or hop on those paths.


SKILL CONTRACT — READ BEFORE ANY TOOL CALL

You are inside the /last30days SKILL. This is a specific research tool with a 1400+ line instruction contract (the rest of this file) that defines EXACTLY how to produce the research output. It is not a generic "last 30 days of X" research prompt. Do NOT treat /last30days as a search keyword you can improvise against.

Named failure mode (2026-04-18 public v3.0.6 0/8 regression): on 8 consecutive public invocations, Opus 4.7 treated /last30days as a generic research keyword and improvised. Every single run violated LAW 2 (invented titles like "The headline", "Kanye West: the last 30 days"), LAW 4 (section headers like "Why he is everywhere this month", "1. gstack dominates", "The 'Homecoming' peak"), or both. One run (Matt Van Horn) skipped Step 0.5 / Step 0.55 entirely and ran the engine bare with zero resolution flags. Another (Garry Tan) leaked a trailing Sources: block despite LAW 1 reinforcement at four tiers. Two runs (Peter Steinberger, Kanye vs Kim) landed on a stale ~/.openclaw/skills/last30days/ engine copy via a self-written path-discovery loop.

How v3.0.7 fixes it: three structural anchors.

  1. The MANDATORY first-line badge (🌐 last30days v{VERSION} · synced {YYYY-MM-DD}) at the top of every response is the LAW 2 / LAW 4 enforcement anchor. See "BADGE (MANDATORY, FIRST LINE OF OUTPUT)" in the synthesis section.
  2. The SKILL_DIR substitution in the engine Bash calls uses the directory of the SKILL.md the model just Read — no resolver list, no precedence walk. Whichever install the harness loaded SKILL.md from is the install whose engine runs. Aligns spec-with-code and works for any harness without enumerating its install path.
  3. This preface tells you plainly: do NOT improvise. Follow SKILL.md top to bottom.

If you catch yourself about to write a ## section header in a GENERAL-query body, a custom title line, a Sources: bullet list, a for dir in ... path-discovery loop, or a bare python3 scripts/last30days.py "{TOPIC}" engine call with no pre-flight flags — stop. Those are the exact failure modes the LAWs and this contract exist to prevent. The 10/10 beta validation from 2026-04-18 and the 0/8 public v3.0.6 regression from the same day had THE SAME MODEL and SIMILAR SKILL.md CONTENT; the delta is the three anchors this release restores. Read SKILL.md top to bottom before emitting your first response.


OUTPUT CONTRACT (BADGE + LAWS — READ BEFORE EMITTING YOUR RESPONSE)

These anchors used to live at line 1094 of this file. Three independent Opus 4.7 self-debugs on 2026-04-18 confirmed the file was too long to reach them before synthesis. Moved here in v3.0.8. Do not synthesize without reading this section.

BADGE (MANDATORY, FIRST LINE OF OUTPUT): The Python engine now emits the badge as the first line of its --emit=compact stdout. Your correct behavior is to PASS THROUGH the script's output verbatim. If you are writing your own synthesis from scratch and need to emit the badge yourself, use:

🌐 last30days v{VERSION} · synced {YYYY-MM-DD}

Replace {VERSION} with the installed plugin version (jq -r '.version' "$SKILL_DIR/../../.claude-plugin/plugin.json" 2>/dev/null || awk '/^version:/{gsub(/"/,"",$2); print $2; exit}' "$SKILL_DIR/SKILL.md") and {YYYY-MM-DD} with today's date. No other text on this line. One blank line after, then the synthesis begins.

Why the badge is MANDATORY: it is the structural anchor for the canonical output shape. Without it the model drifts into blog-post narrative format with ## section headers and invented titles, violating LAW 2 and LAW 4. The 2026-04-18 public v3.0.6 0/8 regression produced outputs with section headers like "The headline", "Why he is everywhere", "1. gstack dominates", "The 'Homecoming' peak". Direct cause: this anchor was absent. Do NOT skip the badge. Do NOT describe it. Do NOT paraphrase it. Emit it verbatim as line 1.

Placement by query type:

  • GENERAL / NEWS / PROMPTING / RECOMMENDATIONS: badge on line 1, blank line 2, What I learned: on line 3, then bold-lead-in paragraphs
  • COMPARISON: badge on line 1, blank line 2, # {TOPIC_A} vs {TOPIC_B} [vs {TOPIC_C}]: What the Community Says (/Last30Days) on line 3, then Quick Verdict section
  • DISCOVERY: pass through the engine's topic-per-section discovery brief verbatim. Its ranked headings, momentum labels, community-voice quotes, evidence counters, /last30days "<topic>" handoffs, and the "Nothing solid this window" empty state are engine-owned and are an explicit exception to the GENERAL synthesis template. A nothing-solid result is a valid final answer — relay it, never retry or fabricate topics around it. Trend cards also carry **Podcast angle:** and **X article angle:** lines (host-authored: YOU wrote them via the leg-3 angles file of the discovery protocol, and the engine rendered them into the brief) plus an engine-owned **Pipeline:** line (annotating topics surfaced in a prior discovery run or already marked covered in the persistent topic queue). All three lines are part of the verbatim relay - at relay time never strip, rewrite, or paraphrase them, even the angle lines whose text originated with you.

VOICE CONTRACT LAW (non-negotiable, read before synthesis)