alama@world: ~/
./toggle-theme
cat blog/tool-draft-blog-post.md

draft-blog-post: the skill that turns my Obsidian vault into blog posts

· post · high energy

A Claude Code skill I built in a week that reads four Obsidian vaults, respects a privacy gate and employer-redaction pass, and writes ready-to-review Markdown drafts. Here is what it does, why it exists, and what I learned building it.

What it is

draft-blog-post is a Claude Code project skill. It lives at .claude/skills/draft-blog-post/SKILL.md in this repo and gets auto-discovered whenever I open a session here. Under the hood it is a single Markdown file — frontmatter, a set of shared rules, and one section per subcommand.

Eleven subcommands cover the full cadence: monthly digest, weekly rollup, quick-insight from a single clipping, tag-scoped rollup, multi-part series from a Map-of-Content, book-note, entity post, side-by-side compare, plus deterministic publish, unpublish, and audit operations. Every drafting subcommand reads from four Obsidian vaults — a personal daily-notes vault, an AI-curated knowledge vault, a saved-conversations vault, and a graph of entities, MOCs, and clippings — then writes a ready-to-review Markdown file to source/_drafts/.

No new PHP runtime dependencies. No external CLIs. No cron job. Just a SKILL.md file and Claude Code's native skill loader.

Why it exists

Cadence target: two to four posts a month. That is not enough volume to justify a CMS, but too much to hand-write from scratch every time. The bottleneck was never "what do I have to say" — by the time I started the pipeline I had 690 clippings in the vault, 23 MOCs, and a running graph of entities. The bottleneck was translating vault syntax into publishable Markdown, and — more importantly — remembering not to leak an employer name I had agreed not to publish, or a personal note I had marked private.

A skill sits in the exact space where a script would over-engineer and a bare prompt would drift. The skill file is the prompt, versioned in the repo, code-reviewable. It reminds me of the guardrails every time I invoke it: the privacy gate for private: true, the redaction list of past employers, the frontmatter shape, the transform table for Obsidian-only syntax. I do not need to remember. The file does.

How it shows up in practice

The interesting parts are not the drafting subcommands — those are prompt scaffolding, and any competent writer would build something similar. The interesting parts are the deterministic ops.

publish is a shell operation, not a Claude judgment call. It verifies the draft exists, verifies the blog file does not, re-scans for employer names without an allow_employer allowlist entry, flips published: true in frontmatter, moves the file from _drafts/ to _blog/, and reports the URL. That matters. Publishing is the one place where I want a machine that stops and refuses, not a machine that tries to be helpful.

audit is the same shape — pure grep and find, never mutates a file. It reports orphan drafts, broken book cross-links, dangling series parts, and employer name leaks. It is the closest thing this blog has to CI, and it runs whenever I ask it to.

The Obsidian transform rules — wikilinks, embeds, callouts, dataview blocks, tag inlining — live in one table at the top of the skill file. Any post-processing bug is one edit away from being fixed for every future post. That is the trick: put the rules where the LLM will read them every single time.

My take

The skill outperformed my expectations on one axis and underperformed on another.

Overperformed: as a forcing function for editorial discipline. Every time I want to publish, the redaction pass reminds me who I have worked for and what I have agreed not to name. That is better memory than mine.

Underperformed: the drafting itself. Even with 690 clippings and a well-tended MOC, the first draft is still a first draft. The skill does not make me a better writer. It removes the friction between having something to say and having a file on disk. That was the point.

The skill file is the prompt. The prompt is the contract. The contract survives the model change.