{"id":47725938,"url":"https://github.com/saurabhdave/ios-dev-ai-writer","last_synced_at":"2026-04-02T20:27:13.226Z","repository":{"id":344063642,"uuid":"1174745964","full_name":"saurabhdave/ios-dev-ai-writer","owner":"saurabhdave","description":"AI agent that auto-generates weekly Medium-style iOS articles \u0026 linkedin post from real trend signals.","archived":false,"fork":false,"pushed_at":"2026-03-27T05:21:28.000Z","size":716,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-27T17:06:48.079Z","etag":null,"topics":["agent","ai","ai-agents","apple","content-generation","content-generation-ai","github-actions","ios","openai","python","swift"],"latest_commit_sha":null,"homepage":"https://saurabhdave.github.io/ios-ai-articles/","language":"Python","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/saurabhdave.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-03-06T19:47:04.000Z","updated_at":"2026-03-27T05:21:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/saurabhdave/ios-dev-ai-writer","commit_stats":null,"previous_names":["saurabhdave/ios-dev-ai-writer"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/saurabhdave/ios-dev-ai-writer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saurabhdave%2Fios-dev-ai-writer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saurabhdave%2Fios-dev-ai-writer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saurabhdave%2Fios-dev-ai-writer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saurabhdave%2Fios-dev-ai-writer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saurabhdave","download_url":"https://codeload.github.com/saurabhdave/ios-dev-ai-writer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saurabhdave%2Fios-dev-ai-writer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31315939,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["agent","ai","ai-agents","apple","content-generation","content-generation-ai","github-actions","ios","openai","python","swift"],"created_at":"2026-04-02T20:27:12.576Z","updated_at":"2026-04-02T20:27:13.221Z","avatar_url":"https://github.com/saurabhdave.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ios-dev-ai-writer\n\n\u003e Automated Apple-platform engineering articles, Swift code examples, LinkedIn posts, and a developer newsletter — generated by a multi-stage LLM pipeline and published on a schedule.\n\n[![Version](https://img.shields.io/badge/version-1.6.12-blue?style=flat-square)](CHANGELOG.md)\n[![Python](https://img.shields.io/badge/python-3.11-3776AB?style=flat-square\u0026logo=python\u0026logoColor=white)](https://www.python.org)\n[![OpenAI](https://img.shields.io/badge/OpenAI-API-412991?style=flat-square\u0026logo=openai\u0026logoColor=white)](https://platform.openai.com)\n[![License](https://img.shields.io/badge/license-MIT-lightgrey?style=flat-square)](LICENSE)\n[![CI](https://img.shields.io/github/actions/workflow/status/saurabhdave/ios-dev-ai-writer/weekly.yml?style=flat-square\u0026label=2x%2Fweek%20pipeline)](https://github.com/saurabhdave/ios-dev-ai-writer/actions)\n\n---\n\n## What It Does\n\nTwice a week, a GitHub Actions job scans iOS/Swift trend sources, picks a topic, and runs it through a multi-stage LLM pipeline that produces a full Medium-style article — complete with a Swift code example, a LinkedIn post, and a developer newsletter issue. Everything is committed and published automatically.\n\n**Published output lives at [saurabhdave/ios-ai-articles](https://github.com/saurabhdave/ios-ai-articles).**\n\n---\n\n## Pipeline\n\n```\nTrend Scanner (8 sources)\n    │\n    ▼\nTopic Agent ──► dedup check (embedding similarity + theme cluster guard)\n    │\n    ▼\nOutline Agent\n    │\n    ▼\nArticle Agent (~900–1,200 words, 5-section Medium structure)\n    │\n    ▼\nEditor Pass ──► Voice Pass ──► Factual Grounding ──► Layout Repair loop\n    │\n    ├──► Code Agent (Swift 6.2.4, snippet validation + repair loop)\n    ├──► LinkedIn Agent (senior-voice post, claim guardrails)\n    ├──► Newsletter Agent (6-section SwiftTribune-style, Markdown + HTML)\n    └──► Review Agent (quality scores → repair trigger if below threshold)\n    │\n    ▼\nAuto-commit to ios-ai-articles\n```\n\n---\n\n## Architecture\n\n```mermaid\nflowchart TD\n    A[GitHub Actions · Mon \u0026 Thu 10:00 UTC] --\u003e B[main.py]\n    B --\u003e C[weekly_pipeline.py]\n\n    C --\u003e S[trend_scanner.py]\n    S --\u003e S1[HackerNews]\n    S --\u003e S2[Reddit r/iOSProgramming]\n    S --\u003e S3[Apple Docs \u0026 News]\n    S --\u003e S4[WWDC Feed]\n    S --\u003e S5[Viral iOS / Social]\n    S --\u003e S6[Custom JSON sources]\n    S --\u003e S7[Web Search — top iOS topics]\n\n    C --\u003e D[topic_agent]\n    C --\u003e E[outline_agent]\n    C --\u003e F[article_agent]\n    C --\u003e G[editor_agent · polish]\n    G --\u003e VP[editor_agent · voice pass]\n    VP --\u003e FG[editor_agent · factual grounding]\n    FG --\u003e LR[editor_agent · layout repair]\n    C --\u003e H[code_agent]\n    C --\u003e L[linkedin_agent]\n    C --\u003e NL[newsletter_agent]\n    C --\u003e R[review_agent]\n\n    D \u0026 E \u0026 F \u0026 G \u0026 VP \u0026 FG \u0026 LR \u0026 H \u0026 L \u0026 NL \u0026 R --\u003e API[OpenAI API]\n\n    LR --\u003e J[outputs/articles/]\n    C --\u003e K[outputs/trends/]\n    C --\u003e M[outputs/linkedin/]\n    C --\u003e N[outputs/codegen/]\n    NL --\u003e NW[outputs/newsletter/ · .md + .html]\n    R --\u003e O[outputs/quality_history.json]\n\n    J \u0026 M \u0026 N \u0026 NW --\u003e P[saurabhdave/ios-ai-articles]\n```\n\n---\n\n## Features\n\n### Content Quality\n| Feature | Detail |\n|---|---|\n| **Editor pass** | Polish for clarity, tone, and Medium readability |\n| **Voice pass** | Strips AI writing patterns — \"Choose X/Z\" constructs, hedge phrases, passive recommendations, vague claims |\n| **Factual grounding** | Conservative rewrite pass to reduce hallucinated claims |\n| **Layout repair loop** | Iteratively scores article against a 14-point Medium rubric and repairs until score ≥ threshold |\n| **Deterministic repair** | Post-process fixes malformed backticks and strips `Operational note:` template artifacts before publication |\n| **Self-review** | LLM scores each article on overall quality, technical depth, and actionability |\n| **Review-triggered repair** | Re-runs editor pass when review score falls below threshold |\n\n### Topic Discovery\n| Feature | Detail |\n|---|---|\n| **8 trend sources** | HackerNews, Reddit, Apple Docs, WWDC, viral/social, platforms, custom JSON, web search |\n| **Embedding dedup** | `text-embedding-3-small` cosine similarity catches near-duplicate topics with low lexical overlap |\n| **Theme cluster guard** | Hard limit on same-cluster articles (Swift concurrency, UIKit migration, SwiftUI profiling) per rolling window |\n| **Apple-platform only** | Topics filtered to iOS/Swift/SwiftUI/Xcode — AI-first subjects explicitly excluded |\n\n### Code Generation\n| Feature | Detail |\n|---|---|\n| **Swift 6 first** | `@Observable` preferred; targets Swift 6.2.4, compiler mode 6 by default |\n| **Validation modes** | `snippet` (syntax + placeholder check), `compile` (strict typecheck), `none` |\n| **Repair loop** | Up to N attempts to fix failing snippets before falling back to `omit` or `error` |\n| **Codegen metadata** | Per-run JSON with path (`direct`/`repaired`/`omitted`) and repair attempt count |\n\n### Output \u0026 Automation\n| Feature | Detail |\n|---|---|\n| **LinkedIn post** | Senior-voice post with code snippet (auto/always/never) and claim guardrails |\n| **Newsletter** | 6-section SwiftTribune issue — Markdown + email-safe inline-styled HTML, auto-incrementing issue number |\n| **Quality history** | `outputs/quality_history.json` accumulates layout scores, review scores, and repair counts across all runs |\n| **Structured logging** | JSON log lines with agent name, token usage, step timing, and status — readable in GitHub Actions |\n| **URL safety** | Unverified URLs stripped from article body before publication |\n| **Reference quality check** | Homepage-level reference URLs (e.g. `developer.apple.com/documentation/swiftui`) flagged with a `WARNING` log event; prompt enforces specific page links, SE proposal citations, and WWDC session citations |\n| **Author context injection** | Articles grounded in your real production experience via `scanners/author_context.json` — failure modes, device-specific observations, and migration gotchas injected per topic family |\n\n---\n\n## Project Structure\n\n```\nios-dev-ai-writer/\n├── agents/\n│   ├── topic_agent.py          # topic selection + dedup\n│   ├── outline_agent.py\n│   ├── article_agent.py\n│   ├── editor_agent.py         # polish → voice → factual grounding → layout repair\n│   ├── code_agent.py           # Swift codegen + validation loop\n│   ├── linkedin_agent.py\n│   ├── newsletter_agent.py\n│   └── review_agent.py\n├── scanners/\n│   ├── trend_scanner.py        # 8-source trend aggregation → TrendSignal\n│   ├── custom_trends.json\n│   └── author_context.json     # your real-world experience bullets per topic family\n├── workflows/\n│   └── weekly_pipeline.py      # main orchestrator\n├── prompts/                    # all LLM prompt templates\n├── utils/\n│   ├── article_repair.py       # deterministic post-processing for article cleanup\n│   ├── observability.py        # structured JSON logging\n│   └── openai_logging.py       # OpenAI client + token tracking\n├── tests/\n│   └── test_openai_config.py   # focused compatibility tests for OpenAI config helpers\n├── outputs/                    # gitignored — published to ios-ai-articles\n├── .github/workflows/\n│   ├── weekly.yml              # scheduled pipeline\n│   └── release.yml             # GitHub Release on v* tag\n├── config.py                   # all env-var configuration\n└── main.py\n```\n\n---\n\n## Setup\n\n**Requirements:** Python 3.11, an OpenAI API key.\n\n```bash\n# 1. Clone and install\ngit clone https://github.com/saurabhdave/ios-dev-ai-writer.git\ncd ios-dev-ai-writer\npip install -e .\n\n# 2. Configure\ncp .env.example .env\n# Set OPENAI_API_KEY at minimum\n\n# 3. Run\npython main.py\n```\n\n---\n\n## Personalising the Output\n\nThe pipeline can ground articles in your real iOS engineering experience via `scanners/author_context.json`. When the generated topic matches a known family (e.g. concurrency, SwiftUI rendering), those bullets are injected into the article prompt so the output references your specific failure modes and production observations — not a generic senior-engineer persona.\n\n### How to fill it in\n\nOpen `scanners/author_context.json` and add 2–3 first-person bullet points per topic family you have experience with:\n\n```json\n{\n  \"concurrency\": [\n    \"Migrated a URLSession-heavy networking layer to async/await — cancellation semantics were the main gotcha\",\n    \"Hit data races in a shared cache when moving to Swift 6 strict concurrency\"\n  ],\n  \"swiftui_rendering\": [\n    \"Profiled a feed view re-rendering 3x per scroll tick — root cause was @Observable held by multiple ancestor views\"\n  ],\n  \"architecture\": [],\n  \"testing\": [],\n  \"performance\": [],\n  \"migration\": []\n}\n```\n\n**Guidelines:**\n- Write in first person and be specific — device names, API names, failure conditions\n- Include at least one failure mode per family (\"This broke when…\", \"We hit this under…\")\n- Leave a family as `[]` if you have no direct experience — the prompt falls back to a generic senior-engineer voice\n- The `_instructions` key is ignored by the pipeline; it's a reminder for you\n\n**Topic families and their keywords:**\n\n| Family | Matched when topic contains |\n|---|---|\n| `concurrency` | async, await, actor, task, concurrency, combine |\n| `swiftui_rendering` | render, swiftui, instruments, profil, body, view |\n| `architecture` | architecture, pattern, design, injection, dependency |\n| `testing` | test, xctest, mock, stub, tdd |\n| `performance` | performance, memory, cpu, battery, optimis |\n| `migration` | migrat, urlsession, uikit, legacy, deprecated |\n\n---\n\n## Configuration\n\nAll settings are driven by environment variables. Set them in `.env` or export directly.\n\n### Core\n\n| Variable | Default | Description |\n|---|---|---|\n| `OPENAI_API_KEY` | — | **Required** |\n| `OPENAI_MODEL` | `gpt-5-mini` | Model for all pipeline stages |\n| `OPENAI_TEMPERATURE` | `0.7` | Global temperature cap |\n| `OPENAI_REASONING_EFFORT` | model-aware | `none`\\|`minimal`\\|`low`\\|`medium`\\|`high`\\|`xhigh` — used for GPT-5 and o-series models. `gpt-5.1` defaults to `none`; `temperature` is only sent for `gpt-5.1` when reasoning is `none`. |\n\n### Content Quality\n\n| Variable | Default | Description |\n|---|---|---|\n| `EDITOR_PASS_ENABLED` | `true` | Polish for clarity, tone, readability |\n| `VOICE_PASS_ENABLED` | `true` | Remove AI writing fingerprints from prose |\n| `FACT_GROUNDING_ENABLED` | `true` | Conservative factual rewrite pass |\n| `FACT_GROUNDING_MAX_PASSES` | `1` | Max factual grounding iterations |\n| `MEDIUM_LAYOUT_REINFORCEMENT_ENABLED` | `true` | Iterative layout repair loop |\n| `MEDIUM_LAYOUT_MAX_REPAIR_PASSES` | `2` | Max layout repair iterations |\n| `MEDIUM_LAYOUT_MIN_SCORE` | `8` | Minimum passing layout score (out of 14) |\n| `SELF_REVIEW_ENABLED` | `true` | LLM self-review scoring pass |\n| `REVIEW_REPAIR_ENABLED` | `true` | Re-run editor pass if review score is low |\n| `REVIEW_REPAIR_MIN_SCORE` | `7` | Score threshold that triggers repair |\n\n### Trend Discovery\n\n| Variable | Default | Description |\n|---|---|---|\n| `TREND_DISCOVERY_ENABLED` | `true` | Enable multi-source trend scanning |\n| `TREND_SOURCES` | *(all 8)* | Comma-separated: `hackernews,reddit,apple,wwdc,viral,social,platforms,custom,websearch` |\n| `TREND_MAX_ITEMS_PER_SOURCE` | `10` | Items fetched per source |\n| `TREND_HTTP_TIMEOUT_SECONDS` | `12` | HTTP timeout for trend fetches |\n| `REDDIT_USER_AGENT` | `ios-dev-ai-writer/1.0 (weekly trend scanner)` | User-agent string for Reddit RSS requests |\n| `CUSTOM_TRENDS_FILE` | `scanners/custom_trends.json` | Path to custom trends JSON |\n| `TOPIC_INTERESTS` | *(19 topics — see config.py)* | Comma-separated list of preferred topic areas fed to the topic agent |\n| `TOPIC_SIMILARITY_THRESHOLD` | `0.72` | Cosine similarity threshold above which a candidate topic is rejected as a semantic near-duplicate |\n\n### Code Generation\n\n| Variable | Default | Description |\n|---|---|---|\n| `SWIFT_LANGUAGE_VERSION` | `6.2.4` | Target Swift release |\n| `SWIFT_COMPILER_LANGUAGE_MODE` | `6` | Maps to `swiftc -swift-version` |\n| `CODEGEN_VALIDATION_MODE` | `snippet` | `snippet`\\|`compile`\\|`none` |\n| `CODEGEN_FAILURE_MODE` | `omit` | `omit` (publish without code) \\| `error` (fail pipeline) |\n\n### Output\n\n| Variable | Default | Description |\n|---|---|---|\n| `LINKEDIN_POST_ENABLED` | `true` | Generate LinkedIn post |\n| `LINKEDIN_CODE_SNIPPET_MODE` | `auto` | `auto`\\|`always`\\|`never` |\n| `NEWSLETTER_ENABLED` | `true` | Generate newsletter issue |\n| `NEWSLETTER_NAME` | `iOS Dev Weekly` | Newsletter display name |\n| `OUTPUT_QUALITY_HISTORY_PATH` | `outputs/quality_history.json` | Append-only quality record |\n| `CROSS_REPO_DEDUP_ENABLED` | `true` | Fetch published titles from the output repo via GitHub API to guard against state drift |\n| `PUBLISHED_REPO_API_URL` | *(ios-ai-articles articles API)* | GitHub Contents API URL used for cross-repo dedup |\n\n---\n\n## Output Artifacts\n\n| Artifact | Location |\n|---|---|\n| Article (Markdown) | `outputs/articles/YYYY-MM-DD-{slug}.md` |\n| Trend snapshot | `outputs/trends/{timestamp}-trend-signals.json` |\n| LinkedIn post | `outputs/linkedin/YYYY-MM-DD-{slug}-linkedin.md` |\n| Code metadata | `outputs/codegen/YYYY-MM-DD-{slug}-codegen.json` |\n| Newsletter (Markdown) | `outputs/newsletter/YYYY-MM-DD-issue-N.md` |\n| Newsletter (HTML) | `outputs/newsletter/YYYY-MM-DD-issue-N.html` |\n| Quality history | `outputs/quality_history.json` |\n| Run summary (CI only) | `outputs/run_summary.json` — ephemeral, not committed or published |\n\nOutputs are gitignored locally and auto-published to [saurabhdave/ios-ai-articles](https://github.com/saurabhdave/ios-ai-articles) on every CI run. `run_summary.json` is the exception — it is consumed by the \"Write run summary\" CI step to populate the GitHub Actions step summary and is never committed.\n\n---\n\n## GitHub Actions\n\n**Schedule:** Monday and Thursday at 10:00 UTC (`0 10 * * 1,4`).\n\n**Secrets required:**\n\n| Secret | Purpose |\n|---|---|\n| `OPENAI_API_KEY` | LLM API access |\n| `DEPLOY_TOKEN` | GitHub PAT with `contents: write` on `saurabhdave/ios-ai-articles` |\n\n**Pipeline steps:** checkout → install deps → `python main.py` → write run summary to GitHub Actions step summary → commit quality history and newsletter counter back to this repo → publish outputs to content repo (skipped if state commit fails).\n\nA GitHub Release is created automatically when a `v*` tag is pushed (see `.github/workflows/release.yml`).\n\n---\n\n## Versioning\n\nFollows [Semantic Versioning](https://semver.org). Current version: **1.6.12** — see [CHANGELOG.md](CHANGELOG.md).\n\nTo cut a release:\n```bash\n# 1. Update VERSION, CHANGELOG.md, and pyproject.toml\n# 2. Commit and tag\ngit tag vX.Y.Z\ngit push origin vX.Y.Z\n# GitHub Actions creates the release automatically\n```\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaurabhdave%2Fios-dev-ai-writer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaurabhdave%2Fios-dev-ai-writer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaurabhdave%2Fios-dev-ai-writer/lists"}