RSS/Atom digest bundle for dsh: subscribe to feeds, fetch on a schedule, dedupe exact and near-duplicate stories, and get LLM-summarized daily Markdown briefings delivered into conversations and/or files (with a standalone CLI).
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-rss-digest
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:JohnXu22786/rss-digest
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
English · 简体中文
RSS/Atom aggregation, smart summarization, and daily Markdown briefings — a first-party-style dsh bundle (DeepSeek Harness, "everything is a plugin").
The plugin manages feed subscriptions with local persistence, polls feeds on a schedule, deduplicates stories (exact + near-duplicate), summarizes them with the harness's LLM (with a deterministic extractive fallback when the model is unavailable), and delivers a daily Markdown digest into live conversations and/or files. It also ships a standalone CLI that shares the same core.
- dsh integration: bundle spec (
dsh.bundle+cordis.patch.yml), model-facing tools viactx.tools, scheduling via the harness timer seam, delivery via agent follow-up messages. See Architecture. - Zero-friction core: the fetch/parse/dedupe/store/digest core has no dependencies at all — only the dsh integration layer imports dsh packages.
Features
- Subscriptions — add / remove / list / enable / disable RSS 2.0, Atom, and RSS 1.0 (RDF) feeds; persisted in a single versioned JSON document with atomic writes and corruption quarantine.
- Scheduled fetching — configurable poll interval, startup cycle, per-request timeout, size cap, retries, and an item retention cap.
- Deduplication — exact-match hashing plus token-Jaccard near-duplicate detection (CJK-aware bigrams), configurable threshold.
- LLM summaries — one batch call per digest or per-item summaries, Chinese or English, bounded output length; automatic degradation to extractive summaries on any model failure.
- Daily digest — Markdown briefing at a wall-clock time (optional IANA
timezone) delivered to live sessions (
Agent.followup) and/or written todigests/<day>.mdfiles. - Two entry points — the same
RssServicepowers both the dsh plugin (ctx.tools+ scheduler) and the standalone CLI.
Installation (dsh)
A bundle is an npm package that ships a configuration layer. Install this
package into a dsh profile; the loader picks up the dsh.bundle declaration
and applies cordis.patch.yml automatically:
# from inside a dsh profile (or with --profile <name> from anywhere)
dsh plugin --profile demo add ./path/to/dsh-rss-digest
# or from a registry: dsh plugin --profile demo add dsh-rss-digest
The profile manifest now lists dsh-rss-digest in dsh.profile.bundles, and
the bundle's patch inserts the plugin row:
- insert:
- id: rss-digest
name: 'dsh-rss-digest'
Optional: tune the plugin from your profile's own
$DSH_HOME/profiles/demo/cordis.patch.yml (a bare row by id replaces the
row's config; every omitted field keeps its default). A fully annotated
example lives in examples/profile.cordis.patch.yml:
- id: rss-digest
config:
fetch:
intervalMinutes: 60
summary:
language: zh
mode: batch
digest:
time: '08:30'
timezone: Asia/Shanghai
deliverTo: both
The plugin starts with sensible defaults even with no configuration at all;
use rss_add in a conversation or dsh-rss-digest add in a shell to register
feeds at runtime.
Peer dependencies: the plugin declares the dsh packages it links against (
@deepseek-ai/cordis,@deepseek-ai/dsh-tools,@deepseek-ai/dsh-llm,@deepseek-ai/cordis-plugin-timer) as peers. A dsh profile already provides them; if your package manager complains about unmet peers,pnpm --dir $DSH_HOME/profiles/demo addthe same four packages once (the profile linker heals the rest).
Configuration reference
All fields are optional; apply(ctx, config) always receives fully defaulted
values.
| Field | Default | Description |
|---|---|---|
dataPath |
'' |
Store file path. '' resolves to $DSH_RSS_DIGEST_DATA, else $DSH_HOME/data/rss-digest/store.json, else ./.dsh-rss-digest/store.json. |
sources[] |
[] |
Initial subscriptions merged at startup: { url, title?, enabled? }. Duplicate URLs are ignored. |
fetch.enabled |
true |
Whether the periodic poller runs. |
fetch.intervalMinutes |
60 |
Poll interval (min 5). |
fetch.onStartup |
true |
Run one cycle ~10 s after startup. |
fetch.requestTimeoutMs |
15000 |
Per-request timeout. |
fetch.sizeLimitBytes |
1048576 |
Max accepted document size. |
fetch.retries |
2 |
Retries for transient failures (network / 5xx / 429 / 408). |
fetch.maxItemsPerSource |
50 |
Parsed items kept per source per cycle (newest kept). |
fetch.storeContentChars |
4000 |
Truncation for stored summary/content text. |
fetch.maxStoredItems |
1000 |
Retention cap (oldest fetched pruned on save). |
dedupe.threshold |
0.9 |
Min token-set Jaccard similarity for near-duplicates. |
dedupe.compareContent |
false |
Let identical bodies collapse empty-title items. |
summary.enabled |
true |
false forces extractive summaries. |
summary.mode |
batch |
batch = one model call per digest; single = per item. |
summary.language |
zh |
Output language of the summary body: zh | en. |
summary.maxLength |
800 |
Hard character cap on the summary body. |
summary.maxTokens |
1024 |
Token budget hint for the model call. |
summary.provider |
'' |
dsh provider route; '' = first registered provider. |
summary.model |
'' |
Model id; '' = first listed model of the provider. |
digest.enabled |
true |
Whether the daily digest scheduler runs. |
digest.time |
'08:00' |
Wall-clock fire time (HH:MM). |
digest.timezone |
'' |
IANA timezone; '' = host local time. |
digest.maxItems |
20 |
Max items per digest. |
digest.deliverTo |
both |
agents | file | both — deliver to live sessions, to digests/<day>.md, or both. |
digest.includeItemLinks |
true |
Whether the item list embeds permalinks (http/https only). |
Tools (model-facing)
Registered through ctx.tools; the model can call them in any conversation:
| Tool | Parameters | Behavior |
|---|---|---|
rss_list |
— | Lists subscriptions (id, url, title, enabled). |
rss_add |
url (req), title? |
Subscribes to a feed; returns the new source. |
rss_remove |
id (req) |
Unsubscribes and drops that source's items. |
rss_fetch |
id? |
Polls one source (or all) now; dedupe is automatic. |
rss_digest |
language?, mode?, maxItems? |
Builds the Markdown digest over undigested items and returns it. |
Example conversation flow:
user: 帮我把 Hacker News 加进来,然后生成今天的简报
model: [rss_add: https://hnrss.org/frontpage]
[rss_fetch]
[rss_digest: language=zh]
Scheduling model
dsh's ctx.jobs registry tracks long-running task executions (bash /
subagent jobs) — it is not a cron API. The harness's scheduling seam is the
timer service (ctx.timeout / ctx.interval, injected via 'timer'), which
is what this plugin uses:
- fetch cycles:
ctx.interval(configurable), plus one startup cycle; - daily digest: a self-re-arming
ctx.timeout— after every run the nextHH:MMoccurrence is recomputed (Intl-based, timezone- and DST-aware), so the digest stays anchored to the configured wall-clock time; overlapping runs are guarded with an in-flight lock.
Delivery
The harness has no broadcast API; a plugin reaches live sessions by queueing
a user-role message on each running Agent (Agent.followup, the same
mechanism the shipped job notices use). digest.deliverTo = agents does that;
file writes digests/<day>.md next to the store; both is the default.
Headless profiles without the agent service simply get the file/log path, and
the plugin never depends on the agent service at startup.
CLI
The same core, no harness required:
dsh-rss-digest add https://hnrss.org/frontpage
dsh-rss-digest fetch
dsh-rss-digest digest --lang en --out digest.md
dsh-rss-digest list --items
dsh-rss-digest status
Run dsh-rss-digest help for the full command reference. The CLI talks to an
OpenAI-compatible /chat/completions endpoint for summaries (DeepSeek by
default: https://api.deepseek.com, key from DEEPSEEK_API_KEY, model
deepseek-chat); without credentials it degrades to extractive summaries,
exactly like the plugin does when the harness LLM fails.
Data
One JSON document (schema: 1), written atomically; corrupt files are moved
aside (.corrupt-<timestamp>) rather than deleted. Full subscription URLs
(including query strings, which signed feeds often need) are persisted as
given; log lines show the URL without its query string. Environment
overrides: DSH_RSS_DIGEST_DATA (store path), DEEPSEEK_API_KEY (CLI).
Architecture
src/
index.ts dsh bundle entry: name / inject / Config / apply
config.ts Config interface + Schemastery schema (defaults)
tools.ts rss_list / rss_add / rss_remove / rss_fetch / rss_digest
scheduler.ts timer-seam scheduling (fetch cycles + self-re-arming digest)
delivery.ts Agent.followup delivery + digests/<day>.md files
dsh-llm.ts ctx.llm adapter (streaming, provider/model resolution)
service.ts RssService — orchestrates everything (dsh-free)
parser.ts dependency-free RSS 2.0 / Atom / RSS 1.0 parser
fetcher.ts fetch with timeout, size cap, retries, charset detection
dedupe.ts normalization, exact hashing, token-Jaccard similarity
store.ts versioned JSON persistence (atomic, quarantining)
summarizer.ts LLM + extractive summarization (degrade contract)
digest.ts Markdown briefing renderer (safe link handling)
llm-client.ts OpenAI-compatible REST client (CLI)
cli.ts standalone CLI entry (bin: dsh-rss-digest)
The core (service.ts and below) has zero runtime dependencies; only the
dsh layer imports @deepseek-ai/* packages.
FAQ
The package manager warns that dsh peer dependencies are not satisfied. The dsh profile already ships
@deepseek-ai/cordis,@deepseek-ai/dsh-tools,@deepseek-ai/dsh-llm, and@deepseek-ai/cordis-plugin-timer; the profile linker wires them in. If a strict manager still complains, runpnpm --dir $DSH_HOME/profiles/<name> add @deepseek-ai/cordis @deepseek-ai/dsh-tools @deepseek-ai/dsh-llm @deepseek-ai/cordis-plugin-timer.Digests fire at the wrong wall-clock time. Timezones are only honored when
digest.timezoneis set to an IANA zone (e.g.Asia/Shanghai); an empty value uses the host's local time.I only get extractive summaries, not LLM ones. Without a reachable model route the plugin degrades deterministically to extractive summaries. Check
summary.provider/summary.modeland, for the CLI, thatDEEPSEEK_API_KEYis set and the endpoint is reachable.Where is my data? The store resolves to (in order) an explicit
config.dataPath,$DSH_RSS_DIGEST_DATA,$DSH_HOME/data/rss-digest/store.json, or<cwd>/.dsh-rss-digest/store.json.
Development
npm install
npm run build # tsc -> lib/
npm test # build + node --test (79 tests: parser, dedupe, store,
# fetcher, time, summarizer, digest, service, cli, wiring)
node lib/cli.js status
Requires Node >= 22.18.
License
MIT — see LICENSE.
Links
More in this category
Tencent/WeKnora#dsh-weknora★ 32318
Four read-only tools over a WeKnora knowledge base: list knowledge bases, hybrid passage search, reassemble one document's chunks in order, and WeKnora's own cited RAG or ReAct-agent answer with a resumable session id.
superdesigndev/treg★ 4452
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1132
Tencent CloudBase backend for DeepSeek Harness — scaffold and deploy full-stack apps from chat, render query results as table cards with paging, sorting and CSV export, preview a deployment on its domain, and call the CloudBase MCP toolset (`mcp__cloudbase__*`) with device-code login.
gitroomhq/postiz-agent#dsh-postiz★ 504
Connects DeepSeek Harness to Postiz over MCP: list connected social media channels, fetch per-platform posting rules, and schedule, draft, or publish posts to X, LinkedIn, Instagram, Facebook, Threads, TikTok, YouTube, Reddit, Bluesky, Mastodon, Discord, Slack, Telegram and more; adds a postiz workflow skill.
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 496
Local IMAP invoice download, OCR, archive, and Excel reimbursement summaries for DeepSeek Harness.
anysearch-team/anysearch-dsh★ 447
AnySearch-powered real-time web and vertical search provider for DeepSeek Harness.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.