write-content
Create content of any configured type from a topic or from the content backlog. Orchestrates research, writing, style review, glossary creation, linking, and reindexing as focused sub-agents. Hooks handle tracker updates and build verification automatically.
Unified content creation skill. Orchestrates focused sub-agents for each phase. Reindex runs in Phase 9 before commit. Hooks handle tracker updates and build verification after commits.
Arguments: $ARGUMENTS
Phase 1: Parse Arguments
Determine content type, source, and mode from $ARGUMENTS.
First, read the content_types keys from .content-ops/config.md to get the list of valid content types. Then match the first word of $ARGUMENTS against those keys:
| Argument | Content Type | Source | Mode |
|---|---|---|---|
"Docker Containers" | ask user (list all types) | user prompt | interactive |
article "Docker Containers" | article | user prompt | interactive |
glossary "container, image" | glossary | user prompt | interactive |
case-study "Project X" | case-study | user prompt | interactive |
backlog 3 | from backlog | backlog | autonomous |
| (empty) | ask user (list all types) | ask user | interactive |
Rules:
- If the first word matches a key in
content_types→ that is the content type; the rest is the topic or terms. - If the first word is
backlog→ type comes from backlog entries, rest is the selection (count,#ids,all,first N,last N). If rest is empty, treat asall. - If the first word does not match any configured type and is not
backlog→ treat the entire argument as a topic and ask the user which content type to use, listing all configured types. - If arguments are empty → ask the user what they want to create, listing all configured content types.
Source determines mode automatically:
source: user prompt→ interactive mode — ask 3-5 interview questions before writingsource: backlog→ autonomous mode — context comes from the backlog entry, no interview
Phase 2: Load Context
Read .content-ops/config.md in this phase. Extract and hold these values — they are used throughout all phases and passed explicitly to each subagent that needs them:
author,content_types,content_strategy,content_pillars_path,backlog_file,translation_tracker_file,reference_content,languages,default_language,source_hierarchy,research_cache_path,research_cache_ttl_days,content_index_path,linking_max_candidates,linking_max_links,image_generation
Also extract and hold:
- The
glossaryconfig block (if present) — needed for Phase 7/8 conditional logic. Key fields:glossary.enabled,glossary.auto_stubs,glossary.content_type,glossary.bidirectional_linking. - The
guidelinesvalue for the relevant content type. Note thatguidelinesis a list of paths per content type (not a single path).
Also read (always):
- The backlog file (
backlog_file) if mode is autonomous
Also read (non-glossary content types):
- The content strategy (
content_strategy) — understand topic context - If
content_pillars_pathis configured: find the matching pillar file for this topic
SEO integration check (at end of Phase 2):
If .content-seo/config.md exists:
Set seo_enabled = true
Check env: DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD both set?
If both set → set seo_brief_available = true
Else → set seo_brief_available = false (continue without SEO brief, no hard stop)
Else:
Set seo_enabled = false
Set seo_brief_available = false
Phase 2.5: SEO Brief (conditional)
Skip this phase entirely if any of the following are true:
seo_enabledis falseseo_brief_availableis false- The content type is the glossary type (i.e., matches
glossary.content_typein config)
If all conditions pass:
- Read
.content-seo/config.mdto getseo_rulesfor this content type. - Spawn the
keyword-researcheragent via Task with a focused prompt:
Use the keyword-researcher agent.
Keyword: [topic — proposed primary keyword based on title/topic]
Locale: [default_language from content-ops config]
Audience: [summary from content_strategy]
Pillars context: [pillar names from content_pillars_path, comma-separated, or "not configured"]
Cache path: .content-seo/keyword-cache/
Cache TTL days: [research_cache_ttl_days from content-ops config]
Check the cache first. Only call DataForSEO for cache misses or stale entries.
Return: primary keyword, volume, related keywords (top 5 with volumes), content gaps (3 subtopics).
- Store
seo_brief = { keyword, volume, related_keywords, content_gaps }. - In autonomous/backlog mode: auto-confirm the best keyword returned by the agent. Do not ask the user.
- In interactive mode: Show the proposed keyword and ask for confirmation before proceeding.
Phase 3: Plan
Behavior depends on mode:
Interactive mode (single article or glossary)
For articles: Ask the user 3-5 quick questions using a single AskUserQuestion call:
- Audience level — Beginners or readers who know the basics?
- Key angle — Main perspective or focus?
- Must-cover points — Specific aspects, examples, or facts to include?
- Exclusions — Anything to leave out?
For glossary: Check for duplicates using Glob on the glossary content path from config:
- If a term already exists: show it and ask to update or skip.
- For terms created as part of another workflow: skip duplicates silently.
Autonomous mode (backlog)
- Parse the selection argument:
allor (empty) → all pending articles in order3orfirst 3→ first 3 pending articleslast 2→ last 2 pending articles#1,#3,#5→ specific articles by their#number in the backlog
- List selected articles in order with backlog numbers
- For each article, find matching topic in the content strategy / pillar files and note context
- Present the batch plan and confirm with the user before proceeding. After confirmation, work autonomously through all phases.
Phase 4: Research
Spawn the content-researcher agent via the Task tool with a focused prompt. Pass only what the agent needs:
For articles:
Use the content-researcher agent.
Topic: [topic]
Task: Research key facts — dates, numbers, technical details, historical context.
Config:
source_hierarchy=[from config, or "not configured"]
research_cache_ttl_days=[from config]
research_cache_path=[from config]
Check the research cache first. Only do web searches for uncached or stale topics.
Return a structured research report.
For glossary:
Use the content-researcher agent.
Terms: [comma-separated terms]
Task: Verify precise definitions, accurate examples, and correct technical details for each term.
Config:
source_hierarchy=[from config, or "not configured"]
research_cache_ttl_days=[from config]
research_cache_path=[from config]
Check the research cache first. Return a structured research report per term.
Store the agent's research report — it is passed as input to Phase 5.
Phase 5: Write
Spawn the draft-writer agent via the Task tool. Pass the research report from Phase 4 and focused context using this generic prompt:
Use the draft-writer agent.
Content type: [content type name]
Topic: [topic or terms]
Output path: [content_types.<type>.path]/[default_language]/[slug].md
Author: [from config]
Frontmatter fields: [content_types.<type>.frontmatter from config]
[If interactive: Audience level, Key angle, Must-cover points, Exclusions from Phase 3]
[If backlog: Pillar context and objectives from the matching pillar file]
Research findings:
[full research report from Phase 4]
Follow the structure guide and formatting rules from the content-style skill for this content type.
Return the file path of the created content.
If seo_brief is set, add the following to the Task prompt before the closing line:
SEO brief:
Target keyword: [seo_brief.keyword] ([seo_brief.volume]/mo)
Related keywords: [seo_brief.related_keywords as comma-separated list]
Content gaps to address: [seo_brief.content_gaps as numbered list]
SEO instructions:
- Include the target keyword in the first 100 words of the article body
- Include the target keyword in at least one H2 heading (naturally)
- Weave related keywords into headings and body text naturally — no keyword stuffing
- Cover all 3 content gaps as sections or meaningful subsections
For backlog items: Also include articles created earlier in this batch as potential cross-references.
Store the created file path(s) — they are passed as input to Phase 5.5 and beyond.
After Phase 5 — inject seo_keyword into frontmatter:
If seo_brief is set:
Read the created file
Add seo_keyword: "[seo_brief.keyword]" to the frontmatter
Write the updated file
Phase 5.5: Image Generation
Skip this phase entirely if any of the following are true:
image_generation.enabledis nottruein config (or theimage_generationsection is absent)- The content type is in
image_generation.skip_types(e.g.,glossary)
The image-generator agent will handle per-article word count and API key checks internally and report if skipped.
For articles, spawn the image-generator agent via the Task tool:
Use the image-generator agent.
Article path: [file path from Phase 5]
Article slug: [slug from Phase 5]
Content type: [article or glossary]
Image generation config:
enabled: [image_generation.enabled from config]
provider: [image_generation.provider from config]
model: [image_generation.model from config, if set]
guidelines: [image_generation.guidelines from config]
output_path: [image_generation.output_path from config]
hero_dimensions: [image_generation.hero_dimensions from config]
inline_dimensions: [image_generation.inline_dimensions from config]
placement: [image_generation.placement from config]
min_word_count: [image_generation.min_word_count from config]
skip_types: [image_generation.skip_types from config]
max_inline_images: [image_generation.max_inline_images from config, if set]
After the agent returns:
- If the agent reported "skipped" or an error: note it and continue to Phase 6 without inserting images.
- If images were generated:
- Insert the hero image markdown after the closing
---of the frontmatter, before the first paragraph - Insert each inline image markdown before its target
## Headingas specified in the agent's output - Store the list of created image file paths — they are added to the git commit in Phase 9
- Insert the hero image markdown after the closing
For glossary entries: Skip this phase.
Phase 6: Style Review
Spawn the style-enforcer agent via the Task tool with a focused prompt:
Use the style-enforcer agent to review the content at [file path from Phase 5].
Content type: [content type name]
Config:
word_range: [content_types.<type>.word_range from config]
guidelines: [content_types.<type>.guidelines list from config]
reference_content: [reference_content list from config]
Check sentence length, paragraph density, scope discipline, tone, plain English, structure, and linking.
Return a structured style review report with must-fix issues.
Apply any must-fix issues from the report before proceeding. If fixes are needed, edit the file(s) directly (they are in scope for this skill).
Phase 7: Auto-create Missing Glossary Entries
Skip this phase if any of the following are true:
- The
glossaryconfig block is absent orglossary.enabledis false glossary.auto_stubsis false- The current content type is the glossary type itself (i.e.,
content_type_name == glossary.content_type)
If all conditions pass, spawn the glossary-creator agent via the Task tool:
Use the glossary-creator agent.
Article path: [file path from Phase 5]
Glossary path: [content_types.[glossary.content_type].path]/[default_language]/
Author: [from config]
[If batch: also list glossary entries created earlier in this batch to avoid duplicates]
Scan the article for technical terms that don't have glossary entries.
Create entries for any missing terms. Skip terms that already exist.
Return the list of created glossary entry file paths (or empty list if none created).
Store the list of created glossary paths — passed to Phase 8.
Phase 8: Bidirectional Linking
Spawn the content-linker agent via the Task tool.
Base prompt (always included):
Use the content-linker agent.
New content: [file path from Phase 5]
Content type: [content type name]
Default language: [from config]
Config:
content_index_path: [content_index_path from config]
linking_max_candidates: [linking_max_candidates from config, default 50]
linking_max_links: [linking_max_links from config, default 10]
url_patterns: [url_patterns from config, if set]
Ensure all cross-content links are complete:
- Update related content's relatedArticles
- Add inline links in existing content where the new topic is mentioned without a link
Return a linking report.
If glossary.enabled AND glossary.bidirectional_linking are true, add to the prompt:
Glossary entries created in this run: [list from Phase 7, if any]
Also ensure glossary bidirectional links:
- Update glossary relatedArticles for terms referenced by the content
- Add inline links in existing articles where glossary terms are mentioned
Review the report. If there are warnings about broken references, fix them before committing.
Phase 9: Reindex + Build + Commit
-
Regenerate the content index: Run the reindex procedure before committing:
- Read config for
content_types,languages,content_index_path - Use Glob to find all
.md/.mdxfiles per type/language - Read each file's frontmatter only
- Write
.content-ops/content-index.jsonwith the structured metadata - (Or invoke the reindex skill if available)
- Read config for
-
Stage all created and modified files (including the updated
content-index.jsonand any image files from Phase 5.5) -
Commit:
content: add [content type] "<title>"
- New [content type]: [file path]
- Images: [N images, or "none"]
- New glossary: [list any created in Phase 7, or "none" or "skipped — glossary disabled"]
- Updated: [list any modified existing files from Phase 8]
Co-Authored-By: Claude <[email protected]>
The post-commit hooks will automatically update trackers when content files are written.
The content index is regenerated in this phase (before commit) so it stays in sync with the committed content.
Phase 10: Suggest Backlog Additions
Skip this phase entirely if the backlog_suggester key is absent or commented out in .content-ops/config.md.
Spawn the backlog-suggester agent via the Task tool:
Use the backlog-suggester agent.
Mode: orchestrator
Triggered by: [file path from Phase 5]
Config:
backlog_file: [backlog_file from config]
content_index_path: [content_index_path from config]
content_strategy: [content_strategy from config]
content_pillars_path: [content_pillars_path from config, or "not configured"]
backlog_suggester: [full backlog_suggester block from config]
After the agent returns:
- If
auto_addis true and entries were appended: stagebacklog_fileand commit it separately with messagecontent: update backlog after [slug] - If
auto_addis false: the agent has already handled user interaction and writing; nothing more to do - If no new entries were found: nothing to commit
Batch Mode (Backlog)
When mode is autonomous (source is backlog), phases 4-9 loop per item:
- For each article in the batch, execute phases 4 through 9 sequentially
- Each article gets its own commit — progress is saved even if the session is interrupted
- Track across items: new glossary entries and new articles created so far, so subsequent items can reference earlier ones (pass this context to draft-writer and glossary-creator agents)
- After all items complete, print a batch summary:
## Batch Complete
### Articles written
- #1 "Title" → [path]
- ...
### Glossary entries created
- term1, term2, ...
### Existing content updated
- [list modified files]
### Commits
- [list of commit hashes and messages]