{"slug": "cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check", "title": "Cross-Post a DEV.to Tutorial to Medium with a Formatting Check", "summary": "Fernando Paladini's open-source project publish-agents offers a reviewable workflow for cross-posting DEV.to tutorials to Medium, using its medium-publisher package to import articles, check formatting, and repair common issues. The tool, built with Patchright browser automation and requiring a saved browser session, outputs a draft for manual inspection before publication.", "body_md": "Cross-posting a technical tutorial is easy to start and surprisingly easy to get wrong. A URL import can leave code blocks split, headings as plain text, or metadata incomplete. The result may look acceptable at a glance while damaging the parts readers need most.\n\nThis tutorial shows a reviewable DEV.to to Medium workflow using [publish-agents](https://github.com/paladini/publish-agents), an open-source TypeScript project by Fernando Paladini. Its `medium-publisher`\n\npackage imports a public article through Medium's import flow, checks the editor against the source Markdown, and can repair a small set of common formatting problems.\n\nCheckout the stable `v0.2.3`\n\nrelease, build the `@paladini/medium-publisher-mcp`\n\npackage, log in once, and create a Medium draft with `publish-devto`\n\n. Keep the default draft behavior while you inspect the title, code blocks, headings, lists, and metadata.\n\nYou need:\n\nThe package uses Patchright browser automation and a saved browser session. It does not use a Medium write API key. The project documents Medium UI changes as a compatibility risk, so treat the browser session and the resulting draft as reviewable state rather than an unattended guarantee.\n\nThe repository's `v0.2.3`\n\nrelease is the stable reference for this walkthrough. Installing from that tag keeps the commands separate from later changes on the default branch.\n\n```\ngit clone https://github.com/paladini/publish-agents.git\ncd publish-agents\ngit checkout v0.2.3\nnpm install\nnpm run build -w @paladini/medium-publisher-mcp\nnpm link -w @paladini/medium-publisher-mcp\n```\n\nThe build produces the CLI and MCP server from the package source. The package declares Node.js 20 or newer and uses `patchright`\n\nas its browser automation dependency. Its post-install step may install the bundled Chromium browser. If that step was skipped in your environment, run the browser installation command documented by Patchright before continuing.\n\nLog in interactively once:\n\n```\nmedium-publisher login\n```\n\nThen check the session in JSON form:\n\n```\nmedium-publisher session-check --json\n```\n\nThe project stores browser state in an operating-system data directory by default. On Windows, the documented default is `%LOCALAPPDATA%\\medium-publisher\\storageState.json`\n\n. You can override it with `MEDIUM_STATE_PATH`\n\nor `MEDIUM_PUBLISHER_HOME`\n\n.\n\nThis is an important boundary: the saved state contains authentication cookies. Protect the directory like any other local credential store, do not commit it, and do not copy it into a CI artifact.\n\nUse the one-shot command with `--draft`\n\nand `--json`\n\n:\n\n```\nmedium-publisher publish-devto `\n  --url \"https://dev.to/your-name/your-tutorial\" `\n  --draft --json\n```\n\nThe URL must point to a public article. The package fetches the DEV.to article through its public API, then opens Medium's official import route. The `--draft`\n\nflag matters because it leaves the final publication decision with you.\n\nThe JSON result includes a Medium URL and metadata details such as whether the title was set, which topics were selected, and whether a hero image was detected. Treat that URL as a draft inspection target until you have checked the actual editor.\n\nOpen the returned draft URL in the browser, or use the package's structured extraction command:\n\n```\nmedium-publisher extract `\n  --url \"https://medium.com/p/your-draft/edit\" `\n  --json\n```\n\nThe extraction workflow is useful because it reports the editor outline and formatting flags. Check at least these points manually:\n\nThe package includes a source review that compares the editor content with the DEV.to Markdown. It also has a security check for secret-like values and unexpected short links. These checks reduce risk, but they cannot replace reading the draft in the Medium editor.\n\nIf extraction identifies a problem, create an actions file and apply only the targeted fixes. For example:\n\n```\n[\n  { \"type\": \"removeEmptyCodeBlocks\" },\n  { \"type\": \"mergeAdjacentCodeBlocks\" },\n  { \"type\": \"promoteDemoteHeading\", \"blockIndex\": 4, \"level\": 2 }\n]\n```\n\nApply it to the draft:\n\n```\nmedium-publisher fix-draft `\n  --url \"https://medium.com/p/your-draft/edit\" `\n  --actions-file fixes.json `\n  --json\n```\n\nRun `extract`\n\nagain after the change. A repair is complete only when the editor and the source agree on the affected blocks. If the editor has changed since the release, stop and inspect the draft manually instead of repeatedly applying actions.\n\nA successful draft run gives you a Medium editor URL, the expected title, readable code blocks, intact headings and lists, and metadata that you can explain. The command does not prove that Medium will preserve every future import. It gives you a repeatable starting point and evidence for a human review.\n\nFor a final pre-publication check, compare the draft with the original DEV.to article block by block. Pay particular attention to content after the first code block because malformed fences can make later paragraphs appear to be code. The repository's own test suite covers its formatting and metadata helpers, but your article is still the source of truth for the review.\n\nThe same package exposes an MCP server. Register `medium-publisher-mcp`\n\nwith your MCP client and use `medium_publish_from_devto`\n\nwith `publish: false`\n\nfor a draft-first flow. The project documents stdio configuration for clients such as Cursor and Claude Code.\n\nAn agent can prepare the URL and summarize the extraction result, but the account owner should approve login, external publication, and any formatting correction. Browser automation is an integration boundary, not a reason to remove human review.\n\nThe CLI uses exit code 3 for a missing or expired session. Run `medium-publisher login`\n\nagain. A two-factor challenge or bot check may require headed browser interaction.\n\nThe source article must be public because the workflow reads its public DEV.to representation. Confirm that the URL loads without authentication and that the article has been published.\n\nMedium's interface can change. The project waits for topic autocomplete and targets the current import and editor controls, but a future UI change can still break a selector. Use `extract`\n\n, inspect the draft, and report reproducible failures to the repository rather than publishing blindly.\n\nStop the workflow if the source or editor contains credentials, private links, or unexpected content. Delete the draft through Medium's controls as appropriate, rotate exposed credentials, and review the local browser-state directory.\n\nNo. The documented workflow uses browser automation and a persistent login session.\n\nThe CLI supports publishing options, but this tutorial deliberately uses `--draft`\n\n. Review the imported editor before choosing a live publication action.\n\nThe package supports public URL import, while the one-shot `publish-devto`\n\npath expects a public DEV.to article so it can compare the imported result with the source Markdown.\n\nNo. Medium UI changes, account challenges, and article-specific content can still require manual intervention.\n\nAI assistance was used to organize this tutorial and check its wording. Commands, version references, workflow boundaries, and limitations were checked against the public `publish-agents`\n\nrepository, its `v0.2.3`\n\nrelease, and the package's documented source behavior.\n\nCross-posting is safer when import, repair, and publication are separate decisions. Use `publish-devto --draft`\n\n, inspect the real Medium editor, rerun extraction after any fix, and publish only after the source and destination agree.\n\nHave you found a Medium import failure that a block-level source review catches earlier than a visual skim?", "url": "https://wpnews.pro/news/cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check", "canonical_source": "https://dev.to/paladini/cross-post-a-devto-tutorial-to-medium-with-a-formatting-check-299n", "published_at": "2026-08-13 15:15:09+00:00", "updated_at": "2026-08-13 15:49:37.404107+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Fernando Paladini", "publish-agents", "medium-publisher", "DEV.to", "Medium", "Patchright"], "alternates": {"html": "https://wpnews.pro/news/cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check", "markdown": "https://wpnews.pro/news/cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check.md", "text": "https://wpnews.pro/news/cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check.txt", "jsonld": "https://wpnews.pro/news/cross-post-a-dev-to-tutorial-to-medium-with-a-formatting-check.jsonld"}}