Building holas.pl with AI — Claude Code, MCP, and Local Image Generation
ai,symfony,php,static-sitePart 6 of 7
- 19 Years of WordPress — Why I Finally Quit
- Symfony as a Static Site Generator — How holas.pl Works
- Securing a Static Site's Only Dynamic Endpoint — The Contact Form
- Building a Tool Decision Tree for Claude Code with Global Memory
- Developer Experience — From Local Dev to Production in Two Containers
- Building holas.pl with AI — Claude Code, MCP, and Local Image Generation
- 4×100 on Lighthouse Mobile — What a Static Site Actually Gets You
This is part 5 of a series on migrating holas.pl from WordPress to a custom Symfony-based static site generator. Part 4 covers the developer experience and deployment.
Building holas.pl involved writing a fair amount of PHP, Twig, and SCSS — and making architectural decisions that would be annoying to undo later. I used Claude Code as an AI pair programmer throughout. This post covers what that workflow actually looks like, where it works well, and where it still needs human judgment.
AGENTS.md — The AI's Instruction Manual
The first practical insight from this project: an AI assistant is only as good as the instructions it's given. Without explicit guidance, Claude defaults to generating functional but generic code — reasonable choices, but not necessarily the choices you'd make yourself.
The solution is AGENTS.md, a file in the project root that Claude Code reads at the start of every session. It documents:
- Architecture rules — final classes only, no inheritance, interface segregation, value objects over associative arrays
- Code style — strict types on every file, Yoda conditions, blank line before return, PSR-4 namespace convention
- Naming conventions — interface naming (
ContentServiceInterface→ContentService), readonly value objects, constructor property promotion - DDEV commands — how to start the environment, run builds, check code quality
- Content structure — where Markdown files live, how frontmatter works, what the URL slug conventions are
With this context, Claude generates code that follows the project's actual conventions. Reviewing a generated class feels like reviewing a pull request from a teammate who has read the style guide — not reviewing output that needs to be translated into project conventions.
EDITOR_GUIDE.md — Delegating Content Creation
The same principle applies to content. EDITOR_GUIDE.md documents:
- Title format and length (under 70 characters, technology names included, no clickbait)
- Description format (120–160 characters, no "In this post...", lead with reader benefit)
- Intro structure (2–3 sentences, no greeting, problem first)
- Body conventions (code blocks for all commands, short paragraphs, numbered steps for procedures)
- EN/PL parity requirements (both versions equal depth, same code blocks)
- Frontmatter fields and their formats
With this guide, the content creation workflow becomes:
- Write the post in rough form — the ideas, the code snippets, the structure
- Hand it to Claude with: "proofread this, correct the English, translate to Polish, and produce the two
.mdfiles with correct frontmatter" - Review the result
The guide is specific enough that Claude doesn't need to ask clarifying questions. Title format, slug convention, category values, tag format, image path pattern — it's all documented. The output is ready to commit.
Image Generation with Draw Things and MCP
Every post needs a featured image (minimum 1200×630px). For holas.pl, images are generated locally using Draw Things via the MCP (Model Context Protocol) integration in Claude Code.
The workflow:
- Claude Code calls
mcp__draw-things__generate_imagewith a prompt describing the image, 4 parallel variants at 1024×576px withsteps=4 - The best variant is selected from
.generated/YYYY-MM-DD-session-name/ - ImageMagick upscales it to production size:
ddev exec convert .generated/session/image.jpg \
-resize 1920x1080! -filter Lanczos -quality 92 \
assets/images/output.jpg
- The image is moved to
content/blog/category/post-name/files/and referenced in frontmatter
The .generated/ directory is gitignored — it holds expendable previews. Only approved images committed to files/ become production assets.
MCP (Model Context Protocol) is what makes this work: it's a standard interface for connecting AI assistants to external tools. Claude Code connects to Draw Things running locally, Hugging Face Spaces, and other services without leaving the development session. The image generation happens on the local machine — no API quota, no external service, no per-image cost.
What AI Does Well
Boilerplate and patterns — generating a new service with its interface, value object, and correct import ordering is instant. The structure is consistent with the rest of the codebase because the conventions are documented.
SCSS from description — describing a component layout in words and getting working SCSS back, with the project's variable names, is faster than writing it from scratch.
Translation — Polish and English content at equal quality. The editorial guide's specificity about what "equal quality" means (same code blocks, same depth, not a summary) produces translations that don't need significant editing.
Repetitive structured work — generating nginx redirect lists, updating frontmatter across multiple files, writing sitemap entries — tasks with clear rules but many instances.
Staying in context — Claude Code reads the project files, understands the existing patterns, and generates code that fits without being told what every class does.
Where Human Judgment Is Still Required
Architecture decisions — which abstractions to introduce, when a value object is warranted, how to structure the content pipeline — these require understanding the trade-offs in a way that goes beyond pattern matching. The AI generates plausible options; the decision is still human.
Design aesthetics — SCSS variables can be generated, but deciding whether the color palette looks right on a dark terminal-style background requires eyes and taste.
Content voice — the editorial guide captures tone conventions, but the actual ideas — what's worth writing about, what angle is interesting — come from experience, not from a prompt.
Reviewing AI output — the code and content still need to be read. AI-generated code is plausible-looking; it takes a developer to notice when something is technically correct but architecturally wrong.
The Practical Result
The entire site — architecture, frontend, content pipeline, contact form, deployment scripts, this blog post series — was built with Claude Code. The time investment in AGENTS.md and EDITOR_GUIDE.md paid back immediately: less time correcting style, less time explaining the same conventions session after session, more time on the actual work.
The most surprising benefit was content. Writing a post in rough Polish, having it proofread, translated to English, and converted to two properly-structured .md files with correct frontmatter in one step — that removes enough friction that publishing actually happens.
The architecture built this way doesn't just produce maintainable code — it produces measurable results. The next post looks at the Lighthouse scores: what drives each of the four metrics and why most of it follows from the architecture, not from optimisation work.