A Claude Code skill and subagent that write posts for this blog
Most of my recent posts start the same way: I debug something in a Claude Code
session, then ask it to “write this up”. Without guidance the result is
generic: wrong front matter, a made-up category, marketing intros, and
sometimes output that was never actually printed. So I gave the repo two
small files that teach Claude Code how this blog works: a skill
(blog-post) and a subagent (blog-writer). This post is how they are
set up. It was written with the skill.
The setup
- Claude Code 2.1.287, run from the blog repo root
- Hugo v0.123.8 extended, theme FixIt, 298 posts in
content/posts/ - Every post is a page bundle:
content/posts/<YYYY-MM-DD>-<slug>/index.md deploy.shbuilds with--buildDrafts, thengit add -A, commits and pushes both the source and thepublic/GitHub Pages repo
Everything lives in the project’s .claude/ folder, so it travels with the
repo and only applies when Claude Code is started in this directory:
|
|
Skill vs. agent: who does what
The two pieces do different jobs:
skill (blog-post) |
agent (blog-writer) |
|
|---|---|---|
| what it is | instructions + files, loaded into the current session | a separate Claude with its own context and system prompt |
| when it runs | Claude decides from the description, or /blog-post |
Claude delegates to it, or you ask for it by name |
| what it knows | the whole conversation so far | only what it is handed, plus the preloaded skill |
| tools | whatever the session has | restricted list in its front matter |
The skill holds the knowledge; the agent is a sandboxed worker that uses it. In practice I mostly use the skill directly, right after a debugging session, because the conversation already contains the real commands and output. The agent is for when I want the writing done in a separate context (it keeps the long post drafting out of the main session) or in the background.
Step 1: the skill
A skill is a folder under .claude/skills/<name>/ with a SKILL.md. Only the
front matter is always visible to Claude; the body is loaded when the skill
is triggered, and the other files are read only when the body says so.
.claude/skills/blog-post/SKILL.md front matter:
|
|
The description is what makes it trigger, so it lists the phrases I
actually use (“write”, “draft”, “blog about”, “turn this into a post”). The body is
a numbered workflow:
- Gather the material from the conversation, shell history, configs and
logs. Never invent output, benchmarks or versions; leave a
TODOinstead. - Decide language, title, slug, category, tags, with examples from real posts.
- Check for related posts with
ls content/posts | grep -i <keyword>and link them withrelref. If the new post corrects an old one, add a dated> **Update YYYY-MM-DD:**note to the old one. - Scaffold with the bundled script.
- Write the body, then fill
summary:. - Verify with a Hugo build.
- Report the path and outline. Don’t run
deploy.sh, commit or push.
Plus a front matter table, so fields like math and resources only get
set when they are actually needed.
The category rule came from real data
Step 2 says “use an existing category, don’t invent new ones”. That rule exists because of what years of hand-written front matter look like:
|
|
|
|
tool, Tools, toolsj and tools are all the same category. The skill
pins it to Linux, tools or programming and tells Claude to run that same
one-liner if unsure.
The style guide
references/style-guide.md is the longest file. It is distilled from the
existing posts and names four exemplars to read when unsure about tone (the
two tmux lag posts, the Claude Desktop install guide, and a short Chinese
note). It covers:
- Voice: first person, short sentences, no “In this comprehensive guide”, no emoji, include dead ends, bold one key fact per section.
- Structure per post type: debugging posts (setup → steps → why → fix →
results table → takeaways), how-to posts (error → why the obvious fix fails
→ steps → gotchas), short Chinese notes. Every post has a standalone
opening and then
<!--more-->, because that is the home page excerpt. - Code: commands and their output in separate blocks, output in
textblocks, real output only, trimmed with.... - Cross-references with
relref, and the FixIt shortcodes that exist. - Privacy: scrub hostnames, tokens, company names before finishing.
- Chinese posts: spaces between Chinese and English, full-width punctuation, technical terms left in English.
Keeping this in a separate file matters: SKILL.md stays short, and the
guide is only read when a post is actually being written.
The scaffolding script
scripts/new-post.sh writes the standard FixIt front matter, so Claude never
has to remember two dozen fields or get the timezone offset wrong:
|
|
A few details that matter:
-
It finds the repo root with
git rev-parse --show-toplevel, so it works from any directory. -
dateandlastmodcome fromdate +%Y-%m-%dT%H:%M:%S%:z, i.e. the real local offset. -
It normalises the slug the same way my old
Rakefiledid: spaces and colons (including the full-width:) become dashes, so Chinese slugs work. -
DRAFT=falsepublishes,DATE=YYYY-MM-DDbackdates, and it refuses to overwrite an existing post:1 2 3 4if [ -e "$file" ]; then echo "exists: $file" >&2 exit 1 fi
This post was created with it:
|
|
|
|
Remember to chmod +x the script, since Claude calls it directly.
Step 2: the agent
A subagent is a single Markdown file under .claude/agents/. The front
matter configures it; the body is its system prompt.
.claude/agents/blog-writer.md:
|
|
Three fields do the work:
descriptionis written for the main Claude, which decides when to delegate. It says what to hand over: the topic and the raw material. This is important because a subagent starts cold; it does not see the conversation that produced the material.toolsis an allowlist. NoAgent(it can’t spawn more agents), no web search, just enough to read, write and build.skills: [blog-post]preloads the skill, so the agent doesn’t have to discover it. The agent and the skill share one source of truth: if I change the style guide, both paths pick it up.
The body is short, because the skill already holds the workflow:
|
|
followed by the rules that need repeating for an agent working alone:
- Use only facts from the material given or verifiable on this machine. If
something is missing, write
TODO: <what is needed>and report it. - Don’t run anything destructive to “reproduce” an issue.
- Never run
deploy.sh,git commitorgit push. - Scrub secrets, internal hostnames and company names.
and a fixed report format: path, title, category/tags, a 3-line outline, any TODOs, and any older post it added an update note to.
Why “never deploy” is not optional
Look at deploy.sh again: it runs hugo --buildDrafts=true and then
git add -A and git push on both repos. So draft: true does not keep
anything private; it is a marker, not a gate. Anything in the working tree,
including a half-written post or a stray file, goes public the moment deploy
runs. The skill says so in its front matter table, and the agent has the rule
in its own prompt. Publishing stays my decision.
Using it
Both are picked up automatically when Claude Code starts in the repo; no
settings change is needed. /agents lists the agent and the skill shows up
in the skill list.
After a debugging session, the skill path:
|
|
Claude matches the request to the skill’s description, loads SKILL.md, and
follows the workflow with the real commands and output still in context.
/blog-post invokes it explicitly.
The agent path, when I want it out of the main context:
|
|
The main session passes the material along, the agent writes and builds the post, and returns the report.
Then I review the diff, edit, and run ./deploy.sh myself.
Verifying
The skill’s last step is a build into a throwaway directory:
|
|
No output means it built, and that every relref resolves; a broken
relref is a build error in Hugo, which makes this a cheap link checker.
Takeaways
- Put the knowledge in a skill, and the isolation in an agent. The agent’s prompt is mostly “follow the skill”, so there is one place to edit.
- Write descriptions for the model that reads them. The skill’s description lists trigger phrases; the agent’s tells the caller what material to hand over, because the agent can’t see the conversation.
- Derive the style guide from your own posts, not from general writing advice. Naming concrete exemplar posts did more than any list of rules.
- Move anything mechanical into a script. Front matter, dates and slug normalisation are exactly where a model drifts; a 60-line shell script doesn’t.
- Guard the irreversible step. Here that is
deploy.sh, which publishes everything including drafts. Both files forbid running it.
References
相关内容
- Installing Claude Desktop on Ubuntu 20.04 / Mint 20 (glibc 2.31)
- 使用hugo+github搭建博客
- 那些年我追的博客们
- 使用 github 发布 gitbook 电子书
支付宝
微信

william