mech.app
Dev Tools

Multi-Platform Publishing CLI: Agent Skill Composition for Content Distribution

How one CLI tool unifies Dev.to, Medium, and Hashnode publishing into a single agent skill, handling auth, content transformation, and API boundaries.

Source: dev.to
Multi-Platform Publishing CLI: Agent Skill Composition for Content Distribution

Publishing technical content to multiple platforms means juggling three different authentication flows, four markdown variants, and five failure modes. A new CLI tool wraps Dev.to, Medium, Hashnode, GitHub, and social platforms into a single agent skill, exposing the plumbing needed to make cross-platform publishing a reliable orchestration step.

The tool started as a separate skill, then merged into the final phase of an open-source contribution workflow. That evolution tells you something about agent skill composition: the boundary between “publish my PR” and “publish my article” is thinner than it looks.

The Five-Phase Workflow

The author built a contribution pipeline with publishing as the last step:

  1. Discovery: Find repo gaps, check activity, read CONTRIBUTING.md
  2. Scope: Pick contribution type, confirm uniqueness
  3. Write: Use repo template, verify commands, document pitfalls
  4. Submit: Branch, commit, open PR with evidence
  5. Publish: Cross-post to Dev.to, GitHub, Mastodon, Bluesky from one command

Phase 5 is where the CLI lives. It runs after the PR merges, not before. This sequencing matters for state management: the article content references a merged commit hash, a live PR URL, and real contribution evidence.

Authentication Boundary Design

Each platform uses a different auth model:

PlatformAuth MethodCredential StorageRefresh Logic
Dev.toAPI key~/.hermes/config.yamlNone (static key)
GitHubPersonal access token~/.hermes/config.yamlNone (static token)
MastodonOAuth 2.0~/.hermes/config.yamlToken refresh on 401
BlueskyApp password~/.hermes/config.yamlNone (static password)

The CLI prompts for missing credentials on first use, then stores them in a single YAML file. No secrets live in environment variables or command-line arguments. The tool reads ~/.hermes/config.yaml at runtime, which means:

  • Agents can invoke the CLI without passing credentials
  • Credential rotation happens in one place
  • Multi-user systems need per-user config files

The setup command runs once per platform:

python3 platform_picker.py setup devto
python3 platform_picker.py setup github
python3 platform_picker.py setup mastodon
python3 platform_picker.py setup bluesky

After setup, the publish command reads credentials automatically:

python3 platform_picker.py publish \
  --file article.md \
  --platform devto \
  --platform github

Content Transformation Layer

Markdown is not portable. Dev.to supports liquid tags, GitHub renders fenced code blocks differently, Medium strips some HTML, Hashnode has its own metadata schema. The CLI needs a normalization layer.

The tool accepts a single markdown file and transforms it per platform:

  • Image hosting: Uploads images to a CDN, replaces local paths with URLs
  • Code blocks: Normalizes language tags, strips unsupported syntax
  • Metadata: Maps frontmatter to each platform’s schema (tags, canonical URL, publish date)
  • Liquid tags: Strips Dev.to-specific tags for other platforms

This transformation happens in memory before the API call. The original markdown file stays unchanged. If a platform rejects the content, the CLI logs the transformed version for debugging.

Failure Mode Exposure

Publishing to five platforms means five points of failure. The CLI needs to expose partial success in a way agents can reason about.

The tool returns structured JSON on completion:

{
  "status": "partial",
  "published": ["devto", "github"],
  "failed": ["mastodon"],
  "errors": {
    "mastodon": {
      "code": 429,
      "message": "Rate limit exceeded",
      "retry_after": 3600
    }
  }
}

This structure lets agents decide:

  • Retry failed platforms after retry_after seconds
  • Log partial success for manual review
  • Skip platforms that return 409 (duplicate content)
  • Escalate 401/403 errors to credential refresh

The CLI does not retry automatically. It surfaces the error and lets the orchestration layer decide. This keeps the tool stateless.

Rate Limit Handling

Each platform has different rate limits:

  • Dev.to: 10 posts per hour
  • Medium: 5 posts per day
  • Hashnode: No documented limit
  • Mastodon: Varies by instance (typically 300 posts per hour)
  • Bluesky: 50 posts per hour

The CLI does not track rate limits internally. It relies on HTTP 429 responses and Retry-After headers. When a platform returns 429, the CLI:

  1. Parses the Retry-After header (seconds or HTTP date)
  2. Includes retry_after in the error response
  3. Returns immediately without retrying

The orchestration layer can schedule a retry or skip the platform. This design keeps the CLI thin and pushes scheduling logic up the stack.

Duplicate Detection

Publishing the same article twice breaks canonical URLs and confuses readers. The CLI checks for duplicates before posting:

  • Dev.to: Searches by title via API
  • GitHub: Checks if file exists in repo
  • Medium: No duplicate check (Medium allows cross-posts)
  • Hashnode: Searches by slug

If a duplicate exists, the CLI returns a 409 status and skips the platform. The agent can decide whether to update the existing post or abort.

Observability Hooks

The CLI logs every API call to ~/.hermes/logs/publish.log:

2026-06-11T04:07:59Z [INFO] Publishing to devto
2026-06-11T04:08:01Z [INFO] devto: POST /api/articles -> 201
2026-06-11T04:08:02Z [INFO] Publishing to github
2026-06-11T04:08:04Z [ERROR] github: POST /repos/owner/repo/contents -> 422 (file already exists)

Each log entry includes:

  • Timestamp
  • Platform
  • HTTP method and endpoint
  • Status code
  • Error message (if any)

This log format works with standard log aggregators (Loki, Datadog, CloudWatch). Agents can tail the log file to monitor progress or parse it for debugging.

Deployment Shape

The CLI is a single Python script with no external dependencies beyond requests and PyYAML. It runs anywhere Python 3.8+ runs:

  • Local machine: Direct invocation from terminal or agent
  • CI/CD pipeline: GitHub Actions, GitLab CI, Jenkins
  • Serverless function: AWS Lambda, Google Cloud Functions (with /tmp for config storage)
  • Container: Docker image with config mounted as volume

The tool does not require a database, message queue, or persistent state. The only persistent data is ~/.hermes/config.yaml, which can live in a mounted volume or secret manager.

Technical Verdict

Use this pattern when:

  • You publish the same content to multiple platforms regularly
  • You want a single agent skill instead of five platform-specific tools
  • You need structured error responses for orchestration logic
  • You can tolerate eventual consistency (partial success is acceptable)

Avoid this pattern when:

  • You need real-time publishing with strict ordering guarantees
  • You require platform-specific features (Dev.to series, Medium publications)
  • You want the CLI to handle retries and rate limit backoff internally
  • You need to publish to platforms with OAuth flows that require browser redirects

The CLI works best as the final step in a workflow, not as a standalone publishing tool. It assumes the content is ready, the credentials are configured, and the orchestration layer handles retries. If you need more control over platform-specific features, call each API directly instead of wrapping them in a single tool.

Tags

agentic-ai orchestration infrastructure

Primary Source

dev.to