Skip to content
Content
Skill

/openpaper

Generate a personalized newspaper-style digest from any news sources. Use this skill whenever the user wants to read news, set up news sources, get a daily digest, curate articles, generate a newspaper, create a morning briefing, manage their reading preferences, or says things

From plugin
openpaper
153 skills
Install
$ npx -y skills add falense/openpaper --skill openpaper --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/openpaper

Context preview

The summary Claude sees to decide when to auto-load this skill.

Generate a personalized newspaper-style digest from any news sources. Use this skill whenever the user wants to read news, set up news sources, get a daily digest, curate articles, generate a newspaper, create a morning briefing, manage their reading preferences, or says things

SKILL.md

openpaper.SKILL.md
name: openpaper
description: >
  Generate a personalized newspaper-style digest from any news sources. Use this skill whenever the user wants to read news, set up news sources, get a daily digest, curate articles, generate a newspaper, create a morning briefing, manage their reading preferences, or says things like "make my paper", "what's new today", "add a news source", "show me the news", or "update my preferences". Also use when the user mentions OpenPaper, newspaper layout, or news curation. This skill handles the full pipeline: adding sources (writing fetchers), curating content, and rendering beautiful HTML newspaper editions.

OpenPaper

A personalized newspaper that knows only you will ever read it.

Three-stage pipeline: **Ingest** (fetch news) → **Curate** (select and rank) → **Present** (render a newspaper). You are the editor-in-chief.

Quick Reference

| Command | What it does | |---------|-------------| | "Make my paper" | Full pipeline: fetch → curate → render | | "Add a source" | Analyze a URL and write a fetcher | | "Show my preferences" | Display and edit preference profile |

Running Scripts

All scripts use `uv run --project`. Use `${CLAUDE_PLUGIN_ROOT}` if set (plugin mode), otherwise `.` (standalone mode):

uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/<script>.py <args>

Data Directory

All state lives in `.openpaper/` relative to the working directory:

.openpaper/
├── sources/           # one .py per source (+ _base.py auto-deployed)
├── preferences.md     # user interests and feedback
├── incoming/          # raw fetched articles (JSON)
├── saved/             # archived articles from past editions
├── editions/          # rendered HTML newspapers
├── cache/             # HTTP cache for fetchers
└── seen.txt           # dedup log

First Run — Setup Wizard

When `.openpaper/` doesn't exist:

1. Create the data directory

mkdir -p .openpaper/sources .openpaper/incoming .openpaper/saved .openpaper/editions .openpaper/cache

2. Add news sources

Say: "What do you want to read? Give me URLs, RSS feeds, or just topics." Then wait for the user's reply.

Deploy the shared base module:

cp ${CLAUDE_PLUGIN_ROOT:-.}/skills/openpaper/scripts/fetcher_base.py .openpaper/sources/_base.py

For each source: 1. **Analyze** — visit the URL, determine type (RSS, HTML, JS-rendered, API) 2. **Write a fetcher** — create `.openpaper/sources/<name>.py`. Read `references/fetcher-guide.md` for the interface spec and `_base.py` templates. 3. **Test** — `uv run .openpaper/sources/<name>.py --cache-dir /tmp/openpaper-test-<name>` > **Cache trap:** Always test with `/tmp/`, NOT `.openpaper/cache/`. Using the real cache pollutes `seen.txt` dedup. 4. **Show** the user what it found

3. Set preferences

Create `.openpaper/preferences.md` by chatting with the user about:

  • Topics of interest and how much
  • Articles per edition (default: 14, up to 18)
  • Location (for weather)
  • Markets data wanted?

Keep under 2000 characters. Example:

# My Reading Preferences

## Interests
- Technology and AI (very interested)
- Software engineering (interested)
- Open source (some interest)

## Sources
Hacker News is my primary source.

## Reading Profile
~14 articles. Include weather for Oslo.

## Feedback

4. Generate first edition

Run the full pipeline and open in browser.

Making a Paper

Stage 1: Ingest

uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/fetch_all.py --data-dir .openpaper

Discovers all fetchers in `.openpaper/sources/`, runs in parallel, deduplicates against `seen.txt`, fetches content, writes new articles to `.openpaper/incoming/`.

Stage 2: Curate

> **Gate:** Only begin after Stage 1 completes and weather/markets data is collected. Curation needs the full candidate pool.

Read `references/curation-guide.md` for the full process. Summary:

1. **Inspect the pool:**

  • `uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/pool.py --data-dir .openpaper stats`
  • `uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/pool.py --data-dir .openpaper list --sort points`
  • `uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/pool.py --data-dir .openpaper show <slug>`

2. **Read** `.openpaper/preferences.md` 3. **Score and rank** articles against user interests 4. **Select** articles (typically 10-18), enforce topic/source diversity 5. **Assign editorial weight** — lead (1), major/lg (2-3), mid/md (3-5), briefs (4-8) 6. **Summarize in parallel** — spawn one subagent per article via `parallel()`:

parallel([
  () => agent("Summarize this lead article: <JSON>. Write 3-4 paragraphs, deck, photo_caption..."),
  () => agent("Summarize this lg article: <JSON>. Write 2 paragraphs..."),
  () => agent("Write a brief for: <JSON>. Return bold + one sentence..."),
  ...
])

7. **Assemble edition YAML** — see `references/edition-schema.md` for the schema

Write the YAML to `.openpaper/editions/draft.yaml`.

Stage 2.5: Archive

1. `mkdir -p .openpaper/saved/<date>-<edition_name>` 2. Move selected article JSONs to the archive 3. Delete remaining from `.openpaper/incoming/`

Stage 3: Present

uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/render.py --data-dir .openpaper --edition .openpaper/editions/draft.yaml

Then preview:

uv run --project ${CLAUDE_PLUGIN_ROOT:-.} skills/openpaper/scripts/serve.py --data-dir .openpaper --latest

Open in browser. Say: "Here's your paper — let me know what you think."

Adding a Source

1. Visit the URL (screenshot or fetch) 2. Determine type: RSS, HTML, JS-rendered, API 3. Read `references/fetcher-guide.md` for the contract and `_base.py` templates 4. Write `.openpaper/sources/<name>.py` 5. Test with `/tmp/` cache dir (not the real one) 6. Show results to the user

**Key rules:**

  • RSS/API first, Playwright for
Read more
Ships withopenpaper

A personalized newspaper that knows only you will ever read it. OpenPaper is a Claude Code plugin that turns any collection of news sources into a single-reader daily newspaper.

Get the whole plugin
Stats
15
Stars
1
Forks
Maintained
Maintenance
Python
Language
MIT
License
3mo ago
Last commit
3mo ago
Created

Repo: falense/openpaper

Other skills on openpaper.