Automated newsletter generator for GitHub Copilot CLI/SDK, VS Code Insiders, or DevTech MVP 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.
This tool generates a curated weekly newsletter by:
- Fetching release notes, changelog entries, and blog posts from GitHub
- Filtering out low-value content (dependency bumps, CI changes, attributions, etc.)
- Summarizing using GitHub Copilot SDK to create concise, factual summaries
- Caching generated content to avoid regenerating unchanged sections
At startup, you'll choose:
- Newsletter type - GitHub Copilot CLI/SDK, VS Code Insiders, or DevTech MVP
- 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. Sections vary by newsletter type:
- Copilot CLI/SDK — Welcome, News & Announcements, Project Updates
- VS Code — Welcome, This Week in VS Code Stable, VS Code Insiders Highlights, News and Announcements (if applicable)
- DevTech MVP — Welcome, Copilot CLI & SDK, VS Code, Visual Studio, Major Releases (auto-detected), Developer Blogs, Developer Videos
flowchart LR
subgraph Sources[Public data sources]
FEEDS[Atom/RSS feeds\nGitHub · Microsoft · YouTube]
VSCODE[VS Code Insiders\nrelease notes markdown]
end
subgraph Ingestion[Ingestion and normalization]
ATOM[AtomFeedService]
VS[VSCodeReleaseNotesService]
FILTER[HTML stripping and\nlow-value filtering]
PRE[Prerelease consolidation]
end
subgraph App[Generation pipeline]
INPUT[Date range +\nnewsletter type + model]
CACHE[CacheService\nsection cache]
PROMPTS[NewsletterService\nprompts]
COPILOT[GitHub Copilot SDK\nCopilotClient + CopilotSession]
SECTIONS[Section content + title]
end
subgraph Output[Run results]
DASH[Console progress +\nrun dashboard]
FILE[output/newsletter-...md]
LOGS["log/newsletter-{Date}.log"]
end
FEEDS --> 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
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.
cd src/NewsletterGenerator
dotnet runThis generates a newsletter using the automatic date logic described above.
dotnet run -- generate
dotnet run -- list-models
dotnet run -- doctor
dotnet run -- clear-cachegenerate is the default command, so dotnet run -- ...options... works without explicitly typing generate.
The repo includes an xUnit test project for feed parsing, cache behavior, prerelease consolidation, and VS Code release note parsing.
dotnet testClear cache and regenerate everything:
dotnet run -- --clear-cache
dotnet run -- -cForce refresh (ignore cache reads this run):
dotnet run -- --force-refresh
dotnet run -- -fCustom date range (N days back from today):
dotnet run -- 7 # Last 7 days ending today
dotnet run -- 14 # Last 14 days ending todayCombine flags:
dotnet run -- --clear-cache 7Choose newsletter type via CLI:
dotnet run -- --newsletter copilot
dotnet run -- --newsletter vscode
dotnet run -- --newsletter devtechChoose model via CLI:
dotnet run -- --model gpt-5.3-codex
dotnet run -- --model gpt-4.1Skip the pre-run confirmation prompt:
dotnet run -- --yesShow full exception details:
dotnet run -- --debugEnable infinite sessions for interactive revision loops:
dotnet run -- --infinite-sessionsUse non-interactive mode for redirected output, scripts, or CI.
Required in --non-interactive mode:
--newsletter--modeldaysBack
Example:
dotnet run -- --non-interactive --newsletter copilot --model gpt-5.3-codex --force-refresh 7Generated newsletters are saved to:
output/newsletter-copilot-cli-sdk-YYYY-MM-DD.md
output/newsletter-vscode-YYYY-MM-DD.md
output/newsletter-devtech-mvp-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.
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.
The tool fetches from these sources:
Copilot CLI/SDK & shared sources:
- CLI Releases: https://github.com/github/copilot-cli/releases.atom
- SDK Releases: https://github.com/github/copilot-sdk/releases.atom
- Changelog: https://github.blog/changelog/label/copilot/feed/
- GitHub Blog: https://github.blog/feed/
VS Code Insiders:
- VS Code Insiders release notes: https://aka.ms/vscode/updates/insiders (resolved to the current release notes markdown)
- VS Code Blog: https://code.visualstudio.com/feed.xml
- GitHub Changelog + Blog VS Code mentions: Copilot changelog entries and blog posts that mention VS Code
DevTech MVP (includes the Copilot and VS Code sources above, plus):
- .NET Blog: https://devblogs.microsoft.com/dotnet/feed/
- Developer Blog: https://devblogs.microsoft.com/feed/
- Visual Studio Blog: https://devblogs.microsoft.com/visualstudio/feed/
- Azure Blog: https://devblogs.microsoft.com/all-things-azure/feed/
- Aspire Blog: https://devblogs.microsoft.com/aspire/feed/
- TypeScript Blog: https://devblogs.microsoft.com/typescript/feed/
- Agent Framework Blog: https://devblogs.microsoft.com/agent-framework/feed/
- YouTube channels: .NET, Visual Studio, VS Code, GitHub, Microsoft Dev
- Visual Studio release notes: https://learn.microsoft.com/visualstudio/releases/2026/release-notes
Filtering rules and summarization prompts can be modified in:
Services/AtomFeedService.cs- Regex filters for release notesServices/NewsletterService.cs- AI prompts and generation logic
This project uses the 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 profiles | ResolveReasoningEffort(operationProfile), ReasoningEffort = reasoningEffort |
Uses low/medium/high profiles by operation so short summaries stay cheaper and larger sections get more headroom |
| SendAndWaitAsync | session.SendAndWaitAsync(new MessageOptions { Prompt = prompt }), AssistantMessageEvent, AssistantMessageDeltaEvent, SessionIdleEvent, SessionErrorEvent |
Submits prompts and waits for the final assistant message while still streaming deltas and idle events |
| Session hooks | OnErrorOccurred, OnSessionStart, OnSessionEnd |
SDK-level error retry and session lifecycle logging without manual plumbing |
| Deterministic session resume | BuildSessionId(runContext, workflowStep, model), ResumeSessionAsync(...) |
Reuses the last matching session for repeat runs when a workflow step and run context are available |
| 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 = PermissionHandler.ApproveAll) |
Uses a least-privilege session policy because newsletter generation does not request tool access |
| Model fallback | SetModelAsync(nextModel) |
Switches models in-session when the active model fails, instead of restarting from scratch |
| Usage metrics | session.Rpc.Usage.GetMetricsAsync(), session.SessionId |
Captures prompt/output token usage for each operation and ties it to the session ID |
| 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 |
| Infinite sessions (interactive revisions) | InfiniteSessions = new InfiniteSessionConfig { Enabled = true }, --infinite-sessions |
Keeps multi-pass revision loops in a single long-running session |
| 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 |