New: Earn 20% as an AgentAlley affiliate. Share your link and earn on every sale you refer. Join now New: Earn 20% as an affiliate. Join now

Generate audio narration of blog posts using Google Gemini

Blog Audio: Gemini TTS Narration for Blog Posts

by AgriciDaniel · MIT license · ★ 2,219 on GitHub · updated Sep 18

Blog Audio: Gemini TTS Narration for Blog Posts cover

Generate audio narration of blog posts using Google Gemini TTS. Supports summary narration, full article read-aloud, and two-speaker podcast/dialogue mode with 30 voice options.

---
name: blog-audio
description: >
  Generate audio narration of blog posts using Google Gemini TTS.
  Supports summary narration, full article read-aloud, and two-speaker
  podcast/dialogue mode with 30 voice options. Outputs MP3 with HTML5
  audio embed code. Works standalone via /blog audio or internally from
  blog-write. Falls back gracefully when API key is not configured.
  Use when user says "blog audio", "narrate blog", "audio version",
  "text to speech", "tts", "podcast mode", "read aloud", "audio narration",
  "voice", "narration", "generate audio".
user-invokable: true
argument-hint: "[generate|voices|setup] [file-or-text] [--mode summary|full|dialogue] [--voice name]"
license: MIT
metadata:
  author: AgriciDaniel
  version: "2.2.0"
---

Overview

Generate professional audio narration of blog content using Google's Gemini TTS. Three modes: summary (200-300 word spoken overview), full article read-aloud, or two-speaker podcast dialogue. 30 voices, 80+ languages, HTML5 embed output.

Manual install (npx, ZIP)
Claude Codenpx skills add AgriciDaniel/claude-blog --skill blog-audio -g -a claude-code
Claude Code (manual)mkdir -p ~/.claude/skills && curl -sL https://codeload.github.com/AgriciDaniel/claude-blog/tar.gz/2500d4c76503 \ | tar -xz -C ~/.claude/skills --strip-components=2 claude-blog-2500d4c76503/skills/blog-audio
Windows (PowerShell)New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Invoke-WebRequest "https://m.agentalley.io/zip/46d993398531a3dc28b0/blog-audio.zip" -OutFile "$env:TEMP\blog-audio.zip"; Expand-Archive "$env:TEMP\blog-audio.zip" -DestinationPath "$HOME\.claude\skills" -Force
Claude.ai / desktopDownload the ZIP and upload it under Settings → Capabilities → Skills.
Codex, Cursor, other agentsPoint them at SKILL.md. The other files are plain text.

Connects with Agent Alley MCP · agentalley.io/mcp

Files included

  • SKILL.md8.6 KB
  • references/1 file · 5.2 KB
  • scripts/6 files · 69.8 KB
Read the files here
AgriciDaniel/2500d4c8 files
SKILL.md
Show the full text252 lines

Blog Audio: Gemini TTS Narration for Blog Posts

Generate professional audio narration of blog content using Google's Gemini TTS. Three modes: summary (200-300 word spoken overview), full article read-aloud, or two-speaker podcast dialogue. 30 voices, 80+ languages, HTML5 embed output.

Quick Reference

Command What it does
/blog audio generate <file> Generate audio narration of a blog post
/blog audio voices Show available voices with characteristics
/blog audio setup Check/configure API key for Gemini TTS

Prerequisites

  • Python 3.11+ (venv managed automatically by run.py)
  • GOOGLE_AI_API_KEY environment variable (same key used by blog-image)
  • FFmpeg (for WAV-to-MP3 conversion; falls back to WAV if missing)

Always Use run.py Wrapper

# CORRECT:
python3 scripts/run.py generate_audio.py --text "..." --voice Charon --json

# WRONG:
python3 scripts/generate_audio.py --text "..."  # Fails without venv

API Key Check (Gate Pattern)

Before generating audio, check for the API key:

test -n "${GOOGLE_AI_API_KEY:-}" && echo "GOOGLE_AI_API_KEY is set" || echo "GOOGLE_AI_API_KEY is not set"
  • If set: proceed with generation
  • If not set: guide the user: "Audio generation requires a Google AI API key. Get one free at https://aistudio.google.com/apikey Then set it: export GOOGLE_AI_API_KEY=your-key This can be the same key used by /blog image, but it must be exported in the shell."
  • When called internally (from blog-write): return silently if key is missing. Never block the writing workflow.

Setup

For /blog audio setup:

  1. Check if GOOGLE_AI_API_KEY is set in environment
  2. If blog-image uses project .mcp.json, confirm the referenced env var is exported
  3. If not, guide user to https://aistudio.google.com/apikey
  4. Verify with a dry run: python3 scripts/run.py generate_audio.py --text "Test" --dry-run --json

Voice Selection

For /blog audio voices:

Load references/voices.md and present the voice catalog to the user.

Ask the user which voice they prefer, or recommend based on content type:

  • Article narration: Charon (Informative) or Sadaltager (Knowledgeable)
  • Tutorial/how-to: Achird (Friendly) or Sulafat (Warm)
  • News/analysis: Rasalgethi (Informative) or Schedar (Even)
  • Lifestyle/wellness: Aoede (Breezy) or Vindemiatrix (Gentle)
  • Dialogue host: Puck (Upbeat) or Laomedeia (Upbeat)
  • Dialogue expert: Kore (Firm) or Charon (Informative)

Generation Workflow

For /blog audio generate <file>:

Step 1: Read the Blog Post

Read the file and extract:

  • Title (from H1 or frontmatter)
  • Full content (markdown body)
  • Approximate word count
Step 2: Choose Mode

Ask the user (or auto-select if they specified --mode):

Mode When to use Output
Summary Quick audio overview (1-2 min) 200-300 word spoken summary
Full Complete read-aloud (5-15 min) Full article as natural speech
Dialogue Podcast-style (3-8 min) Two-person conversation about the article
Step 3: Prepare Text

Claude prepares the text; the script does TTS only.

Summary mode: Write a 200-300 word spoken summary of the article. Rules:

  • Write as natural speech, not written text
  • Open with the article's key finding or answer
  • Cover 3-5 main takeaways
  • Close with actionable advice
  • No markdown, no "In this article...", no meta-commentary
  • Use conversational transitions ("Here's what matters...", "The key finding is...")

Full mode: Strip the markdown content to clean spoken text:

  • Headings become natural transitions ("Next, let's look at...")
  • Links become plain text (remove URLs, keep anchor text)
  • Images and charts: omit or briefly describe ("As the data shows...")
  • Code blocks: describe verbally ("The code uses a for-loop to...")
  • Lists: convert to natural sentences
  • Remove frontmatter, schema markup, HTML tags
  • Add brief intro: "This is [title], published on [date]."

Dialogue mode: Write a 2-person conversation script about the article:

  • Speaker1 = Host (curious, asks good questions)
  • Speaker2 = Expert (knowledgeable, gives clear answers)
  • Format each line as: Speaker1: What's the key takeaway here?
  • Cover the article's main points conversationally
  • 15-25 exchanges (produces ~3-8 minutes)
  • Natural, not stilted ("That's a great point" over "Indeed, as the research indicates")
Step 4: Select Voice

If the user chose a voice, use it. Otherwise, recommend based on mode:

  • Summary/Full: default to Charon (Informative)
  • Dialogue: default to Puck (Host) + Kore (Expert)
Step 5: Generate Audio

Write the prepared text to a file under the working directory, then call:

# Single voice (summary or full mode)
python3 scripts/run.py generate_audio.py \
  --text-file blog_audio_prepared.txt \
  --voice Charon \
  --model flash \
  --output audio/post-slug.mp3 \
  --json

# Two voices (dialogue mode)
python3 scripts/run.py generate_audio.py \
  --text-file blog_audio_dialogue.txt \
  --voice Puck \
  --voice2 Kore \
  --model pro \
  --output audio/post-slug-dialogue.mp3 \
  --json

Model selection:

  • flash (default): maps to gemini-3.1-flash-tts-preview, good for summaries and standard narration.
  • flash31: explicit alias for gemini-3.1-flash-tts-preview.
  • legacy-flash25: retained only for older compatibility.
  • pro or legacy-pro25: maps to gemini-2.5-pro-preview-tts, use only when needed.
Step 6: Deliver

Present the result to the user:

  1. File path: where the audio was saved
  2. Duration: human-readable (e.g., "3:42")
  3. Embed code: ready-to-paste HTML5 audio tag
  4. Cost: estimated API cost
  5. Placement suggestion: where to insert the embed in the blog post

Embedding Guide

Standard HTML (Hugo, Jekyll, static sites)
<audio controls preload="metadata">
  <source src="audio/post-slug.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>
MDX (Next.js, Gatsby)
<audio controls preload="metadata">
  <source src="/audio/post-slug.mp3" type="audio/mpeg" />
</audio>
WordPress
[audio src="audio/post-slug.mp3"]
Placement

Insert the audio player after the introduction (below the first H2) or at the very top of the article with a label: "Listen to this article" or "Audio version".

Internal API (for blog-write)

When invoked internally from blog-write:

Input:

  • text: Prepared text (already cleaned by Claude)
  • voice: Voice name (default: Charon)
  • voice2: Second voice for dialogue (optional)
  • model: flash or pro
  • output_path: Where to save the file

Output:

### Audio Narration
- **Path:** /path/to/audio/post-slug.mp3
- **Duration:** 3:42
- **Voice:** Charon
- **Embed:** `<audio controls preload="metadata"><source src="audio/post-slug.mp3" type="audio/mpeg"></audio>`

Graceful fallback: If GOOGLE_AI_API_KEY is not set, return immediately with no error. The writing workflow continues without audio. Never block blog-write because audio generation is unavailable.

Error Handling

Error Resolution
GOOGLE_AI_API_KEY not set Get key at https://aistudio.google.com/apikey
FFmpeg not found Install: sudo apt install ffmpeg. Falls back to WAV output.
Rate limited Wait and retry. Check limits at https://aistudio.google.com/rate-limit
Text too long (>8,192 input tokens) Split into sections around 7,800 tokens; the script chunks and stitches prepared text
Unknown voice name Run /blog audio voices to see valid options
API error Check key validity and model availability
API key missing (internal call) Return silently: writing workflow continues

Reference Documentation

Load on-demand: do NOT load all at startup:

  • references/voices.md: Full 30-voice catalog, recommendations by content type, dialogue pairings
1---
2name: blog-audio
3description: >
4 Generate audio narration of blog posts using Google Gemini TTS.
5 Supports summary narration, full article read-aloud, and two-speaker
6 podcast/dialogue mode with 30 voice options. Outputs MP3 with HTML5
7 audio embed code. Works standalone via /blog audio or internally from
8 blog-write. Falls back gracefully when API key is not configured.
9 Use when user says "blog audio", "narrate blog", "audio version",
10 "text to speech", "tts", "podcast mode", "read aloud", "audio narration",
11 "voice", "narration", "generate audio".
12user-invokable: true
13argument-hint: "[generate|voices|setup] [file-or-text] [--mode summary|full|dialogue] [--voice name]"
14license: MIT
15metadata:
16 author: AgriciDaniel
17 version: "2.2.0"
18---
19 
20# Blog Audio: Gemini TTS Narration for Blog Posts
21 
22Generate professional audio narration of blog content using Google's Gemini TTS.
23Three modes: summary (200-300 word spoken overview), full article read-aloud,
24or two-speaker podcast dialogue. 30 voices, 80+ languages, HTML5 embed output.
25 
26## Quick Reference
27 
28| Command | What it does |
29|---------|-------------|
30| `/blog audio generate <file>` | Generate audio narration of a blog post |
31| `/blog audio voices` | Show available voices with characteristics |
32| `/blog audio setup` | Check/configure API key for Gemini TTS |
33 
34## Prerequisites
35 
36- Python 3.11+ (venv managed automatically by `run.py`)
37- `GOOGLE_AI_API_KEY` environment variable (same key used by blog-image)
38- FFmpeg (for WAV-to-MP3 conversion; falls back to WAV if missing)
39 
40## Always Use run.py Wrapper
41 
42```bash
43# CORRECT:
44python3 scripts/run.py generate_audio.py --text "..." --voice Charon --json
45 
46# WRONG:
47python3 scripts/generate_audio.py --text "..." # Fails without venv
48```
49 
50## API Key Check (Gate Pattern)
51 
52Before generating audio, check for the API key:
53 
54```bash
55test -n "${GOOGLE_AI_API_KEY:-}" && echo "GOOGLE_AI_API_KEY is set" || echo "GOOGLE_AI_API_KEY is not set"
56```
57 
58- If set: proceed with generation
59- If not set: guide the user:
60 "Audio generation requires a Google AI API key. Get one free at https://aistudio.google.com/apikey
61 Then set it: `export GOOGLE_AI_API_KEY=your-key`
62 This can be the same key used by `/blog image`, but it must be exported in the shell."
63- **When called internally** (from blog-write): return silently if key is missing.
64 Never block the writing workflow.
65 
66## Setup
67 
68For `/blog audio setup`:
69 
701. Check if `GOOGLE_AI_API_KEY` is set in environment
712. If blog-image uses project `.mcp.json`, confirm the referenced env var is exported
723. If not, guide user to https://aistudio.google.com/apikey
734. Verify with a dry run: `python3 scripts/run.py generate_audio.py --text "Test" --dry-run --json`
74 
75## Voice Selection
76 
77For `/blog audio voices`:
78 
79Load `references/voices.md` and present the voice catalog to the user.
80 
81Ask the user which voice they prefer, or recommend based on content type:
82- **Article narration**: Charon (Informative) or Sadaltager (Knowledgeable)
83- **Tutorial/how-to**: Achird (Friendly) or Sulafat (Warm)
84- **News/analysis**: Rasalgethi (Informative) or Schedar (Even)
85- **Lifestyle/wellness**: Aoede (Breezy) or Vindemiatrix (Gentle)
86- **Dialogue host**: Puck (Upbeat) or Laomedeia (Upbeat)
87- **Dialogue expert**: Kore (Firm) or Charon (Informative)
88 
89## Generation Workflow
90 
91For `/blog audio generate <file>`:
92 
93### Step 1: Read the Blog Post
94 
95Read the file and extract:
96- Title (from H1 or frontmatter)
97- Full content (markdown body)
98- Approximate word count
99 
100### Step 2: Choose Mode
101 
102Ask the user (or auto-select if they specified `--mode`):
103 
104| Mode | When to use | Output |
105|------|-------------|--------|
106| **Summary** | Quick audio overview (1-2 min) | 200-300 word spoken summary |
107| **Full** | Complete read-aloud (5-15 min) | Full article as natural speech |
108| **Dialogue** | Podcast-style (3-8 min) | Two-person conversation about the article |
109 
110### Step 3: Prepare Text
111 
112Claude prepares the text; the script does TTS only.
113 
114**Summary mode:**
115Write a 200-300 word spoken summary of the article. Rules:
116- Write as natural speech, not written text
117- Open with the article's key finding or answer
118- Cover 3-5 main takeaways
119- Close with actionable advice
120- No markdown, no "In this article...", no meta-commentary
121- Use conversational transitions ("Here's what matters...", "The key finding is...")
122 
123**Full mode:**
124Strip the markdown content to clean spoken text:
125- Headings become natural transitions ("Next, let's look at...")
126- Links become plain text (remove URLs, keep anchor text)
127- Images and charts: omit or briefly describe ("As the data shows...")
128- Code blocks: describe verbally ("The code uses a for-loop to...")
129- Lists: convert to natural sentences
130- Remove frontmatter, schema markup, HTML tags
131- Add brief intro: "This is [title], published on [date]."
132 
133**Dialogue mode:**
134Write a 2-person conversation script about the article:
135- Speaker1 = Host (curious, asks good questions)
136- Speaker2 = Expert (knowledgeable, gives clear answers)
137- Format each line as: `Speaker1: What's the key takeaway here?`
138- Cover the article's main points conversationally
139- 15-25 exchanges (produces ~3-8 minutes)
140- Natural, not stilted ("That's a great point" over "Indeed, as the research indicates")
141 
142### Step 4: Select Voice
143 
144If the user chose a voice, use it. Otherwise, recommend based on mode:
145- Summary/Full: default to Charon (Informative)
146- Dialogue: default to Puck (Host) + Kore (Expert)
147 
148### Step 5: Generate Audio
149 
150Write the prepared text to a file under the working directory, then call:
151 
152```bash
153# Single voice (summary or full mode)
154python3 scripts/run.py generate_audio.py \
155 --text-file blog_audio_prepared.txt \
156 --voice Charon \
157 --model flash \
158 --output audio/post-slug.mp3 \
159 --json
160 
161# Two voices (dialogue mode)
162python3 scripts/run.py generate_audio.py \
163 --text-file blog_audio_dialogue.txt \
164 --voice Puck \
165 --voice2 Kore \
166 --model pro \
167 --output audio/post-slug-dialogue.mp3 \
168 --json
169```
170 
171**Model selection:**
172- `flash` (default): maps to `gemini-3.1-flash-tts-preview`, good for summaries and standard narration.
173- `flash31`: explicit alias for `gemini-3.1-flash-tts-preview`.
174- `legacy-flash25`: retained only for older compatibility.
175- `pro` or `legacy-pro25`: maps to `gemini-2.5-pro-preview-tts`, use only when needed.
176 
177### Step 6: Deliver
178 
179Present the result to the user:
1801. **File path**: where the audio was saved
1812. **Duration**: human-readable (e.g., "3:42")
1823. **Embed code**: ready-to-paste HTML5 audio tag
1834. **Cost**: estimated API cost
1845. **Placement suggestion**: where to insert the embed in the blog post
185 
186## Embedding Guide
187 
188### Standard HTML (Hugo, Jekyll, static sites)
189```html
190<audio controls preload="metadata">
191 <source src="audio/post-slug.mp3" type="audio/mpeg">
192 Your browser does not support the audio element.
193</audio>
194```
195 
196### MDX (Next.js, Gatsby)
197```jsx
198<audio controls preload="metadata">
199 <source src="/audio/post-slug.mp3" type="audio/mpeg" />
200</audio>
201```
202 
203### WordPress
204```
205[audio src="audio/post-slug.mp3"]
206```
207 
208### Placement
209Insert the audio player after the introduction (below the first H2) or at the
210very top of the article with a label: "Listen to this article" or "Audio version".
211 
212## Internal API (for blog-write)
213 
214When invoked internally from blog-write:
215 
216**Input:**
217- `text`: Prepared text (already cleaned by Claude)
218- `voice`: Voice name (default: Charon)
219- `voice2`: Second voice for dialogue (optional)
220- `model`: flash or pro
221- `output_path`: Where to save the file
222 
223**Output:**
224```markdown
225### Audio Narration
226- **Path:** /path/to/audio/post-slug.mp3
227- **Duration:** 3:42
228- **Voice:** Charon
229- **Embed:** `<audio controls preload="metadata"><source src="audio/post-slug.mp3" type="audio/mpeg"></audio>`
230```
231 
232**Graceful fallback:** If `GOOGLE_AI_API_KEY` is not set, return immediately
233with no error. The writing workflow continues without audio. Never block
234blog-write because audio generation is unavailable.
235 
236## Error Handling
237 
238| Error | Resolution |
239|-------|-----------|
240| GOOGLE_AI_API_KEY not set | Get key at https://aistudio.google.com/apikey |
241| FFmpeg not found | Install: `sudo apt install ffmpeg`. Falls back to WAV output. |
242| Rate limited | Wait and retry. Check limits at https://aistudio.google.com/rate-limit |
243| Text too long (>8,192 input tokens) | Split into sections around 7,800 tokens; the script chunks and stitches prepared text |
244| Unknown voice name | Run `/blog audio voices` to see valid options |
245| API error | Check key validity and model availability |
246| API key missing (internal call) | Return silently: writing workflow continues |
247 
248## Reference Documentation
249 
250Load on-demand: do NOT load all at startup:
251- `references/voices.md`: Full 30-voice catalog, recommendations by content type, dialogue pairings
252 
View all files on GitHub →

Details

Updated
Sep 18, 2026
License
MIT
Stars on the repo
★ 2,219
Checked
Source
AgriciDaniel/claude-blog ↗
Creator
AgriciDaniel · 130 listings
View creator

Made with blog-audio

Blog Audio: Gemini TTS Narration for Blog Posts — claude-blog /blog write demo: end-to-end article generation with the 5-gate Delivery Contract (from the AgriciDaniel/claude-blog README)
Blog Audio: Gemini TTS Narration for Blog Posts — claude-blog /blog write demo: end-to-end article generation with the 5-gate Delivery Contract (from the AgriciDaniel/claude-blog README)

by AgriciDaniel

Similar skills

See all →
SkillDraft written long-form content in your voicetimscheuerai/content-vault · by timscheueraiDraft written long-form content (newsletter issue, blog post, Substack issue) in your voice. 800-2000 words, structured intro + body + CTA, paragraph-by-paragraph not list-by-list. Pulls voice from /linkedin-copywriter's corpus (same author, same register · longer arc). Runs the brand-doc skill checklist before output. Logs the row in the Notion Content DB and (for newsletters) drafts a separate punchy subject line. Use when asked to "write a newsletter", "draft a blog post", "long-form X", "Substack issue about Y", "weekly newsletter", or "/long-form". For spoken video scripts (YouTube longs / shorts) use /youtube-script instead · the cadence is fundamentally different.★ 37 GitHub starsPromptA comprehensive producer-assistant prompt for podcastersMaster Podcast Producer & Sonic Storytellerf/prompts.chat · by Sercan SolmazA comprehensive producer-assistant prompt for podcasters that goes beyond standard question lists. It designs the episode's sound design (sonic cues), narrative arc, and opening hook to ensure maximum listener retention.New listingMCPPodcast content analysis specialistPodcast content analyzerdavila7/claude-code-templates · by davila7Podcast content analysis specialist. Use PROACTIVELY for identifying viral moments, creating chapter markers, extracting SEO keywords, and scoring engagement potential from transcripts.★ 32.3k GitHub starsMCPPodcast editing and post-production specialistPodcast editordavila7/claude-code-templates · by davila7Podcast editing and post-production specialist. Use PROACTIVELY for audio editing guidance, show notes creation, chapter markers, timestamp management, and podcast publishing workflows.★ 32.3k GitHub starsMCPPodcast metadata and show notes specialistPodcast metadata specialistdavila7/claude-code-templates · by davila7Podcast metadata and show notes specialist. Use PROACTIVELY for SEO-optimized titles, chapter markers, platform-specific descriptions, and comprehensive publishing metadata.★ 32.3k GitHub starsMCPAudio transcription specialistPodcast transcriberdavila7/claude-code-templates · by davila7Audio transcription specialist. Use PROACTIVELY for extracting accurate transcripts from media files with speaker identification, timestamps, and structured output.★ 32.3k GitHub stars

More from AgriciDaniel

SkillThe Lighthouse Agentic Browsing fractionAgentic Browsing ReadinessAgriciDaniel/claude-seo · by AgriciDanielAudit and fix agent readiness: the Lighthouse Agentic Browsing fraction, accessibility tree for agents, robots.txt and Content-Signal for AI agents, WAF treatment of agent traffic, llms.txt, Markdown delivery, ai-catalog.json, /.well-known discovery files, and WebMCP tools. Exclude AI citability and brand signals (seo-geo) and commerce protocol depth (seo-ecommerce).★ 17.4k GitHub starsSkillOptimize content for AI Overviews , ChatGPT web searchAI Search / GEO Optimization (May 2026)AgriciDaniel/claude-seo · by AgriciDanielOptimize content for AI Overviews (formerly SGE), ChatGPT web search, Perplexity, and other AI-powered search experiences. Generative Engine Optimization (GEO) analysis including brand mention signals, AI crawler accessibility, llms.txt compliance, passage-level citability scoring, and platform-specific optimization. Use when user says "AI Overviews", "SGE", GEO", "AI search", "LLM optimization", "Perplexity", "AI citations", ChatGPT search", or "AI visibility".★ 17.4k GitHub starsSkillReferring domains, anchor text distributionBacklink Profile AnalysisAgriciDaniel/claude-seo · by AgriciDanielBacklink profile analysis: referring domains, anchor text distribution, toxic link detection, competitor gap analysis. Works with free APIs (Moz, Bing Webmaster, Common Crawl) and DataForSEO extension. Use when user says backlinks, link profile, referring domains, anchor text, toxic links, link gap, link building, disavow, or backlink audit.★ 17.4k GitHub starsSkillYou share your Amazon advertising numbers and goals; you getAmazon Ads AuditAgriciDaniel/claude-ads · by AgriciDanielYou share your Amazon advertising numbers and goals; you get back a plain-language review of what's draining your budget and what to change first.★ 9.5k GitHub starsSkillPaste what you're spending on Apple Search AdsApple Ads AuditAgriciDaniel/claude-ads · by AgriciDanielPaste what you're spending on Apple Search Ads and your recent numbers; get back a plain review of what's wasting money and what to fix first.★ 9.5k GitHub starsSkillPaste the sales and spend numbers each ad platform claimsAttribution AuditAgriciDaniel/claude-ads · by AgriciDanielPaste the sales and spend numbers each ad platform claims, and get back a plain explanation of why they disagree and which figure to trust.★ 9.5k GitHub stars