# news-digest

Classify new stories from tech feeds by topic and write a Markdown digest of the chosen topics.

Category: News and research. Allow `hnrss.org, lobste.rs`.

Updated 2026-10-17. HTML: https://asipipes.com/pipes-library/news-digest

## CLI

```
pipes run examples/news-digest.pipe.yaml
```

## MCP call

```json
{
  "name": "pipe__news-digest",
  "arguments": {}
}
```

## Pipe file

`examples/news-digest.pipe.yaml`

```yaml
# Example (c): news RSS -> llm_classify (cached, non-deterministic) -> filter -> digest file
pipes: 0
name: news-digest
version: 0.1.0
description: Classify new stories from tech feeds by topic and write a Markdown digest of the chosen topics.
permissions:
  network: [hnrss.org, lobste.rs]
  llm: [fast]                                  # alias resolved via pipes.toml; exact model pinned in pipes.lock
inputs:
  type: object
  properties:
    feeds:
      type: array
      items: { type: string, format: uri }
      default: ["https://hnrss.org/frontpage", "https://lobste.rs/rss"]
    topics:
      type: array
      items: { enum: [ai, security, rust, databases, other] }
      default: [ai, security, rust]
    max_items: { type: integer, minimum: 1, maximum: 200, default: 8 }
outputs:
  type: object
  properties:
    markdown: { type: string }
    count:    { type: integer }
output:
  markdown: "${steps.digest.text}"
  count:    "${steps.digest.count}"

steps:
  - id: fetch
    block: fetch_http
    for_each: "${inputs.feeds}"
    with: { url: "${item}" }
    retry: { max: 2, backoff_ms: 500 }
    on_error: skip                             # one dead feed shouldn't kill the digest

  - id: stories
    block: parse_rss
    in: "${steps.fetch}"

  - id: unique
    block: dedupe
    in: "${steps.stories}"
    with: { key: [link] }

  - id: new
    block: diff_since_last                     # classify only unseen stories (fewer LLM calls)
    in: "${steps.unique}"
    with: { key: [link], emit: [added], first_run: all }

  - id: capped
    block: limit
    in: "${steps.new}"
    with: { n: "${inputs.max_items}" }

  - id: classified
    block: llm_classify                        # NON-DETERMINISTIC: cached by H(model, prompt, fields, params)
    in: "${steps.capped}"
    with:
      model: fast
      fields: [title, summary]
      labels: [ai, security, rust, databases, other]
      instructions: "Pick the single best topic for this tech news story."
      into: topic
      fallback: other                          # used if the model answers outside the label set
    retry: { max: 2, backoff_ms: 2000 }

  - id: wanted
    block: filter
    in: "${steps.classified}"
    with:
      where: { field: topic, op: in, value: "${inputs.topics}" }

  - id: ordered
    block: sort
    in: "${steps.wanted}"
    with: { by: [{ field: topic }, { field: published, desc: true }] }

  - id: digest
    block: format
    in: "${steps.ordered}"
    with:
      header: "# Tech digest\n\n"
      item_template: "- **[${item.topic}]** [${item.title}](${item.link})"
      separator: "\n"
      footer: "\n"

  - id: write
    block: emit_file
    in: "${steps.digest.text}"
    with: { path: news-digest.md, format: text, mode: write }
```
