{"id":48310504,"url":"https://github.com/jongalloway/newsletter-generator","last_synced_at":"2026-04-05T00:17:45.446Z","repository":{"id":340851559,"uuid":"1157531665","full_name":"jongalloway/newsletter-generator","owner":"jongalloway","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-01T01:27:53.000Z","size":251,"stargazers_count":3,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-01T03:59:13.551Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jongalloway.png","metadata":{"files":{"readme":"README.md","changelog":"newsletter-generator.slnx","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T23:34:58.000Z","updated_at":"2026-04-01T01:27:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jongalloway/newsletter-generator","commit_stats":null,"previous_names":["jongalloway/newsletter-generator"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jongalloway/newsletter-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongalloway%2Fnewsletter-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongalloway%2Fnewsletter-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongalloway%2Fnewsletter-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongalloway%2Fnewsletter-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jongalloway","download_url":"https://codeload.github.com/jongalloway/newsletter-generator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jongalloway%2Fnewsletter-generator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31419647,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-05T00:17:43.830Z","updated_at":"2026-04-05T00:17:45.124Z","avatar_url":"https://github.com/jongalloway.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# newsletter-generator\n\nAutomated weekly newsletter generator for GitHub Copilot CLI/SDK or VS Code Insiders updates.\n\n\u003e **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.\n\n## What it does\n\nThis tool generates a curated weekly newsletter by:\n\n1. **Fetching** release notes, changelog entries, and blog posts from GitHub\n2. **Filtering** out low-value content (dependency bumps, CI changes, attributions, etc.)\n3. **Summarizing** using GitHub Copilot SDK to create concise, factual summaries\n4. **Caching** generated content to avoid regenerating unchanged sections\n\nAt startup, you'll choose:\n\n- **Newsletter type** - GitHub Copilot CLI/SDK or VS Code Insiders\n- **Copilot model** - The model used for newsletter generation prompts\n- **Cache behavior** - Use cache, clear cache, or force refresh for the run\n\nThe console UI now includes:\n\n- **Progress tasks** for feed fetch and generation stages\n- **Run Review** confirmation before generation\n- **Run Dashboard** summary with source counts, cache stats, stage durations, and a release tree showing prereleases that were rolled up or skipped\n\nThe output is a markdown newsletter with these main sections:\n\n- **Welcome** - Brief opening paragraph summarizing the week's highlights\n- **News and Announcements** - Curated changelog/blog items (for the Copilot CLI/SDK newsletter)\n- **Project Updates** - Product-specific release highlights\n\n## Information flow\n\n```mermaid\nflowchart LR\n    subgraph Sources[Public data sources]\n        CLI[Copilot CLI releases.atom]\n        SDK[Copilot SDK releases.atom]\n        CHG[GitHub Changelog feed]\n        BLOG[GitHub Blog feed]\n        VSCODE[VS Code Insiders release notes]\n        VSBLOG[VS Code blog feed]\n    end\n\n    subgraph Ingestion[Ingestion and normalization]\n        ATOM[AtomFeedService]\n        VS[VSCodeReleaseNotesService]\n        FILTER[HTML stripping and low-value filtering]\n        PRE[Prerelease consolidation]\n    end\n\n    subgraph App[Generation pipeline]\n        INPUT[Date range + newsletter type + model]\n        CACHE[CacheService\\nsection cache]\n        PROMPTS[NewsletterService prompts]\n        COPILOT[GitHub Copilot SDK\\nCopilotClient + CopilotSession]\n        SECTIONS[Welcome + News + Project Updates + Title]\n    end\n\n    subgraph Output[Run results]\n        DASH[Console progress + run dashboard]\n        FILE[output/newsletter-...md]\n        LOGS[\"log/newsletter-{Date}.log\"]\n    end\n\n    CLI --\u003e ATOM\n    SDK --\u003e ATOM\n    CHG --\u003e ATOM\n    BLOG --\u003e ATOM\n    VSBLOG --\u003e ATOM\n    VSCODE --\u003e VS\n\n    ATOM --\u003e FILTER\n    VS --\u003e FILTER\n    FILTER --\u003e PRE\n    INPUT --\u003e CACHE\n    PRE --\u003e CACHE\n    CACHE --\u003e|cache hit| SECTIONS\n    CACHE --\u003e|cache miss| PROMPTS\n    INPUT --\u003e PROMPTS\n    PROMPTS --\u003e COPILOT\n    COPILOT --\u003e SECTIONS\n    SECTIONS --\u003e CACHE\n    SECTIONS --\u003e DASH\n    PRE --\u003e DASH\n    SECTIONS --\u003e FILE\n    INPUT --\u003e DASH\n    DASH --\u003e LOGS\n```\n\n## Date logic\n\nAt startup, the tool asks how many days back to include (default: **7**).\n\n- End date is always today\n- Start date is today minus the selected number of days\n\nYou can pass `daysBack` as a command argument to skip the prompt.\n\n## Usage\n\n### Basic usage\n\n```bash\ncd src/NewsletterGenerator\ndotnet run\n```\n\nThis generates a newsletter using the automatic date logic described above.\n\n### Commands\n\n```bash\ndotnet run -- generate\ndotnet run -- list-models\ndotnet run -- doctor\ndotnet run -- clear-cache\n```\n\n`generate` is the default command, so `dotnet run -- ...options...` works without explicitly typing `generate`.\n\n### Tests\n\nThe repo includes an xUnit test project for feed parsing, cache behavior, prerelease consolidation, and VS Code release note parsing.\n\n```bash\ndotnet test\n```\n\n### Generate options\n\n**Clear cache and regenerate everything:**\n\n```bash\ndotnet run -- --clear-cache\ndotnet run -- -c\n```\n\n**Force refresh (ignore cache reads this run):**\n\n```bash\ndotnet run -- --force-refresh\ndotnet run -- -f\n```\n\n**Custom date range (N days back from today):**\n\n```bash\ndotnet run -- 7              # Last 7 days ending today\ndotnet run -- 14             # Last 14 days ending today\n```\n\n**Combine flags:**\n\n```bash\ndotnet run -- --clear-cache 7\n```\n\n**Choose newsletter type via CLI:**\n\n```bash\ndotnet run -- --newsletter copilot\ndotnet run -- --newsletter vscode\n```\n\n**Choose model via CLI:**\n\n```bash\ndotnet run -- --model gpt-5.3-codex\ndotnet run -- --model gpt-4.1\n```\n\n**Skip the pre-run confirmation prompt:**\n\n```bash\ndotnet run -- --yes\n```\n\n**Show full exception details:**\n\n```bash\ndotnet run -- --debug\n```\n\n### Non-interactive mode\n\nUse non-interactive mode for redirected output, scripts, or CI.\n\nRequired in `--non-interactive` mode:\n\n- `--newsletter`\n- `--model`\n- `daysBack`\n\nExample:\n\n```bash\ndotnet run -- --non-interactive --newsletter copilot --model gpt-5.3-codex --force-refresh 7\n```\n\n### Output\n\nGenerated newsletters are saved to:\n\n```text\noutput/newsletter-copilot-cli-sdk-YYYY-MM-DD.md\noutput/newsletter-vscode-insiders-YYYY-MM-DD.md\n```\n\nThe filename uses the end date of the coverage period and includes the newsletter slug.\n\nAfter each interactive generation run, the app prompts whether to start again from the beginning.\n\n## Caching\n\nThe tool caches:\n\n- Feed data (atom/RSS feeds)\n- Generated summaries for each section\n\nCache 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.\n\n## Configuration\n\nThe tool fetches from these sources:\n\n- **CLI Releases**: \u003chttps://github.com/github/copilot-cli/releases.atom\u003e\n- **SDK Releases**: \u003chttps://github.com/github/copilot-sdk/releases.atom\u003e\n- **Changelog**: \u003chttps://github.blog/changelog/label/copilot/feed/\u003e\n- **Blog**: \u003chttps://github.blog/feed/\u003e\n- **VS Code Insiders**: \u003chttps://aka.ms/vscode/updates/insiders\u003e (resolved to the current release notes markdown)\n- **VS Code Blog**: \u003chttps://code.visualstudio.com/feed.xml\u003e (for VS Code newsletter mode)\n- **GitHub Changelog + Blog VS Code mentions**: Copilot changelog entries and blog posts that mention VS Code (for VS Code newsletter mode)\n\nFiltering rules and summarization prompts can be modified in:\n\n- `Services/AtomFeedService.cs` - Regex filters for release notes\n- `Services/NewsletterService.cs` - AI prompts and generation logic\n\n## GitHub Copilot SDK features used\n\nThis project uses the [GitHub.Copilot.SDK](https://www.nuget.org/packages/GitHub.Copilot.SDK) NuGet package and exercises these SDK features:\n\n| Feature | Where | Why |\n| - | - | - |\n| **Streaming** | All AI sessions (`Streaming = true`) | Enables incremental response delivery; delta events are logged for diagnostics |\n| **ReasoningEffort** | All AI sessions (`ReasoningEffort = \"low\"`) | Summarization prompts don't need deep chain-of-thought; reduces latency |\n| **Session hooks** | `OnErrorOccurred`, `OnSessionStart`, `OnSessionEnd` | SDK-level error retry and session lifecycle logging without manual plumbing |\n| **ClientName** | All AI sessions (`ClientName = \"newsletter-generator\"`) | Tags requests with a stable application identity, which is useful for diagnostics and example code |\n| **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 |\n| **PingAsync** | `doctor` command + startup status | Lightweight connectivity check without creating a full session |\n| **ListModelsAsync** | Model selection, `list-models` command | Enumerate available models for interactive selection |\n| **System messages** | `SystemMessageMode.Replace` on all sessions | Full control over system prompt for editorial tone and output formatting |\n| **Event-driven responses** | `AssistantMessageEvent`, `AssistantMessageDeltaEvent`, `SessionIdleEvent`, `SessionErrorEvent` | Collect final responses and streaming deltas via pattern matching |\n| **GetAuthStatusAsync** | Startup status table | Display authentication state before generation |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjongalloway%2Fnewsletter-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjongalloway%2Fnewsletter-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjongalloway%2Fnewsletter-generator/lists"}