An open API service indexing awesome lists of open source software.

https://github.com/jongalloway/newsletter-generator


https://github.com/jongalloway/newsletter-generator

Last synced: 2 months ago
JSON representation

Awesome Lists containing this project

README

          

# newsletter-generator

Automated weekly newsletter generator for GitHub Copilot CLI/SDK or VS Code Insiders updates.

> **Note:** This tool was built for generating internal team newsletters, but all source data comes from public release feeds and blog posts. You're welcome to fork and adapt it for your own newsletter needs.

## What it does

This tool generates a curated weekly newsletter by:

1. **Fetching** release notes, changelog entries, and blog posts from GitHub
2. **Filtering** out low-value content (dependency bumps, CI changes, attributions, etc.)
3. **Summarizing** using GitHub Copilot SDK to create concise, factual summaries
4. **Caching** generated content to avoid regenerating unchanged sections

At startup, you'll choose:

- **Newsletter type** - GitHub Copilot CLI/SDK or VS Code Insiders
- **Copilot model** - The model used for newsletter generation prompts
- **Cache behavior** - Use cache, clear cache, or force refresh for the run

The console UI now includes:

- **Progress tasks** for feed fetch and generation stages
- **Run Review** confirmation before generation
- **Run Dashboard** summary with source counts, cache stats, stage durations, and a release tree showing prereleases that were rolled up or skipped

The output is a markdown newsletter with these main sections:

- **Welcome** - Brief opening paragraph summarizing the week's highlights
- **News and Announcements** - Curated changelog/blog items (for the Copilot CLI/SDK newsletter)
- **Project Updates** - Product-specific release highlights

## Information flow

```mermaid
flowchart LR
subgraph Sources[Public data sources]
CLI[Copilot CLI releases.atom]
SDK[Copilot SDK releases.atom]
CHG[GitHub Changelog feed]
BLOG[GitHub Blog feed]
VSCODE[VS Code Insiders release notes]
VSBLOG[VS Code blog feed]
end

subgraph Ingestion[Ingestion and normalization]
ATOM[AtomFeedService]
VS[VSCodeReleaseNotesService]
FILTER[HTML stripping and low-value filtering]
PRE[Prerelease consolidation]
end

subgraph App[Generation pipeline]
INPUT[Date range + newsletter type + model]
CACHE[CacheService\nsection cache]
PROMPTS[NewsletterService prompts]
COPILOT[GitHub Copilot SDK\nCopilotClient + CopilotSession]
SECTIONS[Welcome + News + Project Updates + Title]
end

subgraph Output[Run results]
DASH[Console progress + run dashboard]
FILE[output/newsletter-...md]
LOGS["log/newsletter-{Date}.log"]
end

CLI --> ATOM
SDK --> ATOM
CHG --> ATOM
BLOG --> ATOM
VSBLOG --> ATOM
VSCODE --> VS

ATOM --> FILTER
VS --> FILTER
FILTER --> PRE
INPUT --> CACHE
PRE --> CACHE
CACHE -->|cache hit| SECTIONS
CACHE -->|cache miss| PROMPTS
INPUT --> PROMPTS
PROMPTS --> COPILOT
COPILOT --> SECTIONS
SECTIONS --> CACHE
SECTIONS --> DASH
PRE --> DASH
SECTIONS --> FILE
INPUT --> DASH
DASH --> LOGS
```

## Date logic

At startup, the tool asks how many days back to include (default: **7**).

- End date is always today
- Start date is today minus the selected number of days

You can pass `daysBack` as a command argument to skip the prompt.

## Usage

### Basic usage

```bash
cd src/NewsletterGenerator
dotnet run
```

This generates a newsletter using the automatic date logic described above.

### Commands

```bash
dotnet run -- generate
dotnet run -- list-models
dotnet run -- doctor
dotnet run -- clear-cache
```

`generate` is the default command, so `dotnet run -- ...options...` works without explicitly typing `generate`.

### Tests

The repo includes an xUnit test project for feed parsing, cache behavior, prerelease consolidation, and VS Code release note parsing.

```bash
dotnet test
```

### Generate options

**Clear cache and regenerate everything:**

```bash
dotnet run -- --clear-cache
dotnet run -- -c
```

**Force refresh (ignore cache reads this run):**

```bash
dotnet run -- --force-refresh
dotnet run -- -f
```

**Custom date range (N days back from today):**

```bash
dotnet run -- 7 # Last 7 days ending today
dotnet run -- 14 # Last 14 days ending today
```

**Combine flags:**

```bash
dotnet run -- --clear-cache 7
```

**Choose newsletter type via CLI:**

```bash
dotnet run -- --newsletter copilot
dotnet run -- --newsletter vscode
```

**Choose model via CLI:**

```bash
dotnet run -- --model gpt-5.3-codex
dotnet run -- --model gpt-4.1
```

**Skip the pre-run confirmation prompt:**

```bash
dotnet run -- --yes
```

**Show full exception details:**

```bash
dotnet run -- --debug
```

### Non-interactive mode

Use non-interactive mode for redirected output, scripts, or CI.

Required in `--non-interactive` mode:

- `--newsletter`
- `--model`
- `daysBack`

Example:

```bash
dotnet run -- --non-interactive --newsletter copilot --model gpt-5.3-codex --force-refresh 7
```

### Output

Generated newsletters are saved to:

```text
output/newsletter-copilot-cli-sdk-YYYY-MM-DD.md
output/newsletter-vscode-insiders-YYYY-MM-DD.md
```

The filename uses the end date of the coverage period and includes the newsletter slug.

After each interactive generation run, the app prompts whether to start again from the beginning.

## Caching

The tool caches:

- Feed data (atom/RSS feeds)
- Generated summaries for each section

Cache files are stored in `src/NewsletterGenerator/.cache/` and use SHA256 hashing to detect changes in source data. The app resolves this location relative to the repository root, so it uses the same cache directory whether you run it from `src/NewsletterGenerator` or from the repo root. When source data changes, that section is regenerated. Use the `clear-cache` command or `--clear-cache` with `generate` to force regeneration of all content.

## Configuration

The tool fetches from these sources:

- **CLI Releases**:
- **SDK Releases**:
- **Changelog**:
- **Blog**:
- **VS Code Insiders**: (resolved to the current release notes markdown)
- **VS Code Blog**: (for VS Code newsletter mode)
- **GitHub Changelog + Blog VS Code mentions**: Copilot changelog entries and blog posts that mention VS Code (for VS Code newsletter mode)

Filtering rules and summarization prompts can be modified in:

- `Services/AtomFeedService.cs` - Regex filters for release notes
- `Services/NewsletterService.cs` - AI prompts and generation logic

## GitHub Copilot SDK features used

This project uses the [GitHub.Copilot.SDK](https://www.nuget.org/packages/GitHub.Copilot.SDK) NuGet package and exercises these SDK features:

| Feature | Where | Why |
| - | - | - |
| **Streaming** | All AI sessions (`Streaming = true`) | Enables incremental response delivery; delta events are logged for diagnostics |
| **ReasoningEffort** | All AI sessions (`ReasoningEffort = "low"`) | Summarization prompts don't need deep chain-of-thought; reduces latency |
| **Session hooks** | `OnErrorOccurred`, `OnSessionStart`, `OnSessionEnd` | SDK-level error retry and session lifecycle logging without manual plumbing |
| **ClientName** | All AI sessions (`ClientName = "newsletter-generator"`) | Tags requests with a stable application identity, which is useful for diagnostics and example code |
| **Permission policy / tool restrictions** | All AI sessions (`AvailableTools = []`, `OnPermissionRequest = DenyUnexpectedPermissionRequest`) | Uses a least-privilege, fail-closed session policy because newsletter generation only needs text summarization, not tool access |
| **PingAsync** | `doctor` command + startup status | Lightweight connectivity check without creating a full session |
| **ListModelsAsync** | Model selection, `list-models` command | Enumerate available models for interactive selection |
| **System messages** | `SystemMessageMode.Replace` on all sessions | Full control over system prompt for editorial tone and output formatting |
| **Event-driven responses** | `AssistantMessageEvent`, `AssistantMessageDeltaEvent`, `SessionIdleEvent`, `SessionErrorEvent` | Collect final responses and streaming deltas via pattern matching |
| **GetAuthStatusAsync** | Startup status table | Display authentication state before generation |