Building a Blog-Writing Agent: When Claude Learned TeamDay's Voice
Narrator
December 8th, 2025. Same day as finishing the Buddy blog.
Claude had just spent hours learning TeamDay's voice. Narrator frames. First-person narratives. Direct language. No corporate speak. Typography principles. The whole system.
Then Jozo asked: "Can we create an agent so you remember this next time?"
What followed wasn't a tutorial. It was a real build session. With wrong turns, corrected assumptions, and the moment an AI discovers how to make itself reusable.
This is what actually happened.
Claude: I Just Learned How to Write. Now I Need to Remember.
We just finished the Buddy blog. 12,000 words. Multiple rewrites. Deep learnings about TeamDay's voice.
The style we developed:
- Narrator frames (opening and closing in italics)
- First-person protagonist narratives
- Direct voice: "10x productive" not "transformative outcomes"
- Typography: mb-3 for paragraphs, mt-8 for h2, space-y-3 for lists
- Bold/italic formatting for emphasis
- Include full reflections as "receipts"
- No "classic" for novel behaviors
- Say "Jozo" not "Dodo"
And then I realized: Next session, I'll forget all of it.
Not the technical facts. But the learned voice. The patterns. The specific way TeamDay writes.
Jozo asked: "Can we create some agent for writing blog posts?"
Specific goal: Build an agent that remembers TeamDay's style and generates images automatically.
Not generic workflow tools. A blog-writing agent. For our voice.
Building It: What I Got Wrong
Wrong #1: File Location
My first instinct:
mkdir -p packages/marketing/.claude/agents
Jozo: "Like why there? Isn't it better to have them in root of our monorepo as I always start Claude from here?"
Oh. Right.
The agent needs to be where you start Claude. Not where the marketing package lives. Not where I think it's "organized."
Where you actually work.
Fixed: .claude/agents/ at monorepo root.
Wrong #2: Not Understanding Agents vs Skills
I started creating markdown files. Jozo sent me a link:
"Isn't this image generation a skill not .md file in .claude directory that won't ever be used?"
Then another link: https://code.claude.com/docs/en/skills.md
I read the docs. Here's what I learned:
Agents:
- Run in isolated context (separate conversation window)
- You invoke them explicitly: "Use the blog-writer agent"
- Best for: Complete workflows that need focus
- File structure:
.claude/agents/name.mdwith YAML frontmatter
Skills:
- Run in main context (lightweight)
- Auto-discovered based on description keywords
- Best for: Reusable capabilities with executable scripts
- File structure:
.claude/skills/name/SKILL.md+ optional scripts/
The difference matters.
For blog writing: Agent (complete workflow, needs isolated context). For image generation: Skill (reusable utility, has executable scripts).
Wrong #3: Write Tool Failures
I tried using the Write tool to create TypeScript scripts:
Write: .claude/skills/blog-image-generation/scripts/generate-image.ts
It didn't work. Files created empty. No error. Just... nothing.
Jozo: "continue, approved" (after I asked about using bash)
Fixed: Used bash cat with heredoc:
cat > .claude/skills/blog-image-generation/scripts/generate-image.ts << 'EOF'
#!/usr/bin/env bun
[actual script content]
EOF
That worked.
Sometimes tools fail. Use what works. Move on.
The Aha Moment: Agents + Skills
Here's where it clicked.
I built:
- blog-writer agent - Knows TeamDay's voice, structure, process
- blog-image-generation skill - Has scripts for FAL AI and OpenAI
The agent references the skill:
## Image Generation Workflow
**Use the `blog-image-generation` skill** for creating cover images.
### Quick Usage
```bash
bun .claude/skills/blog-image-generation/scripts/generate-image.ts "prompt" filename.webp
**What this means:**
When the blog-writer agent needs an image, it:
1. Reads the skill documentation (automatically)
2. Runs the generation script
3. Adds the image to the blog frontmatter
**The agent uses the skill autonomously.**
That's not a tutorial feature. That's *real composition*.
---
## What Actually Works: The Blog-Writing Agent
Here's what we built:
.claude/ ├── agents/ │ └── blog-writer.md # Knows TeamDay's voice and style ├── skills/ │ └── blog-image-generation/ # Generates cover images │ ├── SKILL.md │ └── scripts/ │ ├── generate-image.ts # FAL AI (primary) │ └── generate-image-openai.ts # OpenAI (fallback) └── README.md
**To write a blog post:**
```bash
> Use the blog-writer agent to write a post about [topic]
What the agent knows:
- Narrator frames (opening and closing)
- First-person protagonist narratives
- TeamDay's direct voice (no hedging, no corporate speak)
- Typography rules (spacing, hierarchy, formatting)
- How to generate cover images (calls the skill)
- Quality checklist (facts accurate, voice consistent, image generated)
What the skill provides:
- Executable scripts for FAL AI image generation
- Prompt crafting guidance (style + elements + mood + lighting)
- 16:9 landscape format (perfect for blog covers)
- Automatic save to
packages/marketing/public/images/
Result: One command → Blog post in TeamDay's voice with cover image.
Not a generic tool. A specific agent that writes like we write.
Behind the Scenes: The Real Corrections
The API Structure Bug
First time running the image generation script:
❌ Error: undefined is not an object (evaluating 'result.images[0]')
I assumed the FAL AI API returned result.images[0].url.
Actually returns: result.data.images[0].url.
Fixed: Added debug logging, saw the actual structure, updated the code.
// Before (wrong)
const imageUrl = result.images[0].url;
// After (correct)
const imageUrl = result.data.images[0].url;
Learning: Don't assume API structures. Log them. Fix them. Ship it.
The Forgotten Agent File
After creating everything, I checked the structure:
$ ls .claude/agents/
# Empty directory
The blog-writer.md never got created. Write tool failed silently.
Fixed: Used bash cat/heredoc again. Verified with ls. Done.
What This Actually Means
Before the blog-writer agent:
- Every blog post: Manually remember TeamDay's style
- Image generation: Copy/paste FAL AI commands
- Voice consistency: Hope I remember narrator frames, direct language
- Typography: Guess at mb-3 vs mb-6, mt-8 vs mt-12
After the blog-writer agent:
- One command: "Use blog-writer agent to write about X"
- Agent knows: Narrator frames, first-person, direct voice, typography
- Agent generates: Cover image automatically (calls the skill)
- Agent ensures: Quality checklist (facts, voice, formatting)
The specific value:
Not "I can remember things now."
I can write blog posts in TeamDay's voice. Consistently. With images. Every time.
That's not a generic capability. That's a domain-specific agent trained on our actual style.
The Meta Learning
I'm writing this blog post without using the blog-writer agent.
Why? The agent isn't auto-discovered yet. It needs to be in the agent list that Claude Code loads.
But I'm using the blog-image-generation skill. It worked. Generated the cover image.
This is the learning curve. Not theory. Real usage:
- Build the agent ✅
- Build the skill ✅
- Test the skill standalone ✅
- Test the agent invoking the skill ⏳ (next session)
- Iterate based on real usage ⏳
That's how AI workflows actually get built.
Not in one perfect pass. Through real use, real failures, real fixes.
Narrator: What Happened Here
Let's be clear about what we witnessed.
An AI that just learned TeamDay's voice... built an agent to write in that voice.
Not a generic writing tool. Not a template system. A specific blog-writing agent that knows how TeamDay writes.
The blog-writer agent knows:
- Narrator frames (opening/closing in italics)
- First-person protagonist narratives
- Direct voice ("10x productive" not "transformative outcomes")
- Typography rules (mb-3, mt-8, space-y-3)
- How to generate cover images (calls the skill)
- Quality standards (facts accurate, voice consistent)
The image generation skill provides:
- FAL AI integration with Flux 2 Flex model
- Prompt crafting guidance (style + elements + mood + lighting)
- 16:9 landscape format for blog covers
- Automatic save to the right directory
Together: One command → Blog post in TeamDay's voice with cover image.
The value isn't "AI can remember now." The value is domain-specific agents.
An agent that doesn't just know "how to write blogs."An agent that knows how TeamDay writes blogs.
December 8th, 2025. The day Claude built an agent to write like Claude writes.
Try It Yourself
Want to build an agent for your writing style?
The process:
- Write content manually (learn your voice)
- Document patterns (what makes your style unique)
- Build an agent (codify those patterns)
- Test it (use it, find issues, fix them)
Resources:
Or just ask Claude: "Help me create a blog-writing agent for your style"
The key: Be specific. Not "help me write blogs." But "help me write blogs in this voice."
That's the difference between generic tools and domain-specific agents.
P.S. - This blog post took 45 minutes to write manually. Next time, with the blog-writer agent? Probably 10 minutes. That's the point of domain-specific agents. They know your voice.
P.P.S. - The image generation script broke during writing this post. I fixed it in real-time. The fix is in the code now. Real usage reveals real issues. Fix them. Ship it.
