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:
@@ -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,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,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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
|
|||||||
@@ -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`
|
||||||
|
|||||||
@@ -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)"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 {},
|
||||||
|
|||||||
@@ -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": "",
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
Reference in New Issue
Block a user