Skip to main content

newsletter-writer

Channel-master writer for email newsletter. Weekly pipeline: scaffold structure, gather data, query the author, synthesize in their voice, review, send. Companion doc workflow. Voice-matched via the authors-voice skill. Works for any behind-the-scenes weekly newsletter built on proven content. Use when: "/newsletter-writer", "newsletter", "draft newsletter", "scaffold newsletter", "newsletter gather", "newsletter review", "newsletter send", "newsletter status", "newsletter to blog", "convert newsletter". Requires: OpenWriter MCP server configured.

跳到安装

来源信息

仓库
travsteward/openwriter
最近来源活动
2026年6月10日 18:46
检测到的 SKILL.md 语言
英语
星标
27
分支
2

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
3 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
newsletter-writer
description
Channel-master writer for email newsletter. Weekly pipeline: scaffold structure, gather data, query the author, synthesize in their voice, review, send. Companion doc workflow. Voice-matched via the authors-voice skill. Works for any behind-the-scenes weekly newsletter built on proven content. Use when: "/newsletter-writer", "newsletter", "draft newsletter", "scaffold newsletter", "newsletter gather", "newsletter review", "newsletter send", "newsletter status", "newsletter to blog", "convert newsletter". Requires: OpenWriter MCP server configured.
metadata
{"author":"travsteward","version":"0.2.0"}
license
MIT
# Newsletter Writer Skill — /newsletter-writer 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). ## Philosophy 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." ## Commands - `/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` — Send via OpenWriter newsletter system - `/newsletter status` — Subscriber count, last sent, domain status ## Critical Rules 1. **ALWAYS use the authors-voice protocol.** Load the voice profile/anchor, pull writing samples from the corpus, write with the profile as behavioral constraints, run anti-AI detection. Never skip this. The authors-voice skill ships with the OpenWriter authors-voice plugin. 2. **NEVER write the author's position for them.** You gather data and present structure. You ASK the author what they think. They tell you. You synthesize their input in their voice. The author's original thinking is the product — you are the editor, not the writer. 3. **Source article content from OpenWriter docs FIRST.** X Articles may not be readable via fxtwitter. The drafts live in OpenWriter. Search `list_documents` before trying external APIs. 4. **Every section headline is specific to that week's content.** No generic recurring labels like "This Week on X" or "The Author's Position." Each section gets a unique, attention-grabbing headline based on the actual topic. Example: "Why the 8-Hour Rule Is Wrong for Shift Workers" not "The Author's Position." "The Tweet That Got 1.75M Views About Sleep Debt" not "This Week on X." Specificity is what grabs attention. 5. **No outbound links except the footer CTA.** The newsletter summarizes and screenshots everything — readers don't need to leave. No "View the post →" or "Read the full article →" links to X or anywhere else. The only link in the entire newsletter is the footer CTA to your brand site. Every other link is a leak. ## Phase 1: Scaffold 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. ### Determine Issue Number Check `list_newsletter_issues` for the latest sent issue number. Increment by 1. ### Create & Populate Skeleton ``` 1. create_document({ title: "The Sleep Brief #N", content_type: "newsletter" }) 2. populate_document with the full skeleton below ``` Use a single `populate_document` call with this TipTap JSON structure. All section titles are placeholders — they get replaced during drafting after gather/query: ```json [ {"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. ### Workspace Ordering 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 ← oldest ``` When 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. ### Standalone `/newsletter scaffold` When run standalone (not part of the full pipeline), just scaffold and stop. The author reviews the bones in the editor. ## Phase 2: Gather Collect all data before presenting anything to the author. ### Tweet Metrics 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 views - `threshold_crossings[]` — tweets that crossed 10K/25K/50K/100K this week If 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). ### Archive Candidates (From the Archives section) If a historical metrics database exists, search it directly for thematically relevant older posts: ```javascript // 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: - Build the regex from the edition's themes to find thematically relevant archive tweets - Prefer the oldest entries — they're the founding ideas. The point of an archives section is resurfacing forgotten ideas, not recapping last month - If no database exists, ask the author to nominate an old favorite ### Top Tweet Content 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. ### Tweet Screenshots 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 before inserting: ```bash cp <screenshot-output-dir>/{tweet_id}.png ~/.openwriter/profiles/Default/_images/{descriptive-name}.png ``` Insert into the newsletter doc via `write_to_pad` using TipTap JSON: ```json {"type": "image", "attrs": {"src": "/_images/{descriptive-name}.png", "alt": "description"}} ``` #### Screenshot Logic: Original vs QT For each tweet section, determine what to screenshot: 1. **Quote tweets** → screenshot the ORIGINAL tweet (the one being quoted). Use fxtwitter to trace the quoted tweet ID. Then blockquote the author's punchiest lines from their QT below the image. This is the proven pattern: ``` [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 post ``` The 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. 2. **If the original fails** (deleted, protected, the screenshot service can't fetch it) → screenshot the author's FULL QUOTE TWEET instead. The QT card embeds the original tweet visually. But then **trim the redundant written text** — delete blockquotes of what was said and what the original said, since the screenshot already shows both. Keep only the author's extended thoughts paragraph. 3. **Standalone tweets** (not QTs) → screenshot the tweet directly. 4. **Archive tweets** → screenshot the archive tweet directly. Delete the blockquote copy of the tweet text — the screenshot replaces it. Keep only the context paragraphs. #### Naming Convention Use descriptive names, not tweet IDs: `caffeine-study.png`, `sleepauthor-90min-qt.png`, `sleepauthor-naps.png`. ### Article Content Search OpenWriter for this week's article: ``` mcp__openwriter__list_documents → find by date + title mcp__openwriter__read_pad → read full content ``` Articles are always in OpenWriter. Do NOT rely on fxtwitter for long-form content. ### Current Events Context (optional) 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. ## Phase 3: Present & Query Present your findings, then ask questions. **Do not write any content yet.** ### Present the Data Show the author: 1. **Top tweets this week** — text, views, bookmarks, likes for each 2. **The article** — title, word count, brief description 3. **Archive candidates** — 2-3 options for From the Archives 4. **Metrics summary** — total views, follower count, top performer ### Ask These Questions (in order) **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**
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看