# Article Production Toolchain

> Article Production Toolchain

- Skill: `lucadominguez/article-production-toolchain` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lucadominguez/article-production-toolchain`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lucadominguez/article-production-toolchain/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: lucadominguez (https://skillmd.com/u/lucadominguez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lucadominguez/article-production-toolchain

---

# Article Production Toolchain

Companion skill for `professional-writer`. Handles the research, citation, visualization, media, and multi-publication infrastructure layers that feed the writer. The writer skill handles voice, structure, and prose — this skill handles everything that goes into the piece before and after the words.

Load alongside `professional-writer` for any article that needs: citations with verified sources, publication-quality charts, embedded media, or cross-publication management.

## When To Load

- Any article that cites sources (papers, studies, data)
- Any article with charts, graphs, or data visualization
- Any article needing embedded images or media
- Setting up or managing multiple publications simultaneously
- Building an editorial pipeline that spans several outlets

Skip for: pure opinion pieces, personal essays, or articles where the writer's voice is the only asset.

---

## Layer 1: Research Gathering

### Source Types and Tools

| Source type | Tool | Command/Pattern |
|------------|------|----------------|
| Academic papers | `arxiv` skill (Hermes-native, loads in-context) OR curl fallback | `arxiv` skill: loaded alongside this skill, then search by keyword. CLI fallback: `curl -s "http://export.arxiv.org/api/query?search_query=all:TERM&max_results=10"` (API is HTTP-only — approve the security prompt) |
| Blogs/RSS | `blogwatcher` skill | Monitor relevant feeds for current content |
| Reddit discussions | `reddit-data-extraction` skill | Extract threads from domain subs. Use Arctic Shift API. |
| Hacker News | Algolia API | `curl -s "https://hn.algolia.com/api/v1/search?query=TOPIC&tags=story&hitsPerPage=10"` |
| GitHub projects | `github-repo-discovery` skill | Find tools, datasets, and reference implementations |
| Existing notes | `obsidian` skill | Search user's vault at `/mnt/c/Users/Lenovo/Desktop/work/` |

PITFALL: The `arxiv` CLI binary does not exist — it's a Hermes skill that gets loaded into context, not a standalone command. If the arxiv skill isn't loaded, use the curl fallback against the arXiv API. The API endpoint is HTTP-only (not HTTPS), which triggers Hermes's security prompt — approve it.

### Research Output Format

Save all gathered sources to a structured research file:

```markdown
# Research: [Article Title]

## Key Papers
- [Title] ([Year]) — [One-line finding]. DOI: [link]
- [Title] ([Year]) — [One-line finding]. arXiv: [id]

## Data Points
- [Statistic] — Source: [publication, year, page/URL]
- [Statistic] — Source: [publication, year, page/URL]

## Competing Views
- [Author/Outlet]: [Position summary] — [Why they hold it]
- [Author/Outlet]: [Position summary] — [Why they hold it]

## Relevant Threads
- HN: [title] ([points] pts, [comments] comments) — [key takeaway]
- Reddit r/[sub]: [title] — [key takeaway]

## Tools & Repos
- [owner/repo] (★[stars]) — [relevance to article]
```

---

## Layer 2: Citation Pipeline

### Quick Start: Which Tool When

| Situation | Tool | Why |
|-----------|------|-----|
| Building a .bib library from scratch | **JabRef/jabref** | GUI, imports from DOI/arXiv/ISBN, exports BibTeX/BibLaTeX |
| CLI/scriptable workflows | **papis/papis** | Command-line, Python API, git-backed |
| Finding papers on a topic | **findpapers** | Searches multiple academic databases, outputs CSV |
| Automated manuscript with live citations | **manubot/manubot** | Markdown → HTML/PDF with auto-resolved citations from DOIs/URLs/arXiv IDs |
| Quick bibliography formatting | **pubs/pubs** | CLI, lightweight, BibTeX-native |

### Citation Workflow

**Step 1: Discover papers**
```bash
# Using findpapers (Python CLI)
pip install findpapers
findpapers search "ketamine depression mechanisms" --limit 30 --output results.csv

# Using arxiv skill (Hermes-native)
# Load arxiv skill, search by keyword
```

**Step 2: Collect to bibliography**
```bash
# Using papis (CLI-based)
papis add --from doi 10.1038/s41586-023-12345-6
papis add --from arxiv 2301.12345
papis add --from url https://example.com/paper

# Using JabRef (GUI-based)
# Launch: java -jar JabRef-*.jar
# Drag-and-drop PDFs, auto-extract metadata
```

**Step 3: Insert citations in article**

For markdown-based articles (Substack, blog):

```markdown
Ketamine's rapid antidepressant effects are mediated through mTOR pathway activation
[@berman2000; @zorumski2023]. Recent meta-analyses confirm effect sizes of d=0.8-1.2
for single-dose protocols [@xu2024].
```

For manubot-based articles (scientific publishing):
```markdown
Ketamine's rapid antidepressant effects are mediated through mTOR pathway activation
[@doi:10.1016/s0006-3223(99)00230-9; @arxiv:2301.12345].
```

**Step 4: Generate formatted bibliography**
```bash
# Using pandoc with .bib file
pandoc article.md --bibliography=references.bib --citeproc -o article-with-refs.md

# Using manubot (auto-resolves from inline DOIs/URLs)
manubot process --content-directory content --output-directory output
```

### Citation Quality Checklist

Before finalizing any article with citations:
- [ ] Every factual claim has a source
- [ ] Sources are primary (studies, papers, official data) not secondary (blog posts about studies)
- [ ] Publication dates are within 3 years unless citing foundational work
- [ ] DOIs resolve (test with `curl -I https://doi.org/10.xxx/xxxx`)
- [ ] No circular citations (source A cites source B, but you're using A as proof of B's claim)
- [ ] Contrarian or disconfirming evidence is cited alongside supporting evidence

---

## Layer 3: Publication-Quality Data Visualization

### Tool Selection by Chart Type

| Chart type | Tool | Why | Code pattern |
|-----------|------|-----|-------------|
| Bar, line, scatter, area | **Apache ECharts** | Beautiful defaults, responsive, interactive. Best for publication embedding. | JSON config object, no build step |
| Custom/unusual viz | **D3.js** | Maximum control. Network graphs, force layouts, custom geometries. | SVG manipulation, steeper learning curve |
| Statistical plots | **Python matplotlib/seaborn → PNG** | Familiar, precise, reproducible. | Generate PNG, embed in article |
| Interactive dashboards | **Streamlit** | Turn Python scripts into web apps. Good for article supplements. | `streamlit run app.py` |
| Real-time data | **Grafana** | For articles about live metrics or monitoring data | Screenshot from Grafana instance |

### ECharts Workflow (Recommended for Most Articles)

1. Write chart config as JSON
2. Render in browser or screenshot via headless Chromium
3. Save as high-res PNG (at least 1200px wide for Substack)
4. Embed in article with alt text and source caption

```javascript
// Example: Comparative bar chart
{
  title: { text: 'Antidepressant Response Rates by Protocol' },
  tooltip: {},
  xAxis: { data: ['Placebo', 'SSRI', 'Ketamine IV', 'Ketamine IM', 'Psilocybin'] },
  yAxis: { name: 'Response Rate (%)' },
  series: [{
    type: 'bar',
    data: [30, 47, 71, 65, 68],
    itemStyle: { color: '#6366f1' }
  }]
}
```

**Rendering for article embedding:**
```bash
# Option A: Save as standalone HTML for interactive (link from article)
cat > chart.html << 'EOF'
<!DOCTYPE html><html><head>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
</head><body><div id="chart" style="width:800px;height:500px;"></div>
<script>/* ... echarts config ... */</script></body></html>
EOF

# Option B: Screenshot via Chromium for static PNG
# Use Playwright or Puppeteer to capture
```

### D3.js for Custom Visualizations

Use when ECharts built-in chart types don't fit. Common in:
- Network/graph visualizations
- Custom interactive explainers
- Force-directed layouts
- Animated transitions between states

### Design Standards for All Charts

- **Dark theme preferred** for tech/science publications (matches Substack dark mode)
- **Font**: Inter for labels, JetBrains Mono for code/numeric data
- **Color palette**: Use 6-color palette — #6366f1 (indigo), #ec4899 (pink), #10b981 (emerald), #f59e0b (amber), #3b82f6 (blue), #ef4444 (red)
- **Minimum resolution**: 1200px wide for Substack (they upscale)
- **Always include**: title, axis labels with units, source line, data date range
- **Never**: 3D charts (distort data), pie charts with >5 slices, dual y-axes without clear labeling

---

## Layer 4: Media & Image Retrieval

### Finding Images for Articles

**For scientific/technical images:**
```bash
# PubMed Central images (open access)
# Search PMC, filter by CC-BY license

# Wikimedia Commons (freely licensed)
curl -s "https://commons.wikimedia.org/w/api.php?action=query&list=search&srsearch=TOPIC&format=json"

# Flickr Creative Commons
curl -s "https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=KEY&text=TOPIC&license=4,5,6,7,8&format=json"
```

**For general illustration/storytelling:**
```bash
# Unsplash (free, no attribution required)
# Use their API or direct search

# Pexels (free, no attribution required)
# Use their API
```

**Reverse image search (verify source/origin):**
```bash
# dessant/search-by-image browser extension
# Right-click → "Search by Image" → 30+ engines
```

### Image Attribution Format

Every image in the article must include:
```
![Alt text describing the image](path/to/image.png)
*Caption explaining what the image shows. Source: [Creator/Organization] ([License])*
```

### Image Quality Standards

- Minimum 1200px wide for Substack
- PNG for diagrams/charts, JPEG for photographs
- Compress photographs (quality 85) — Substack has file size limits
- Keep diagrams under 500KB

---

## Layer 5: Multi-Publication Orchestration

### Publication Manifest

For managing multiple publications, create a master manifest at `~/writing/publications.yaml`:

```yaml
publications:
  - id: research-collective
    name: "Research Collective"
    platform: substack
    url: https://researchcollective.substack.com
    niche: "Nootropics, research chemicals, neurochemistry"
    cadence: weekly
    voice_profile: "scott-alexander"
    free_tier: "Deep-dive articles, literature reviews"
    paid_tier: "Protocol guides, sourcing directories, compound analyses"
    
  - id: transhumanism-digest
    name: "Transhumanism Digest"
    platform: substack
    url: https://transhumanismdigest.substack.com
    niche: "BCI, longevity, gene therapy, AI-human integration"
    cadence: biweekly
    voice_profile: "dwarkesh-patel"
    free_tier: "Trend analysis, paper summaries"
    paid_tier: "Investment memos, technology deep-dives"
    
  - id: personal-blog
    name: "Personal Blog"
    platform: ghost
    url: https://lucadominguez.com
    niche: "Building in public, agent workflows, experiments"
    cadence: as-written
    voice_profile: "default"
    free_tier: "Everything"
```

### Editorial Calendar

For each publication, maintain a forward-looking calendar at `~/writing/{pub-id}/calendar.md`:

```markdown
# Editorial Calendar: Research Collective

## July 2026
| Week | Article | Status | Research | Citations | Charts | Media |
|------|---------|--------|-----------|-----------|--------|-------|
| Jul 20 | Ketamine mechanisms update | Drafting | Done | 12 sources | 3 charts | 2 images |
| Jul 27 | Peptide sourcing guide v2 | Research | In progress | — | — | — |

## August 2026
| Week | Article | Status |
|------|---------|--------|
| Aug 3 | Novel nootropic patent review | Planned |
| Aug 10 | — (buffer week) | — |
| Aug 17 | RC safety protocols update | Planned |
| Aug 24 | Community compound survey results | Planned |
```

### Cross-Publication Content Strategy

**Repurpose, don't duplicate:**
- A research finding can be a deep-dive in one pub and a shorter angle piece in another
- Same data, different framing: "Here's what this means for nootropics users" vs "Here's what this means for longevity research"
- Shared research folder, pub-specific drafts

**Content tiers across publications:**
```
Research (shared) → 
  ├─ Research Collective: Deep-dive + protocol implications
  ├─ Transhumanism Digest: Future trajectory + investment angle
  └─ Personal Blog: How I researched this + tooling used
```

### Batch Production Mode

When filling multiple slots across publications, use parallel subagents:

```
professional-writer loaded for article A (Research Collective)
professional-writer loaded for article B (Transhumanism Digest)
```

Each subagent gets: its publication's voice profile, the shared research folder, and specific angle instructions.

### Quality Consistency Across Publications

Every article across every publication must pass the same gate:

- [ ] Citations verified (DOIs resolve, sources are primary)
- [ ] Charts meet design standards (see Layer 3)
- [ ] Humanization pass complete (see professional-writer Phase 4)
- [ ] Read-aloud check (no AI rhythm)
- [ ] Title earns the click (specific, surprising, or useful)
- [ ] Publication voice profile applied correctly

---

## Layer 6: Obsidian Integration

### Vault Layout for Publications

```
{Vault}/Publications/
├── Publications Hub.md              ← Master dashboard
├── publications.yaml                ← Manifest (from Layer 5)
│
├── research-collective/
│   ├── Publication Hub.md           ← Pub-specific dashboard
│   ├── Editorial Calendar.md
│   ├── Voice Profile.md             ← This pub's voice parameters
│   ├── Articles/
│   │   ├── 2026-07-20-ketamine-mechanisms/
│   │   │   ├── research.md
│   │   │   ├── sources.bib
│   │   │   ├── draft-v1.md
│   │   │   ├── final.md
│   │   │   └── charts/
│   │   └── ...
│   ├── Playbooks/                   ← Paid content
│   └── Assets/                      ← Shared images, templates
│
├── transhumanism-digest/
│   └── ...
│
└── Shared Research/                 ← Cross-publication research
    ├── nootropics-literature.md
    ├── longevity-clinical-trials.md
    └── bci-patent-tracker.md
```

### Git Workflow for Articles

```bash
# After creating/updating article in Obsidian vault
cd /mnt/c/Users/Lenovo/Desktop/work
git.exe add "Publications/research-collective/Articles/2026-07-20-ketamine-mechanisms/"
git.exe commit -m "article: ketamine mechanisms update - draft 1"
git.exe push origin main
```

PITFALL: On WSL, use `git.exe` (Windows git) not `git` (Linux git) for repos on `/mnt/c/`. The Linux git on NTFS filesystems is slow and can corrupt the index.

---

## Quick Reference: Tool by Article Phase

| Phase | Tool | Key command |
|-------|------|------------|
| **Research** | arxiv skill | Search papers |
| | blogwatcher skill | Monitor feeds |
| | reddit-data-extraction skill | Extract discussions |
| | Algolia HN API | `curl` search |
| **Citation** | findpapers | `findpapers search "..."` |
| | papis | `papis add --from doi ...` |
| | JabRef | GUI for .bib management |
| | manubot | Auto-citation from markdown |
| **Charts** | ECharts | JSON config → HTML or PNG |
| | D3.js | Custom SVG viz |
| | Streamlit | Interactive supplement |
| **Media** | search-by-image | Reverse image lookup |
| | Wikimedia API | Free licensed images |
| | Unsplash/Pexels | Stock photography |
| **Publishing** | subskill: substack-publishing | Email or browser |
| | himalaya CLI | Email-to-Substack |
| | Obsidian vault | Git-backed content |
| **Multi-pub** | publications.yaml | Manifest |
| | Editorial calendars | Per-pub planning |
---

## Layer 7: Email Delivery

When the user wants the article emailed directly (vs published to Substack), use Gmail SMTP via Python. See `references/email-delivery.md` for the full script and prerequisites.

Quick pattern:
```bash
python3 << 'PYEOF'
import smtplib, os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

env = {}
with open(os.path.expanduser('~/.hermes/.env')) as f:
    for line in f:
        line = line.strip()
        if line and '=' in line:
            k, v = line.split('=', 1)
            env[k] = v.strip().strip('"').strip("'")

msg = MIMEMultipart()
msg['Subject'] = 'Article: TITLE'
msg['From'] = env['EMAIL_ADDRESS']
msg['To'] = env['EMAIL_ADDRESS']
with open('/path/to/article.md') as f:
    msg.attach(MIMEText(f.read(), 'plain'))

s = smtplib.SMTP('smtp.gmail.com', 587, timeout=15)
s.starttls()
s.login(env['EMAIL_ADDRESS'], env['EMAIL_PASSWORD'])
s.sendmail(env['EMAIL_ADDRESS'], env['EMAIL_ADDRESS'], msg.as_string())
s.quit()
print("SENT")
PYEOF
```

Requires: `EMAIL_ADDRESS` and `EMAIL_PASSWORD` (Gmail App Password, not account password) in `~/.hermes/.env`.

---

## PITFALLS

1. **Citation DOI resolution is slow over VPN/proxy.** Run citation lookups without proxy routing. If behind a VPN, temporarily route `curl` through direct connection.

2. **ECharts requires JavaScript rendering.** It doesn't produce static PNGs natively. Use a headless browser (Playwright/Puppeteer) or the ECharts export server for programmatic screenshots. For simple charts, matplotlib+seaborn → PNG is faster.

3. **papis and JabRef conflict on .bib format.** Pick one and stick with it per publication. papis uses its own library format but can export .bib. JabRef works natively with .bib. Don't mix them on the same library.

4. **Image licenses matter for publication.** A CC-BY image on Wikimedia is fine. An image you found via reverse image search on someone's blog is not — even if `search-by-image` found it. Always verify license before embedding.

5. **Multi-publication voice bleed.** When writing for two publications in the same session, reload the voice profile between pieces. The `professional-writer` skill's voice parameters persist in memory — reset them explicitly between publications.

6. **Substack image compression.** Substack compresses images aggressively. Upload at 2x the display size (2400px wide if the column is 1200px) and let them downscale. PNG for diagrams, JPEG quality 95 for photos.

7. **Git on /mnt/c/ from WSL is slow.** Use `git.exe` not `git`. Commits on large vaults can take 30+ seconds — use `background=true` with `notify_on_complete=true`.

8. **Matplotlib works in direct `terminal()` but not `execute_code` sandbox.** The sandbox environment doesn't have matplotlib installed. When generating charts, use `python3 << 'PYEOF' ... PYEOF` in a direct `terminal()` call — the system Python has matplotlib. The `execute_code` sandbox is fine for gh API calls (though see `github-repo-discovery` pitfall #8 about auto-parsed dicts).
