feat(digg): rename to 'Digg' and bump per-cluster post limits (#372)

* feat(digg): bump POSTS_PER_CLUSTER to 5 and render limit to 3

Match the per-item enrichment cap and inline-display cap used by the
other sources (Reddit, HN, YouTube, TikTok, GitHub all use 5 fetched /
3 displayed). At the previous 3/2 caps the engine routinely truncated
cluster context — a recent run on cli-printing-press lost the Jason
Calacanis quote tweet entirely because the display cut off after Garry
Tan's first two posts.

* feat(digg): rename 'Digg AI 1000' to 'Digg' in user-facing strings

Drop the 'AI 1000' suffix from the footer line, source label, inline
quote attribution ('via Digg'), why_relevant, container, mock title,
SKILL.md source list, and README sources table. Internal code comments
and docstrings still reference the upstream Digg AI 1000 product.

Bumps version to 3.2.1 and adds a CHANGELOG entry covering this rename
and the POSTS_PER_CLUSTER / render-limit bumps from the prior commit.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
Matt Van Horn
2026-05-09 21:04:23 -07:00
committed by GitHub
parent 80392061d4
commit dc934ddb6a
11 changed files with 31 additions and 25 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
{ {
"name": "last30days", "name": "last30days",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.", "description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.",
"version": "3.2.0", "version": "3.2.1",
"author": { "author": {
"name": "Matt Van Horn", "name": "Matt Van Horn",
"url": "https://github.com/mvanhorn" "url": "https://github.com/mvanhorn"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "last30days", "name": "last30days",
"version": "3.2.0", "version": "3.2.1",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.", "description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.",
"author": { "author": {
"name": "Matt Van Horn", "name": "Matt Van Horn",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "last30days", "name": "last30days",
"version": "3.2.0", "version": "3.2.1",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.", "description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.",
"author": { "author": {
"name": "Matt Van Horn", "name": "Matt Van Horn",
+5
View File
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Changed
- Rename "Digg AI 1000" to just "Digg" in user-facing output (footer line, source label, inline-quote suffix, why_relevant, container attribution). Internal references to the upstream Digg AI 1000 product remain in code comments and docstrings.
- Bump `POSTS_PER_CLUSTER` from 3 to 5 and the render-side display limit from 2 to 3 to match the per-source enrichment caps used by Reddit, HN, YouTube, TikTok, and GitHub. The previous 3/2 caps routinely truncated cluster context (e.g. dropped a Jason Calacanis quote tweet on a `cli-printing-press` run).
## [3.2.0] - 2026-05-09 ## [3.2.0] - 2026-05-09
### Added ### Added
+1 -1
View File
@@ -68,7 +68,7 @@ If you're meeting with a CEO, have you read all their tweets and YouTube transcr
| **Hacker News** | The developer consensus. 825 points, 899 comments. Where technical people actually argue. | | **Hacker News** | The developer consensus. 825 points, 899 comments. Where technical people actually argue. |
| **Polymarket** | Not opinions. Odds. Backed by real money. 96% confidence on album sales. 4% on an acquisition. | | **Polymarket** | Not opinions. Odds. Backed by real money. 96% confidence on album sales. 4% on an acquisition. |
| **GitHub** | For people: PR velocity, top repos by stars, release notes. For topics: issues and discussions. | | **GitHub** | For people: PR velocity, top repos by stars, release notes. For topics: issues and discussions. |
| **Digg AI 1000** | Curated story clusters from ~1000 high-signal AI accounts on X, with attributable inline quotes (no X auth required). Auto-enabled when `digg-pp-cli` is on PATH. | | **Digg** | Curated story clusters from Digg's AI 1000 leaderboard (~1000 high-signal AI accounts on X), with attributable inline quotes (no X auth required). Auto-enabled when `digg-pp-cli` is on PATH. |
| **Threads** | The post-Twitter text layer. Conversations from creators and brands. | | **Threads** | The post-Twitter text layer. Conversations from creators and brands. |
| **Pinterest** | Visual discovery. Pins, saves, and comments on products and ideas. | | **Pinterest** | Visual discovery. Pins, saves, and comments on products and ideas. |
| **Bluesky** | The decentralized social layer. AT Protocol posts from the post-Twitter migration. | | **Bluesky** | The decentralized social layer. AT Protocol posts from the post-Twitter migration. |
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "last30days-skill" name = "last30days-skill"
version = "3.2.0" version = "3.2.1"
description = "Multi-source last-30-days research skill" description = "Multi-source last-30-days research skill"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+4 -4
View File
@@ -1,6 +1,6 @@
--- ---
name: last30days name: last30days
version: "3.2.0" version: "3.2.1"
description: "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." description: "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."
argument-hint: 'last30days nvidia earnings reaction | last30days AI video tools | last30days what users want in react' argument-hint: 'last30days nvidia earnings reaction | last30days AI video tools | last30days what users want in react'
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
@@ -234,7 +234,7 @@ If your Bash call to `last30days.py` does NOT include the FULL pre-flight checkl
--- ---
# last30days v3.2.0: Research Any Topic from the Last 30 Days # last30days v3.2.1: Research Any Topic from the Last 30 Days
> **Permissions overview:** Reads public web/platform data and optionally saves research briefings to `LAST30DAYS_MEMORY_DIR` (defaults to `~/Documents/Last30Days`). X/Twitter search uses optional user-provided tokens (AUTH_TOKEN/CT0 env vars). Bluesky search uses optional app password (BSKY_HANDLE/BSKY_APP_PASSWORD env vars - create at bsky.app/settings/app-passwords). All credential usage and data writes are documented in the [Security & Permissions](#security--permissions) section. > **Permissions overview:** Reads public web/platform data and optionally saves research briefings to `LAST30DAYS_MEMORY_DIR` (defaults to `~/Documents/Last30Days`). X/Twitter search uses optional user-provided tokens (AUTH_TOKEN/CT0 env vars). Bluesky search uses optional app password (BSKY_HANDLE/BSKY_APP_PASSWORD env vars - create at bsky.app/settings/app-passwords). All credential usage and data writes are documented in the [Security & Permissions](#security--permissions) section.
@@ -318,7 +318,7 @@ Common patterns:
- Always active: Reddit, Hacker News, Polymarket - Always active: Reddit, Hacker News, Polymarket
- If gh CLI is installed (check `which gh`): add GitHub - If gh CLI is installed (check `which gh`): add GitHub
- If digg-pp-cli is installed (check `which digg-pp-cli`): add Digg AI 1000 - If digg-pp-cli is installed (check `which digg-pp-cli`): add Digg
- If AUTH_TOKEN/CT0 or XAI_API_KEY or FROM_BROWSER is set, or xurl CLI is installed and authenticated: add X - If AUTH_TOKEN/CT0 or XAI_API_KEY or FROM_BROWSER is set, or xurl CLI is installed and authenticated: add X
- If yt-dlp is installed (check `which yt-dlp`): add YouTube - If yt-dlp is installed (check `which yt-dlp`): add YouTube
- If SCRAPECREATORS_API_KEY is set and INCLUDE_SOURCES contains tiktok: add TikTok - If SCRAPECREATORS_API_KEY is set and INCLUDE_SOURCES contains tiktok: add TikTok
@@ -826,7 +826,7 @@ Only show lines for platforms where something was resolved. Skip empty lines. On
- For how_to: prioritize YouTube (tutorials) and Reddit (guides) - For how_to: prioritize YouTube (tutorials) and Reddit (guides)
- Primary subquery weight = 1.0, secondary = 0.6-0.8, peripheral = 0.3-0.5 - Primary subquery weight = 1.0, secondary = 0.6-0.8, peripheral = 0.3-0.5
**Available sources (include ALL in primary subquery):** reddit, x, youtube, tiktok, instagram, hackernews, polymarket. Optional: bluesky, truthsocial, threads, pinterest, grounding (web search - only if user has Brave/Exa/Serper key), digg (Digg AI 1000 clusters - only if `digg-pp-cli` is on PATH) **Available sources (include ALL in primary subquery):** reddit, x, youtube, tiktok, instagram, hackernews, polymarket. Optional: bluesky, truthsocial, threads, pinterest, grounding (web search - only if user has Brave/Exa/Serper key), digg (Digg clusters - only if `digg-pp-cli` is on PATH)
**Intent → freshness_mode mapping:** **Intent → freshness_mode mapping:**
- breaking_news, prediction → `strict_recent` - breaking_news, prediction → `strict_recent`
+5 -4
View File
@@ -44,8 +44,9 @@ ENRICH_CONFIG = {
"deep": 5, "deep": 5,
} }
# X posts pulled per enriched cluster. # X posts pulled per enriched cluster. Matches the 5-comment cap used by
POSTS_PER_CLUSTER = 3 # Reddit/HN/YouTube/TikTok/GitHub enrichment.
POSTS_PER_CLUSTER = 5
SEARCH_TIMEOUT = 30 SEARCH_TIMEOUT = 30
POSTS_TIMEOUT = 15 POSTS_TIMEOUT = 15
@@ -285,9 +286,9 @@ def parse_digg_response(
"posts": [], "posts": [],
"relevance": round(relevance, 2), "relevance": round(relevance, 2),
"why_relevant": ( "why_relevant": (
f"Digg AI 1000 cluster (rank {rank}, {post_count} posts, {unique_authors} authors)" f"Digg cluster (rank {rank}, {post_count} posts, {unique_authors} authors)"
if rank is not None if rank is not None
else f"Digg AI 1000 cluster ({post_count} posts, {unique_authors} authors)" else f"Digg cluster ({post_count} posts, {unique_authors} authors)"
), ),
} }
) )
+2 -2
View File
@@ -412,7 +412,7 @@ def _normalize_digg(
Each cluster is one item. The TLDR carries the most useful body for Each cluster is one item. The TLDR carries the most useful body for
rerank and synthesis. Top-ranked X posts attached at search time are rerank and synthesis. Top-ranked X posts attached at search time are
passed through under metadata['posts'] so render can emit them as passed through under metadata['posts'] so render can emit them as
inline 'via Digg AI 1000' quotes. inline 'via Digg' quotes.
""" """
title = str(item.get("title") or "").strip() title = str(item.get("title") or "").strip()
tldr = str(item.get("tldr") or "").strip() tldr = str(item.get("tldr") or "").strip()
@@ -428,7 +428,7 @@ def _normalize_digg(
body=body, body=body,
url=str(item.get("url") or f"https://di.gg/ai/{cluster_url_id}"), url=str(item.get("url") or f"https://di.gg/ai/{cluster_url_id}"),
author="", author="",
container="Digg AI 1000", container="Digg",
published_at=item.get("date"), published_at=item.get("date"),
date_confidence=_date_confidence(item, from_date, to_date, default="high"), date_confidence=_date_confidence(item, from_date, to_date, default="high"),
engagement=item.get("engagement") or {}, engagement=item.get("engagement") or {},
+2 -2
View File
@@ -538,7 +538,7 @@ def _finalize_items_by_source(
if source == "digg" and items: if source == "digg" and items:
# Pull top-ranked X posts only for the survivors that will appear # Pull top-ranked X posts only for the survivors that will appear
# in the brief. Spending the enrichment budget here (rather than # in the brief. Spending the enrichment budget here (rather than
# at retrieval time) keeps the inline 'via Digg AI 1000' quotes # at retrieval time) keeps the inline 'via Digg' quotes
# paired with the clusters dedupe actually kept. # paired with the clusters dedupe actually kept.
digg.enrich_source_items(items, top_k=3) digg.enrich_source_items(items, top_k=3)
finalized[source] = items finalized[source] = items
@@ -1078,7 +1078,7 @@ def _mock_stream_results(source: str, subquery: schema.SubQuery) -> tuple[list[d
"digg": [ "digg": [
{ {
"id": "mock1abc", "id": "mock1abc",
"title": f"Digg AI 1000 cluster about {subquery.search_query}", "title": f"Digg cluster about {subquery.search_query}",
"url": "https://di.gg/ai/mock1abc", "url": "https://di.gg/ai/mock1abc",
"tldr": f"Curated cluster summarizing recent {subquery.search_query} discussion across the AI 1000.", "tldr": f"Curated cluster summarizing recent {subquery.search_query} discussion across the AI 1000.",
"author": "", "author": "",
+8 -8
View File
@@ -53,7 +53,7 @@ SOURCE_LABELS = {
"xiaohongshu": "Xiaohongshu", "xiaohongshu": "Xiaohongshu",
"x": "X", "x": "X",
"github": "GitHub", "github": "GitHub",
"digg": "Digg AI 1000", "digg": "Digg",
"perplexity": "Perplexity", "perplexity": "Perplexity",
} }
@@ -853,7 +853,7 @@ def render_full(report: schema.Report) -> str:
tc_score = tc.get("score", "") tc_score = tc.get("score", "")
attribution = _comment_attribution(item.source, tc.get("author")) attribution = _comment_attribution(item.source, tc.get("author"))
lines.append(f" Top comment {attribution} ({tc_score} {vote_label}): {excerpt}") lines.append(f" Top comment {attribution} ({tc_score} {vote_label}): {excerpt}")
# Digg AI 1000: inline X-post quotes attached to the cluster. # Digg: inline X-post quotes attached to the cluster.
for post in _digg_posts_for(item, limit=3): for post in _digg_posts_for(item, limit=3):
lines.append(f" > {_format_digg_quote(post)}") lines.append(f" > {_format_digg_quote(post)}")
# Comment insights for Reddit # Comment insights for Reddit
@@ -1229,7 +1229,7 @@ _FOOTER_SOURCES: list[tuple[str, str, str, str, list[tuple[str, str]]]] = [
("bluesky", "🦋", "Bluesky", "post", [("likes", "likes"), ("reposts", "reposts")]), ("bluesky", "🦋", "Bluesky", "post", [("likes", "likes"), ("reposts", "reposts")]),
("truthsocial", "🇺🇸", "Truth Social", "post", [("likes", "likes"), ("reposts", "reposts")]), ("truthsocial", "🇺🇸", "Truth Social", "post", [("likes", "likes"), ("reposts", "reposts")]),
("github", "🐙", "GitHub", "item", [("reactions", "reactions"), ("comments", "comments")]), ("github", "🐙", "GitHub", "item", [("reactions", "reactions"), ("comments", "comments")]),
("digg", "⛏️", "Digg AI 1000", "cluster", [("postCount", "posts"), ("uniqueAuthors", "authors")]), ("digg", "⛏️", "Digg", "cluster", [("postCount", "posts"), ("uniqueAuthors", "authors")]),
] ]
@@ -1684,7 +1684,7 @@ def _comment_insight(item: schema.SourceItem | None) -> str | None:
return str(insights[0]).strip() or None return str(insights[0]).strip() or None
def _digg_posts_for(item: schema.SourceItem | None, limit: int = 2) -> list[dict]: def _digg_posts_for(item: schema.SourceItem | None, limit: int = 3) -> list[dict]:
"""Return up to `limit` parsed Digg posts attached as enrichment to a cluster. """Return up to `limit` parsed Digg posts attached as enrichment to a cluster.
Returns an empty list for non-digg sources or clusters without enrichment. Returns an empty list for non-digg sources or clusters without enrichment.
@@ -1704,17 +1704,17 @@ def _digg_posts_for(item: schema.SourceItem | None, limit: int = 2) -> list[dict
def _format_digg_quote(post: dict, body_limit: int = 200) -> str: def _format_digg_quote(post: dict, body_limit: int = 200) -> str:
"""Format a Digg-attached X post as an inline 'via Digg AI 1000' quote line.""" """Format a Digg-attached X post as an inline 'via Digg' quote line."""
handle = post.get("username") or "" handle = post.get("username") or ""
x_url = post.get("x_url") or "" x_url = post.get("x_url") or ""
body = (post.get("body") or "").replace("\n", " ").strip() body = (post.get("body") or "").replace("\n", " ").strip()
if len(body) > body_limit: if len(body) > body_limit:
body = body[: body_limit - 1].rstrip() + "" body = body[: body_limit - 1].rstrip() + ""
if x_url and handle: if x_url and handle:
return f"[@{handle}]({x_url}) via Digg AI 1000: {body}" return f"[@{handle}]({x_url}) via Digg: {body}"
if handle: if handle:
return f"@{handle} via Digg AI 1000: {body}" return f"@{handle} via Digg: {body}"
return f"via Digg AI 1000: {body}" return f"via Digg: {body}"
def _transcript_highlights(item: schema.SourceItem | None) -> list[str]: def _transcript_highlights(item: schema.SourceItem | None) -> list[str]: