Skip to content
Content
Skill

/blog-writer

Channel-master writer for long-form blog posts. Owns the SHAPE of a post — beat structure, title/preview/slug commitments, per-post container layout, per-site voice anchor discovery — and delegates VOICE (prose generation) to /authors-voice and PUBLISH mechanics to the

From plugin
openwriter
288 skills2 agents
Install
$ npx -y skills add travsteward/openwriter --skill blog-writer --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/blog-writer

Context preview

The summary Claude sees to decide when to auto-load this skill.

Channel-master writer for long-form blog posts. Owns the SHAPE of a post — beat structure, title/preview/slug commitments, per-post container layout, per-site voice anchor discovery — and delegates VOICE (prose generation) to /authors-voice and PUBLISH mechanics to the

SKILL.md

blog-writer.SKILL.md
name: blog-writer
description: |
  Channel-master writer for long-form blog posts. Owns the SHAPE of a post —
  beat structure, title/preview/slug commitments, per-post container layout,
  per-site voice anchor discovery — and delegates VOICE (prose generation) to
  /authors-voice and PUBLISH mechanics to the openwriter github plugin
  (`add_blog_site` + `post_to_blog`).

  Use when: "/blog-writer", "write a blog post", "blog draft", "brainstorm blog
  topics", "blog beats", "extract beats from this post", "write about this
  feature", "draft a post", "blog title", "preview text", "OG description",
  "blog image", "featured image", "OG image", "integrate", "create the files",
  "wire up the blog post", "publish to blog", "post to blog", "set up blog repo",
  "register blog site".

  Requires: OpenWriter MCP server configured + github plugin enabled +
  `gh auth login` set up locally. Project SHOULD have a `## Blog` section in its
  CLAUDE.md for writing-rules / image-style overrides (optional after setup).
metadata:
  author: travsteward
  version: "0.5.0"
license: MIT

Blog Writer

Channel-master skill for long-form blog content. Owns ideation → beats → draft → image → publish.

**Architecture:** beats-first (v0.5.0) + plugin-backed publish (v0.4.0). Each post lives in its own container with two sibling docs: a `Beats` doc (the structural commitments — beat list + title/preview/slug as B0) and a `Draft` doc (the voice-poured prose). Beats reshape regularly; draft re-pours via `/authors-voice` against a per-site anchor (`voice/anchor-<site-slug>.md`). Publish is a single `post_to_blog` MCP call against a site registered via `add_blog_site` — site-specific frontmatter (layout, author, prerender, `date → publishedDate` for Astro, etc.) lives on the github plugin's per-site config in `~/.openwriter/config.json`, not in project config. Mirrors book-writer's discipline at post scale.

Convention

This skill obeys the shared writer contract at [WRITER-CONVENTION.md](../WRITER-CONVENTION.md). Brief shape and return shape match that doc. Sub-form values: `long` | `short` | `tutorial` | `announcement`.

**OpenWriter pad mechanics are canonical in [/openwriter](../openwriter/SKILL.md) — read it, don't re-derive from tool descriptions.** The load-bearing rule: `populate_document` is **create-only, used ONCE**; it re-sends the whole body, so calling it again to "fix" a doc appends a duplicate. All edits (rewrite / insert / delete) go through `write_to_pad` on fresh node IDs. Plus the read ladder (`outline_doc` → `search_docs` → `peek_doc` → `read_pad`).

**Blog posts ship SEO-complete in the FIRST pass.** A blog post almost always carries SEO — internal/cluster links, meta + slug + tags, snippet-targeted headings, FAQ/schema-eligible content all go into the *initial* draft (the minion brief + first `populate_document`), never bolted on after. Bolting SEO on later forces an edit (→ `write_to_pad`, never a 2nd populate). SEO-strategy-led posts (pillar / landing / comparison) front-end through `/seo-writer` if you have it installed (not bundled with OpenWriter).

**Cover image is a publish gate** (firm rule 12). Placement rules: no bottom-band text (the platform renders og:title there), keep the subject center-safe, ≤5–7 words of overlay text, do not echo the title/description. Palette / brand skin comes from the project's `style_doc` (e.g. `recipebox.md`). Generation, canonical size, and the tmp-first rule for text-overlay covers: [docs/images.md](docs/images.md).

**ABOUT TO HAND-EDIT A PUBLISHED POST'S `.md`, ITS `image:` FRONTMATTER, OR DROP A COVER INTO THE REPO'S `public/`? STOP.** The OpenWriter doc's `blogContext.coverImage` is the single source of truth, and `post_to_blog` is the ONLY writer to the blog repo. Edits flow **doc → Accept → Publish** — never doc *and* repo in parallel, because that drift is exactly what breaks idempotent republish (Publish rewrites `<content_dir>/<slug>.md` wholesale and your hand-edits vanish). Never hand-name a cover; never side-channel an image; never `git push` the post yourself. Canonical publish flow + the `lastPublish`-clobber footgun: [docs/integrate.md](docs/integrate.md).

Modes

| Mode | Trigger | What it does | Sub-doc | |---|---|---|---| | `setup` | `/blog-writer setup`, "register blog repo", "add blog site" | One-time per blog: `inspect_blog_repo` clones the target, auto-proposes `frontmatter_defaults` + `frontmatter_field_map` from existing posts; `add_blog_site` persists the site config | [docs/setup.md](docs/setup.md) | | `brainstorm` | `/blog-writer brainstorm`, "brainstorm blog topics" | Open a Blog Ideas doc; propose 3-5 candidate angles with tone + length labels; hand off to `beats` when user picks | [docs/brainstorm.md](docs/brainstorm.md) | | `beats` | `/blog-writer beats`, "extract beats", "blog beats" | Query-first beat extraction with 3-pass (short/announcement, 3-5 beats) or 5-pass (long/tutorial, 8-15 beats); 9 blog category tags (CLAIM/REFRAME/MECHANISM/EVIDENCE/DEMO/SCENE/OBJECTION/APHORISM/PIVOT) + HOOK/CTA positional roles; locks title + preview + slug as B0; dopamine arc layered with conversion arc | [docs/beats.md](docs/beats.md) + [docs/titling.md](docs/titling.md) | | `draft` | `/blog-writer draft`, "draft this post", "pour the beats" | Per-beat dispatch to `/authors-voice` Apply Protocol with site-specific anchor; cross-beat coherence pass; supports beat-level reshape loop (re-pour only the affected beats) | [docs/draft.md](docs/draft.md) + [docs/voice-anchor.md](docs/voice-anchor.md) | | `images` | `/blog-writer images`, "blog image", "featured image" | Read style doc, craft prompt, generate cover via `insert_image` (no docId → returns path → `blogContext.coverImage`); optional inline images at `afterNodeId` | [docs/images.md](docs/images.md) | | `integrate` | `/blog-writer integrate`, "publish", "post to blog" | `post_to_blog` against the registered site — builds frontmatter from `blogContext` + site defaults,

Read more
Ships withopenwriter

Your agent writes. You decide. The open-source writing surface for the agentic era. OpenWriter is a markdown-native rich text editor built for humans and agents working side by side. Your agent writes, and you review and approve every change before it lands.

Get the whole plugin
Stats
28
Stars
1
Forks
Active
Maintenance
TypeScript
Language
MIT
License
7d ago
Last commit
7mo ago
Created

Repo: travsteward/openwriter

Other skills on openwriter.