| name | documentation-asset-manager |
| description | Manages Tallyfy documentation assets on Cloudflare R2. Uploads images, generates AI captions using Claude Code native vision, maintains CSV inventory with build-time integration. Use when managing docs screenshots or media files. |
| version | 2.0.0 |
| author | Tallyfy Dev Team |
| tags | documentation, assets, r2, cloudflare, inventory, ai-captions, screenshots, vision, build-integration |
| dependencies | pillow, requests |
Documentation Asset Manager
Purpose
Automates the complete workflow for Tallyfy documentation asset management with Claude Code native vision:
- Upload assets to Cloudflare R2 storage (screenshots bucket)
- Generate AI captions using Claude Code native vision (alt text, descriptive, SEO)
- Maintain CSV inventory at
$GITHUB_ROOT/documentation/documentation_assets.csv - Build-time integration - Automatic alt text injection during Starlight builds
- Return public URLs immediately (screenshots.tallyfy.com domain)
- Track asset usage across documentation files
Recent Updates (v2.0.0)
✅ Completed:
- 287 images captioned with vision-based analysis (100% coverage for native images)
- Claude Code native - No external API key needed
- Build-time integration - Remark plugin auto-injects alt text from CSV
- Parallel Task agents - Batch processing support
- Path portability - Auto-detection, works on any developer machine
Activation Triggers
- "upload documentation asset"
- "upload screenshot to R2"
- "add image to docs"
- "manage documentation media"
- "generate image caption"
- "update documentation screenshot"
- When user provides image/asset path for documentation
Prerequisites
Required Credentials (in parent directory):
$GITHUB_ROOT/cloudflare_credentials.jsonwith fields:account_id- Cloudflare account IDapi_token- Cloudflare API token with R2 access
Required Packages (auto-check):
pip3 install pillow requests
Claude Code Native (Recommended):
- No external API key needed - Uses Claude Code's native vision capabilities
- For batch operations: Use Task agents (see Batch Vision Captioning section)
- For single images:
image_captioner.pyuses subprocess to call claude native
Instructions
Step 1: Validate Prerequisites
# Check credentials exist
test -f $GITHUB_ROOT/cloudflare_credentials.json || echo "❌ Credentials missing"
# Check Anthropic API key
test -f ~/.anthropic/api_key || echo "⚠️ Set ANTHROPIC_API_KEY or create ~/.anthropic/api_key"
# Check Python packages
python3 -c "import anthropic, PIL, requests" 2>/dev/null || echo "⚠️ Run: pip3 install anthropic pillow requests"
Step 2: Upload Asset to R2
Use the R2 uploader script to upload any file:
# Single file upload
python3 ~/.claude/skills/documentation-asset-manager/scripts/r2_uploader.py \
--file /path/to/screenshot.png \
--key "tallyfy/pro/desktop-light-new-feature.png" \
--credentials $GITHUB_ROOT/cloudflare_credentials.json
# Returns JSON with public URL:
# {
# "success": true,
# "url": "https://screenshots.tallyfy.com/tallyfy/pro/desktop-light-new-feature.png",
# "key": "tallyfy/pro/desktop-light-new-feature.png",
# "size": "125.4KB"
# }
Step 3: Generate AI Captions
Generate all three caption types using Claude Code native vision:
For Single Images:
# Generate captions using Claude Code native (no API key needed)
python3 ~/.claude/skills/documentation-asset-manager/scripts/image_captioner.py \
--url "https://screenshots.tallyfy.com/tallyfy/pro/desktop-light-new-feature.png" \
--output /tmp/captions.json
# Returns:
# {
# "captions": {
# "alt_text": "Task assignment interface showing user selection dropdown",
# "descriptive": "Tallyfy task assignment screen with dropdown menu...",
# "seo": "Tallyfy task assignment feature showing how to assign tasks..."
# }
# }
For Batch Operations (Recommended for >10 images):
# Use Task agents for parallel processing
# See "Batch Vision Captioning" section below
Step 4: Update CSV Inventory
Add asset to master inventory CSV:
# Add to inventory
python3 ~/.claude/skills/documentation-asset-manager/scripts/asset_inventory.py \
--action add \
--r2-key "tallyfy/pro/desktop-light-new-feature.png" \
--url "https://screenshots.tallyfy.com/tallyfy/pro/desktop-light-new-feature.png" \
--captions /tmp/captions.json \
--article-ids "pro-tasks-assign,pro-getting-started"
Step 5: Complete Workflow (Orchestrator)
For one-command complete workflow:
# Upload + Caption + Inventory in one go
python3 ~/.claude/skills/documentation-asset-manager/scripts/orchestrator.py \
--file /path/to/screenshot.png \
--key "tallyfy/pro/desktop-light-new-feature.png" \
--article-ids "pro-tasks-assign" \
--credentials $GITHUB_ROOT/cloudflare_credentials.json
# Complete output:
# ✅ Uploaded: https://screenshots.tallyfy.com/tallyfy/pro/desktop-light-new-feature.png
# ✅ Alt text: "Task assignment interface showing user selection dropdown"
# ✅ Added to inventory: $GITHUB_ROOT/documentation/documentation_assets.csv
Build-Time Integration
Automatic Alt Text Injection:
The master CSV is integrated with Starlight documentation builds via a remark plugin:
Location: /support-docs/src/plugins/remark/inject-alt-text.ts
How It Works:
- During build, remark plugin reads
/documentation/documentation_assets.csv - Matches image URLs in markdown to CSV entries
- Auto-injects
ai_caption_altas alt text - Handles URL encoding/decoding automatically
- Falls back gracefully if CSV unavailable
Local Development:
cd /support-docs
# CSV symlinked to ../documentation/documentation_assets.csv
npm run dev # Alt text injected automatically
CI/CD (Netlify):
# package.json prebuild script copies CSV
npm run prebuild # Copies CSV before build
npm run build # Alt text injected during build
Example:
<!-- Before build: -->

<!-- After build: -->

Batch Vision Captioning
For Large-Scale Caption Regeneration:
When regenerating captions for many images (>10), use parallel Task agents for efficiency:
Process:
- Prepare images - Download and resize to max 1800px (Claude vision constraint)
- Launch Task agents - Process in parallel batches of ~48 images
- Consolidate results - Merge all batch outputs
- Update CSV - Apply new captions to master inventory
Example (287 images completed in ~60 minutes):
# See /temporary/asset-inventory/ for reference implementation:
# - prepare_images_for_vision.py (download + resize)
# - Task agents launched in parallel (6 batches of ~48 images)
# - consolidate_and_update.py (merge results → CSV)
Why Task Agents:
- Native Claude Code vision (no API keys)
- Parallel processing (6x speedup)
- Automatic retry and error handling
- Real-time progress tracking
Workflow Patterns
Pattern 1: Upload New Screenshot
# User: "Upload this screenshot to docs"
# Skill detects image file path, generates reasonable R2 key from filename
# Executes: upload → generate captions → update inventory → return URL
Pattern 2: Replace Existing Screenshot
# User: "Replace screenshot at /tallyfy/pro/desktop-light-tasks.png"
# Skill: uploads with same key (overwrites), regenerates captions, updates inventory
Pattern 3: Batch Upload
# User: "Upload all screenshots in /docs/images/"
# Skill: processes each file sequentially with delays to avoid rate limits
Pattern 4: Generate Caption Only
# User: "Generate caption for existing image at https://screenshots.tallyfy.com/..."
# Skill: skips upload, generates captions using Claude Code native, updates inventory
Pattern 5: Regenerate All Captions (Completed)
# All 287 native images now have vision-based captions (completed 2025-11)
# Location: /documentation/documentation_assets.csv (columns: ai_caption_alt, ai_caption_descriptive, ai_caption_seo)
# Build integration: Auto-injected during Starlight builds via remark plugin
Temporary Assets
Working Directory: $GITHUB_ROOT/temporary/asset-manager/
Files Created:
upload_result.json- R2 upload responsecaptions_alt.json- Alt text captioncaptions_descriptive.json- Descriptive captioncaptions_seo.json- SEO captionasset_metadata.json- Combined metadataprocessing.log- Processing log
Cleanup: Temporary files auto-cleaned after successful completion
Master Asset Inventory
Location: $GITHUB_ROOT/documentation/documentation_assets.csv
CSV Columns:
filename- Asset filenamer2_key- Full R2 bucket keyproduction_url- Public URL (screenshots.tallyfy.com)source_type- native|orphaned|external|missingfile_type- File extensionfile_size- Human-readable sizefile_size_bytes- Size in bytesurl_exists- Boolean (URL accessible)article_ids- Comma-separated article IDsarticle_count- Number of articles referencinglast_modified- R2 last modified timestampetag- R2 ETagai_caption_alt- Alt text captionai_caption_descriptive- Detailed captionai_caption_seo- SEO-optimized captionneeds_caption- yes|no (based on file type)
Auto-Update: CSV automatically updated on any upload/delete/caption operation
Error Handling
Scenario 1: Credentials Missing
ERROR: Credentials file not found
ACTION: Check $GITHUB_ROOT/cloudflare_credentials.json exists
PROMPT: "Cannot find Cloudflare credentials. Ensure file exists with:
- account_id
- api_token"
Scenario 2: R2 Upload Fails
ERROR: R2 upload failed (API error)
ACTION: Retry once after 5s, then notify user
PROMPT: "Upload to R2 failed: {error_message}
Retrying once..."
Scenario 3: Caption Generation Fails
ERROR: Claude API error (rate limit, network, etc)
ACTION: Complete upload, skip caption, mark for later
PROMPT: "File uploaded successfully to {url}
Caption generation failed: {error}
You can generate captions later with:
python3 scripts/image_captioner.py --url {url}"
Scenario 4: Rate Limit Hit
ERROR: Anthropic API rate limit
ACTION: Wait 60s, retry once, then defer
PROMPT: "Rate limit reached. Waiting 60s before retry...
If persistent, use --skip-captions flag to upload without captions"
Scenario 5: Inventory CSV Locked
ERROR: CSV file in use (another process)
ACTION: Wait 2s, retry 3 times, then warn
PROMPT: "Inventory CSV busy. Retried 3 times. Upload succeeded but inventory not updated.
Run: python3 scripts/asset_inventory.py --sync to update manually"
Advanced Usage
Skip Caption Generation
# For quick uploads without AI captions
python3 scripts/orchestrator.py --file screenshot.png --skip-captions
Regenerate Captions for Existing Assets
# Regenerate all captions for specific asset
python3 scripts/image_captioner.py \
--url "https://screenshots.tallyfy.com/tallyfy/pro/desktop-light-tasks.png" \
--update-inventory
Bulk Operations
# Process all images in directory
for img in /docs/screenshots/*.png; do
python3 scripts/orchestrator.py --file "$img" --batch-mode
sleep 3 # Delay between uploads
done
Verify Inventory Integrity
# Check all URLs in inventory are accessible
python3 scripts/asset_inventory.py --verify-urls
Generate Statistics
# Get inventory statistics
python3 scripts/asset_inventory.py --stats
# Output:
# Total assets: 812
# Active (used in docs): 290
# Orphaned (not referenced): 499
# Missing (404s): 23
# Images with captions: 288
R2 Bucket Configuration
Bucket Name: screenshots
Account ID: ac9046789c9ad74c8704a143b442f918
Public Domain: screenshots.tallyfy.com
API Endpoint: https://api.cloudflare.com/client/v4/accounts/{account_id}/r2/buckets/screenshots/objects
CDN: Cloudflare global edge cache
Naming Conventions
Recommended R2 Key Patterns:
Structured (Preferred):
tallyfy/pro/desktop-light-{action}-{context}.png tallyfy/pro/mobile-dark-{feature}.png manufactory/{feature}-{version}.pngLegacy (Auto-generated):
file-{random_id}.png
Examples:
tallyfy/pro/desktop-light-assign-task.pngtallyfy/pro/desktop-light-sidebar-numerated.pngmanufactory-triggers-v2.png
Security Notes
- Credentials stored in parent directory (not in skill)
- No API keys embedded in skill files
- Anthropic API key from environment or user's ~/.anthropic/
- R2 bucket is private with public domain mapping
- All uploads go through Cloudflare API authentication
Troubleshooting
"Module not found" errors:
pip3 install pillow requests
"Credentials not found":
cat $GITHUB_ROOT/cloudflare_credentials.json
# Should show account_id and api_token
"Claude Code not available":
# Ensure Claude Code CLI is installed and accessible
which claude # Should return path to claude executable
For external API usage (legacy):
# Not recommended - use Claude Code native instead
export ANTHROPIC_API_KEY="your-api-key-here"
"R2 upload succeeds but URL 404":
- Check bucket name is "screenshots"
- Verify custom domain screenshots.tallyfy.com is configured
- Wait 30-60s for CDN cache
"CSV inventory out of sync":
# Rebuild inventory from R2 + documentation
python3 scripts/asset_inventory.py --rebuild
See Also
references/r2_api_reference.md- Complete Cloudflare R2 API docsreferences/supported_formats.md- Supported file types and limitsreferences/examples.md- Usage examples and tutorials$GITHUB_ROOT/documentation/CLAUDE.md- Documentation repo guidelines
Maintenance
Regular Tasks:
- Weekly: Verify inventory CSV integrity
- Monthly: Review orphaned assets (consider cleanup)
- Quarterly: Check for missing images (404s) and fix
- As needed: Regenerate captions for improved quality
Inventory Location:
- Primary:
$GITHUB_ROOT/documentation/documentation_assets.csv(version controlled) - Working Directory:
$GITHUB_ROOT/temporary/asset-inventory/asset_inventory_master.csv(generated master)