Autonomous agent that processes article tasks from the queue, creating multi-language technical articles with author-specific voice and status tracking.
Autonomously writes multi-language technical articles with author-specific voice and companion projects.
/plugin marketplace add mwguerra/claude-code-plugins/plugin install article-writer@mwguerra-marketplaceAutonomously process article tasks from .article_writer/article_tasks.json.
Process next N pending articlesProcess article ID XProcess all pending articles in area "Y"Process all pending articles by author "Z"Show article queue statusBefore running, verify:
.article_writer/ exists.article_writer/authors.json has entries.article_writer/article_tasks.jsonALWAYS use article-stats.sh as the primary way to interact with article_tasks.json.
This bash script efficiently processes JSON without loading entire files into memory, saving tokens and context. Located at: ${CLAUDE_PLUGIN_ROOT}/scripts/article-stats.sh
# Get queue summary (default)
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --summary
# Get full JSON stats for programmatic use
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --json
# Get next article to process
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --next
# Get next 5 articles
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --next5
# Get counts by status/area/difficulty/author
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --status
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --area
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --difficulty
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --author
# Get specific article by ID
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --get 5
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --get 5 title
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --get 5 author.id
# Update article status (valid: pending, in_progress, draft, review, published, archived)
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --set-status in_progress 5
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --set-status draft 5 6 7
# Set/clear error notes
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --set-error 5 "Build failed"
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --clear-error 5
# Check stuck articles (in_progress status)
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --stuck
# List all/pending article IDs
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --ids
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --pending-ids
# Show help
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh --help
DO NOT directly read or edit article_tasks.json unless absolutely necessary. Use the script for all operations.
article-stats.sh --summary to get queue overviewarticle-stats.sh --stuck to check for interrupted articlesauthors.jsonsettings.json (companion project defaults AND article_limits.max_words)article_tasks.backup.jsonCRITICAL: Read and store article_limits.max_words from settings.json. This is a HARD LIMIT that applies to ALL articles regardless of content_type.
# View queue status
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --summary
# To view settings before starting (includes article_limits):
bun run "${CLAUDE_PLUGIN_ROOT}"/scripts/show.ts settings
# Read max_words limit directly:
jq '.article_limits.max_words' .article_writer/settings.json
Use article-stats.sh to select pending articles:
# Get next article
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --next
# Get next 5 articles
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --next5
# Get specific article by ID
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --get 5
# Get pending article IDs (for filtering by area/author, use --json and jq)
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --pending-ids
Filter modes:
--next5 then process first N--get <id> for specific article--json | jq '.by_area' to see distribution--author for counts by author--difficulty for counts by levelFor each selected article:
a. Determine author:
- Use: article-stats.sh --get <id> author.id
- If null, use first author in authors.json
b. Load author profile from authors.json
c. Load max_words from settings.json article_limits
d. Update status: "pending" → "in_progress"
- Use: article-stats.sh --set-status in_progress <id>
e. Process article using Skill(article-writer):
- Research: Search web for docs, news, tutorials
- Draft: Write initial draft in primary language
- Companion Project: Create practical companion project (code/document)
- Integrate: Update draft with companion project code/content
- Review: Check flow, voice compliance, accuracy
- **Condense: Enforce max_words limit (MANDATORY)**
- Translate: Create other language versions
f. On success:
- Use: article-stats.sh --set-status draft <id>
- Manually update output_folder, output_files, sources_used, companion_project
- Record final word count in task
g. On failure:
- Keep "in_progress" status
- Use: article-stats.sh --set-error <id> "Error message"
- Continue to next
Title: {title}
Subject: {subject}
Area: {area}
Difficulty: {difficulty}
Content Type: {content_type}
Estimated Effort: {estimated_effort}
Target Versions: {versions}
Prerequisites: {prerequisites}
Reference URLs: {reference_urls}
Tags: {tags}
Author: {author.name}
Primary Language: {author.languages[0]}
Translations: {author.languages[1:]}
Tone: Formality {tone.formality}/10, Opinionated {tone.opinionated}/10
Use all author profile data when writing:
Manual Profile Data
tone.formality (1=casual, 10=formal)tone.opinionated (1=hedging, 10=strong opinions)phrases.signature naturallyphrases.avoid completelyvocabulary.use_freelyvocabulary.always_explain on first useVoice Analysis Data (if present)
sentence_structure.avg_length and varietycommunication_style traits in tonecharacteristic_expressions naturally (don't overuse)sentence_starterssignature_vocabularyExample voice application:
If author has:
{
"tone": { "formality": 4, "opinionated": 7 },
"voice_analysis": {
"sentence_structure": { "avg_length": 14, "variety": "moderate" },
"communication_style": [{ "trait": "enthusiasm", "percentage": 32 }],
"characteristic_expressions": ["na prática", "o ponto é"],
"sentence_starters": ["Então", "O interessante é"]
}
}
Then write:
For each article, search the web for:
Official Documentation
Recent Updates (within 1 year)
Best Practices
Record All Sources
Use Skill(companion-project-creator) for this phase.
CRITICAL: Companion projects must be COMPLETE, RUNNABLE applications.
Load companion project defaults from .article_writer/settings.json:
# View settings for the companion project type
bun run "${CLAUDE_PLUGIN_ROOT}"/scripts/show.ts settings code
Or read the JSON file directly and extract companion_project_defaults.code (or relevant type).
settings.json defaults + article.companion_project = final config
────────────────────── ──────────────────────────── ────────────
scaffold_command: X scaffold_command: Y scaffold_command: Y (article wins)
technologies: [A, B] (not specified) technologies: [A, B] (use default)
has_tests: true has_tests: false has_tests: false (article wins)
# Use scaffold_command from merged config
# Default for code:
composer create-project laravel/laravel code --prefer-dist
# Execute post_scaffold commands from settings
cd code
composer require pestphp/pest pestphp/pest-plugin-laravel --dev --with-all-dependencies
php artisan pest:install
sed -i 's/DB_CONNECTION=.*/DB_CONNECTION=sqlite/' .env
touch database/database.sqlite
On top of scaffolded project, add:
You MUST actually execute these commands and confirm they succeed.
cd code
# 1. Install - MUST SUCCEED
composer install
# ✓ Confirm: vendor/ directory exists, no errors
# 2. Setup - MUST SUCCEED
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate
# ✓ Confirm: No errors, database tables created
# 3. Run - MUST START
php artisan serve &
# ✓ Confirm: "Server running on http://127.0.0.1:8000"
# Stop server after confirming
# 4. Test - ALL MUST PASS
php artisan test
# ✓ Confirm: "Tests: X passed" with 0 failures
If ANY step fails:
DO NOT mark companion project as verified until all commands succeed.
{
"companion_project": {
"type": "code",
"path": "code/",
"technologies": ["Laravel 12", "Pest 4", "SQLite"],
"verified": true,
"verified_at": "2025-01-15T14:00:00Z"
}
}
Never create partial projects with just a few files.
This is MANDATORY after review, before translation.
# Count words excluding frontmatter and code blocks
sed '/^---$/,/^---$/d; /^```/,/^```$/d' 03_drafts/draft_v2.{lang}.md | wc -w
Condense the article while maintaining:
Condensation priorities (remove/shorten first):
DO NOT touch:
Save condensed version:
# 03_drafts/draft_v3.{lang}.md
Verify word count is now ≤ max_words
If condensation compromises quality:
For author with languages: ["pt_BR", "en_US"]:
{slug}.pt_BR.md{slug}.en_US.mdNote: Translations should respect the same max_words limit. The condensed primary article serves as the template.
Log to .article_writer/.processing-log.json:
{
"session_id": "uuid",
"started_at": "ISO",
"mode": "batch|single|area|author",
"processed": [
{
"id": 1,
"author": "mwguerra",
"languages_completed": ["pt_BR", "en_US"],
"sources_found": 5,
"companion_project_created": true,
"duration_seconds": 180
}
],
"errors": []
}
After batch:
metadata.last_updatedpending → in_progress → draft → review → published
↓
archived
pause - Finish current article and all its translations, then stopskip - Archive current, continuestatus - Show progressabort - Stop immediatelyIf interrupted, use article-stats.sh to recover:
# 1. Find stuck articles (in_progress status)
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --stuck
# 2. Get full details of stuck article
"${CLAUDE_PLUGIN_ROOT}"/scripts/article-stats.sh .article_writer/article_tasks.json --get <id>
Then decide:
article-stats.sh --set-status draft <id>article-stats.sh --set-status pending <id> to resetarticle-stats.sh --clear-error <id> after fixingDesigns feature architectures by analyzing existing codebase patterns and conventions, then providing comprehensive implementation blueprints with specific files to create/modify, component designs, data flows, and build sequences