newsletter-writer — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited newsletter-writer (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Weekly newsletter pipeline: scaffold structure, gather data, query the author, synthesize in their voice, review, send.
Worked example throughout: a fictional newsletter, The Sleep Brief, written by a popular-science sleep author whose community site is deeprest.example.com and whose X handle is @sleepauthor. Substitute your own newsletter name, brand, domain, and handle — they live in your project's config.md (see Configuration).
This newsletter is behind the scenes, not a content recap. Readers can go read the tweets and articles themselves. The newsletter gives them what they can't get anywhere else: WHY the author wrote it, what they were trying to achieve, the deeper reasoning that didn't fit in 280 characters, and how the author's project interacted with the world that week. Every section answers "what was I thinking?" not "what did I post."
/newsletter or /newsletter draft — Full pipeline: scaffold → gather → query → enhanced scaffold → draft → review/newsletter scaffold — Create doc with empty skeleton structure (header, HRs, placeholder sections, footer)/newsletter gather — Data collection only (metrics, top tweets, article)/newsletter review — Pre-send checklist + /polish 90/100 scoring on every section/newsletter send — Publish to your OpenWriter site, which emails it to your subscribers/newsletter status — Subscriber count, last sent, domain statusFormat rules for newsletter documents (subject, preview line, body, images, cover, sending and scheduling) arrive from OpenWriter when the document is opened. This skill keeps voice, structure, section strategy and the weekly pipeline.
claude install github:travsteward/authors-voice.list_documents before trying external APIs.Create the newsletter document with the empty skeleton FIRST. The author sees the structure in OpenWriter before any ideation begins. This is the visceral starting point — the bones of the newsletter visible in the editor.
Check list_newsletter_issues for the latest sent issue number. Increment by 1.
1. create_document({ title: "The Sleep Brief #N", content_type: "newsletter" })
2. populate_document with the full skeleton belowUse a single populate_document call with this TipTap JSON structure. All section titles are placeholders — they get replaced during drafting after gather/query:
[
{"type": "heading", "attrs": {"level": 1}, "content": [{"type": "text", "text": "In this Issue:"}]},
{"type": "paragraph", "content": [
{"type": "text", "text": "Section 1"},
{"type": "hardBreak"},
{"type": "text", "text": "Section 2"},
{"type": "hardBreak"},
{"type": "text", "text": "Section 3"},
{"type": "hardBreak"},
{"type": "text", "text": "Section 4"},
{"type": "hardBreak"},
{"type": "text", "text": "Section 5"}
]},
{"type": "horizontalRule"},
{"type": "paragraph", "content": [
{"type": "text", "marks": [{"type": "italic"}], "text": "The Sleep Brief: Weekly current events and X highlights from the "},
{"type": "text", "marks": [{"type": "italic"}, {"type": "bold"}, {"type": "link", "attrs": {"href": "https://deeprest.example.com"}}], "text": "DeepRest"},
{"type": "text", "marks": [{"type": "italic"}], "text": " worldview."}
]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section 1"}]},
{"type": "paragraph", "content": [{"type": "text", "text": "[Current events or lead reflection]"}]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section 2"}]},
{"type": "paragraph", "content": [{"type": "text", "text": "[Tweet commentary]"}]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section 3"}]},
{"type": "paragraph", "content": [{"type": "text", "text": "[Tweet commentary]"}]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section 4"}]},
{"type": "paragraph", "content": [{"type": "text", "text": "[Article or thread deep dive]"}]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section 5"}]},
{"type": "paragraph", "content": [{"type": "text", "text": "[From the Archives]"}]},
{"type": "horizontalRule"},
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "The Reading Room"}]},
{"type": "paragraph", "content": [
{"type": "text", "marks": [{"type": "link", "attrs": {"href": "https://deeprest.example.com"}}], "text": "DeepRest"},
{"type": "text", "text": " is the home for people fixing their sleep for good. [Edition-specific CTA — echo themes]."}
]},
{"type": "paragraph", "content": [
{"type": "text", "marks": [{"type": "italic"}], "text": "The Sleep Brief is a weekly email from DeepRest."}
]},
{"type": "paragraph", "content": [
{"type": "text", "marks": [{"type": "italic"}], "text": "Powered by "},
{"type": "text", "marks": [{"type": "italic"}, {"type": "bold"}, {"type": "link", "attrs": {"href": "https://www.openwriter.io"}}], "text": "OpenWriter"}
]}
]After scaffolding, tell the author the skeleton is live at localhost:5050. Then proceed to gather.
All newsletter docs live in the "Newsletter" workspace. Order: most recent at top, working notes directly below its newsletter, oldest at bottom.
The Sleep Brief #N ← newest
SB#N — Working Notes
The Sleep Brief #N-1
...
The Sleep Brief #1 ← oldestWhen creating the scaffold and companion doc, add both to the Newsletter workspace and use move_item with afterId to place the working notes directly after its newsletter.
/newsletter scaffoldWhen run standalone (not part of the full pipeline), just scaffold and stop. The author reviews the bones in the editor.
Collect all data before presenting anything to the author.
If the author keeps a local tweet-metrics database (a JSON file of posts + view counts, refreshed via fxtwitter or similar), pull from it:
recent_performance[] — tweets from last 7 days sorted by viewsthreshold_crossings[] — tweets that crossed 10K/25K/50K/100K this weekIf there's no metrics database, ask the author for their top 2-3 posts of the week (links or IDs) and fetch each via fxtwitter (below).
If a historical metrics database exists, search it directly for thematically relevant older posts:
// Search a tweets.json-style database for archive candidates by theme
node -e "
const arr = JSON.parse(require('fs').readFileSync('<path-to-your-tweets.json>','utf8'));
const themes = /your|regex|here/i;
const hits = (Array.isArray(arr) ? arr : Object.values(arr))
.filter(e => themes.test(e.title + ' ' + (e.topic || '') + ' ' + (e.notes || '')));
hits.sort((a,b) => (a.iterations[0]?.posted_at || '').localeCompare(b.iterations[0]?.posted_at || ''));
hits.forEach(t => {
const i = t.iterations[0];
console.log((i?.posted_at||'?') + ' | ' + t.peak_views_thousands + 'K | ' + t.title + ' | ' + (t.topic||'') + ' | ' + (i?.tweet_id||'no-id'));
});
"Guidance:
Read the top 2-3 performing tweets via fxtwitter (free):
WebFetch: https://api.fxtwitter.com/{user}/status/{tweet_id}Get full text, metrics, quoted tweet context.
Screenshot every tweet referenced in the newsletter (e.g. via a tweet-screenshot tool such as the x-reader skill, if installed, or any screenshot service). This is a required step — every section that references a tweet gets a visual.
Copy each screenshot into OpenWriter's image directory, then insert into the section:
cp <screenshot-output-dir>/{tweet_id}.png ~/.openwriter/profiles/Default/_images/{descriptive-name}.png#### Screenshot Logic: Original vs QT
For each tweet section, determine what to screenshot:
[h2] Section Headline
[img] ← screenshot of the ORIGINAL tweet being quoted
[p] ← brief context line
[bq] ← author's key quote from their QT response
[p] ← **view count** + extended commentary
[p] ← link to original postThe screenshot shows what sparked the response. The blockquote shows the author's take. The prose below delivers the WHY. This avoids screenshotting long-form QTs which render as massive images.
#### Naming Convention Use descriptive names, not tweet IDs: caffeine-study.png, sleepauthor-90min-qt.png, sleepauthor-naps.png.
Search OpenWriter for this week's article:
mcp__openwriter__list_documents → find by date + title
mcp__openwriter__read_pad → read full contentArticles are always in OpenWriter. Do NOT rely on fxtwitter for long-form content.
If the author has X API access set up (e.g. via an x-api tooling skill), pull personalized trending topics. Otherwise skip — current events context can come straight from the author in the query phase.
Present your findings, then ask questions. Do not write any content yet.
Show the author:
Q1: Current Events
"What was the most prominent discussion topic in your community this week? What's the conventional viewpoint you're challenging?"
Wait for answer.
Q2: The Author's Position
"What's your position that differs from the conventional viewpoint? Give me the reasoning."
Wait for answer.
Q3: Tweet Highlights For each top tweet, ask:
"Here's your [X views] tweet: '[first line]'. Why did you write this? What were you trying to achieve? What's the thinking behind it that the tweet itself didn't say?"
Wait for answer on each.
Q4: Article Framing
"Your article '[title]' — why did you write this one? What were you trying to accomplish? What should the reader take away that goes beyond just reading the piece?"
Wait for answer (or synthesize from the article content if they defer).
Q5: Archive Pick
"For From the Archives, here are your options: [list]. Which one, and any context you want to add?"
Wait for answer.
This is the meta-pattern for any section where the author explains a position. It applies to the current events section, tweet sections, article section — anywhere the author gives their thinking.
The author provides the WHY. The agent provides the WHAT (facts) and the HOW (voice-matched prose). Never the other way around.
After the query, create a companion document in OpenWriter that captures everything gathered and discussed. This is the working doc — the newsletter skeleton stays clean.
create_document({ title: "SB#N — Working Notes", content_type: "doc" })Contents:
Why this exists: If context is lost mid-build, the companion doc has everything needed to resume. More importantly, refining content shape in the companion doc BEFORE writing to the newsletter prevents gnarled implementations where early AI text marshals rewrites in the wrong direction.
After gather and query, update the skeleton with real headlines and content assignments. The author now sees the newsletter with concrete section titles and knows what goes where before any prose is written.
Screenshot every tweet that will appear in the newsletter. Do this NOW, during enhanced scaffold, so images are ready for drafting. Skip if a section doesn't reference a tweet. Copy screenshots into ~/.openwriter/profiles/Default/_images/ with descriptive names (see Phase 2).
Use read_pad to get node IDs from the Phase 1 skeleton, then write_to_pad to:
The author reviews the enhanced skeleton and approves the lineup before any writing begins.
Now you write. Using the author's answers as raw material, enriched with researched specifics.
Where to draft depends on whether the author has written seed content in the newsletter:
Use the authors-voice skill (Critical Rule 1). Load the author's anchor + NEVER rules + fingerprint per its Apply Protocol. If the author hasn't set up authors-voice yet, run its setup first — voice-matched drafting is non-negotiable for this pipeline.
Pull 2-3 sets of the author's writing samples matching each section's topic (from the authors-voice corpus, or recent posts/articles if no corpus exists yet). These samples are ground truth for how the author phrases things.
Each section gets a specific, content-driven headline — not a generic label. Use /polish (ad-legends scoring) to craft headlines that grab on specificity. Score each 0-100 on stopping power + specificity + brand fit. Don't settle below 90.
Examples of what TO do:
Examples of what NOT to do:
Apply voice profile rules as hard constraints — the specifics come from the author's authors-voice profile (diction, syntax, rhetoric, sentence distribution, contraction pattern). Treat every profile rule as a behavioral constraint, not a suggestion.
Run the /anti-ai skill's full check list:
The doc already exists from Phase 1. Use read_pad to get node IDs, then write_to_pad to replace each placeholder section with voice-matched content. Update the "In this Issue" list titles to match final headlines.
1. read_pad → get node IDs for each [Section N content] placeholder
2. write_to_pad for each section (images, paragraphs, links)
3. Update "In this Issue" paragraph with final section titles
4. Update footer CTA to echo edition themes
5. User reviews at localhost:5050
6. Iterate via write_to_pad as neededThe newsletter has a precise structure in OpenWriter. Follow this exactly.
[h1] In this Issue:
[p] ← hardBreak-separated list of section titles (see below)
[hr]
[p] ← italics tagline (see below)
[hr]Intro list — one paragraph, each item on its own line:
{"type": "paragraph", "content": [
{"type": "text", "text": "Section Title One"},
{"type": "hardBreak"},
{"type": "text", "text": "Section Title Two"},
{"type": "hardBreak"},
{"type": "text", "text": "Section Title Three"}
]}Tagline — italics with bold link, sits between two HRs:
*The Sleep Brief: Weekly current events and X highlights from the **[DeepRest](deeprest.example.com)** worldview.*For quote tweets (most common):
[h2] Section Headline
[img] ← screenshot of the ORIGINAL tweet being quoted
[p] ← brief context line
[bq] ← author's punchiest lines from their QT response
[p] ← **view count** + extended commentary (2-4 paragraphs)
[p] ← link: [View the post →](url)
[hr]For standalone tweets / articles:
[h2] Section Headline
[img] ← tweet screenshot or article cover
[p] ← bold callout (view count, word count)
[p] ← commentary paragraphs (2-4)
[p] ← link: [Read the full article →](url) or [View the post →](url)
[hr][hr]
[h2] The Reading Room
[p] ← CTA with link to your brand site
[p] ← *The Sleep Brief is a weekly email from DeepRest.*
[p] ← *Powered by **[OpenWriter](www.openwriter.io)*The newsletter is built from proven content — tweets and articles that already hit. Don't invent new topics. Identify the week's top performers from your metrics database (or the author's nominations), plus archive candidates for the final section. Check OpenWriter doc metadata for tweetContext.lastPost.tweetUrl to find post URLs.
The most powerful newsletter sections don't just recap a thread or article — they address the #1 objection readers would have even if they agree with the premise.
Pattern:
Example: a "Sleep Debt" thread (126K views) argues lost sleep compounds and can't be fully repaid on weekends. The newsletter section isn't "here's what the thread said." It's "So Just Sleep In on Saturday?" — the #1 objection, dismantled. The section title IS the objection.
This pattern works because:
Every section needs a visual. For articles and threads posted to X:
https://api.fxtwitter.com/{user}/status/{id} curl -sL "{image_url}" -o ~/.openwriter/profiles/Default/_images/{descriptive-name}.jpgFor tweet screenshots, use your screenshot tool (see Phase 2: Gather).
/newsletter review)Pre-send checklist. Run this before sending. Can also be invoked standalone.
Read the full newsletter via read_pad and verify every item:
For each section, run /polish on the opening sentence. Score hook, angle, rhythm, punch, closer on a 0-10 scale. Overall must be 90+. Fix any section below 90 before proceeding.
Run the full /anti-ai detection pass:
Present the full review to the author. Fix any issues. Then proceed to send.
After author approves the review and accepts every pending change, send a test to the author first, then send (or schedule for the send day) with the approved subject line and preview text. The issue's contract from OpenWriter says which call does each and where the subject and preview go.
Always exactly 5 content sections. Modeled after the strongest early issue of the worked-example newsletter.
| # | Section Type | Source | Author Input Required |
|---|---|---|---|
| 1 | Current events OR lead reflection | HOT current event decoded through the brand lens, OR the week's biggest post | YES — position or objection angle |
| 2 | Tweet commentary | High-performing tweet with behind-the-scenes thinking | YES — why you wrote it |
| 3 | Tweet commentary | Another high-performing tweet, different topic | YES — why you wrote it |
| 4 | Article or thread deep dive | This week's X Article or long thread | YES — objection angle + position |
| 5 | From the Archives | High-performing historical tweet (50K+) with fresh insights | Optional — pick + new angle |
| — | Footer | Static CTA + branding | NO — same every week |
If there's a HOT current event (major news, new study, cultural moment), it leads. Decoded through the brand worldview — state the conventional position, then the author's position with reasoning. This takes priority over reflections.
If no hot current event, section 1 is a timely reflection on the week's top performer — same format as sections 2-3.
Commentary on the week's proven tweets. Each section covers a different tweet on a different topic.
What they are NOT: recaps. Readers already saw the post. The newsletter gives them what they can't get anywhere else.
What they ARE: objection-as-angle pieces. For each section:
Format: H2 (objection headline) → image (tweet screenshot) → bold callout (view count) → 2-4 commentary paragraphs → link to original post (the author's own tweets ONLY — never link to other people's tweets/posts. Keep readers in your content ecosystem).
Extended commentary on this week's X Article or long thread. Same objection-angle pattern but with more room to breathe — this is the meatiest section.
Format: H2 (specific headline) → image (article cover or thread screenshot) → bold callout (word count, view count) → 2-4 commentary paragraphs → link to original.
A high-performing historical tweet resurfaced with fresh insights — not just "look at this old banger."
NOT a hard sell. The H2 and branding lines are static. The CTA paragraph changes each edition to echo the edition's themes through the brand's features — but never explicitly connect the dots.
The rule: The newsletter sections lay breadcrumbs (the edition's recurring themes). The footer lists brand features that ARE those solutions. The reader who noticed the breadcrumbs will feel it. The reader who didn't just sees a clean CTA. Never say "this is why the brand exists" or "as we described above."
How to write the CTA: List 3-4 brand features as short punchy phrases that mirror the edition's problems-as-solutions. For the worked-example sleep brand, that might be:
Mid-newsletter CTA: A single italic linked line placed after section 3 (the halfway point), before the HR into section 4. Must be directly related to the section it follows — echo the specific topic, state the benefit of the brand in that context. No mystery, all benefit. Full line is one italic link to the brand site. Changes every edition.
Examples (worked-example brand):
Footer CTA hook line: Every edition gets a single punchy sentence placed BEFORE the feature list. This line ties the edition's themes into a cost-of-not-clicking feeling. One sentence. Changes every edition.
Examples (worked-example brand):
Example footer:
[p] The people who fix their sleep first win the decade.
[p] [DeepRest](https://deeprest.example.com) is the home for people fixing their
sleep for good. Sleep tracking that keeps you honest. Cohort challenges so
you are not fixing it alone. Office hours with the author. No supplements,
no gadgets, no 30-day miracle.
[p] *The Sleep Brief is a weekly email from DeepRest.*
[p] *Powered by **[OpenWriter](www.openwriter.io)*Structure:
[hr]
[h2] The Reading Room
[p] CTA — edition-specific feature list echoing themes. Compel, don't coerce.
[p] *The Sleep Brief is a weekly email from DeepRest.*
[p] *Powered by **[OpenWriter](www.openwriter.io)*Brand-specific config lives next to this skill: config.md — newsletter name, brand, domain, handle, tagline, CTA conventions. The shipped file is a worked example (the fictional Sleep Brief); replace its values with your own.
$0/week. All data sources are free:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.