Crushing the Command Line with Amazon Q: Building a Medium to DEV.to Converter

This is a submission for the Amazon Q Developer "Quack The Code" Challenge: Crushing the Command Line What I Built I built medium2dev, a command-line tool that automatically converts Medium blog posts to DEV.to-compatible markdown format and optionally publishes them directly to DEV.to as draft posts. This tool solves a common problem for technical writers who publish on multiple platforms: maintaining consistent formatting across different publishing platforms. The tool preserves: Article title and headings Text formatting (bold, italic, etc.) Code blocks with proper syntax highlighting Inline images (automatically downloaded and referenced) Lists and other structural elements Generates proper DEV.to frontmatter Demo Here's a demonstration of converting a Medium article to DEV.to format: # Basic conversion python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 # Publish directly to DEV.to as a draft python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 --publish --api-key YOUR_DEVTO_API_KEY # Or use environment variable for API key export DEVTO_API_KEY=your_api_key python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 --publish Output: 2025-05-03 15:07:49,696 - medium2dev - INFO - Fetching article from https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 2025-05-03 15:07:51,100 - medium2dev - INFO - Original Medium content word count: 463 2025-05-03 15:07:51,100 - medium2dev - INFO - Downloaded 0 content images 2025-05-03 15:07:51,116 - medium2dev - INFO - Conversion complete! Output saved to /Users/vivekvelso/Documents/opensource/medium2dev/new/aws-resource-tag-compliance-with-automation-64ae16e42a11.md Conversion successful! Output saved to: /Users/vivekvelso/Documents/opensource/medium2dev/aws-resource-tag-compliance-with-automation-64ae16e42a11.md Images saved to: /Users/vivekvelso/Documents/opensource/medium2dev/images When publishing to DEV.to, it also displays a word count comparison: Successfully published as draft to DEV.to! Word Count Comparison: | Platform | Word Count | |----------|------------| | Medium | 463 | | DEV.to | 458 | The tool successfully: Fetches the Medium article Cleans up author metadata and unnecessary UI elements Removes Medium-specific UI elements like "Listen", "Share", etc. Converts the content to markdown Generates a properly formatted DEV.to markdown file (Optionally) Publishes the article as a draft to DEV.to Provides a word count comparison between platforms Code Repository The code is available on GitHub: medium2dev Key files: medium2dev.py: The main Python script requirements.txt: Dependencies README.md: Documentation How I Used Amazon Q Developer I used Amazon Q Developer CLI to create this entire project from scratch. Here's how the conversation went: Initial Prompt I started by explaining my idea to Amazon Q: I am submitting an idea for this challenge. Crushing the Command Line Build an automation with Amazon Q Developer CLI that makes your work easier, faster, or better. My idea is to create a tool to convert medium posts to dev.to markdown posts preserving the title heading, formatting and inline images the same way. Please give exact steps and prompts by referring to collection from promptz tool if you find anything to enhance the solution, and also what prompts were given to automate the whole command line solution to q and how q responded, log all that in this blog post for submission, along with the code that is generated, then test it on the url https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 and verify the markdown generated, and confirm that all text, image, formatting is same. Amazon Q's Approach Amazon Q first checked for relevant prompts in the promptz.dev collection that might help with this task: ["CLI", "Markdown"] After finding no specific prompts for Markdown conversion, Amazon Q proceeded to design a solution from scratch. It created a Python script that would: Fetch Medium articles using web scraping Extract content while preserving structure Download and properly reference images Convert HTML to Markdown format Generate DEV.to compatible frontmatter Solution Implementation Amazon Q generated the complete medium2dev.py script with these key components: class Medium2Dev: def __init__(self, url, output_dir=None, image_dir=None, api_key=None): """Initialize the converter with the Medium post URL.""" self.url = url self.output_dir = output_dir or os.getcwd() self.image_dir = image_dir or os.path.join(self.output_dir, 'images') self.api_key = api_key

May 3, 2025 - 21:06
 0
Crushing the Command Line with Amazon Q: Building a Medium to DEV.to Converter

This is a submission for the Amazon Q Developer "Quack The Code" Challenge: Crushing the Command Line

What I Built

I built medium2dev, a command-line tool that automatically converts Medium blog posts to DEV.to-compatible markdown format and optionally publishes them directly to DEV.to as draft posts. This tool solves a common problem for technical writers who publish on multiple platforms: maintaining consistent formatting across different publishing platforms.

The tool preserves:

  • Article title and headings
  • Text formatting (bold, italic, etc.)
  • Code blocks with proper syntax highlighting
  • Inline images (automatically downloaded and referenced)
  • Lists and other structural elements
  • Generates proper DEV.to frontmatter

Demo

Here's a demonstration of converting a Medium article to DEV.to format:

# Basic conversion
python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11

# Publish directly to DEV.to as a draft
python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 --publish --api-key YOUR_DEVTO_API_KEY

# Or use environment variable for API key
export DEVTO_API_KEY=your_api_key
python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 --publish

Output:

2025-05-03 15:07:49,696 - medium2dev - INFO - Fetching article from https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11
2025-05-03 15:07:51,100 - medium2dev - INFO - Original Medium content word count: 463
2025-05-03 15:07:51,100 - medium2dev - INFO - Downloaded 0 content images
2025-05-03 15:07:51,116 - medium2dev - INFO - Conversion complete! Output saved to /Users/vivekvelso/Documents/opensource/medium2dev/new/aws-resource-tag-compliance-with-automation-64ae16e42a11.md

Conversion successful! Output saved to: /Users/vivekvelso/Documents/opensource/medium2dev/aws-resource-tag-compliance-with-automation-64ae16e42a11.md
Images saved to: /Users/vivekvelso/Documents/opensource/medium2dev/images

When publishing to DEV.to, it also displays a word count comparison:

Successfully published as draft to DEV.to!

Word Count Comparison:
| Platform | Word Count |
|----------|------------|
| Medium   | 463        |
| DEV.to   | 458        |

The tool successfully:

  1. Fetches the Medium article
  2. Cleans up author metadata and unnecessary UI elements
  3. Removes Medium-specific UI elements like "Listen", "Share", etc.
  4. Converts the content to markdown
  5. Generates a properly formatted DEV.to markdown file
  6. (Optionally) Publishes the article as a draft to DEV.to
  7. Provides a word count comparison between platforms

Code Repository

The code is available on GitHub: medium2dev

Key files:

  • medium2dev.py: The main Python script
  • requirements.txt: Dependencies
  • README.md: Documentation

How I Used Amazon Q Developer

I used Amazon Q Developer CLI to create this entire project from scratch. Here's how the conversation went:

Initial Prompt

I started by explaining my idea to Amazon Q:

I am submitting an idea for this challenge. Crushing the Command Line
Build an automation with Amazon Q Developer CLI that makes your work easier, faster, or better.

My idea is to create a tool to convert medium posts to dev.to markdown posts preserving the title heading, formatting and inline images the same way. Please give exact steps and prompts by referring to collection from promptz tool if you find anything to enhance the solution, and also what prompts were given to automate the whole command line solution to q and how q responded, log all that in this blog post for submission, along with the code that is generated, then test it on the url https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11 and verify the markdown generated, and confirm that all text, image, formatting is same.

Amazon Q's Approach

Amazon Q first checked for relevant prompts in the promptz.dev collection that might help with this task:



["CLI", "Markdown"]


After finding no specific prompts for Markdown conversion, Amazon Q proceeded to design a solution from scratch. It created a Python script that would:

  1. Fetch Medium articles using web scraping
  2. Extract content while preserving structure
  3. Download and properly reference images
  4. Convert HTML to Markdown format
  5. Generate DEV.to compatible frontmatter

Solution Implementation

Amazon Q generated the complete medium2dev.py script with these key components:

class Medium2Dev:
    def __init__(self, url, output_dir=None, image_dir=None, api_key=None):
        """Initialize the converter with the Medium post URL."""
        self.url = url
        self.output_dir = output_dir or os.getcwd()
        self.image_dir = image_dir or os.path.join(self.output_dir, 'images')
        self.api_key = api_key
        # ...

    def fetch_article(self):
        """Fetch the Medium article content."""
        # ...

    def extract_content(self, html_content):
        """Extract the article content from the HTML."""
        # ...

    def download_images(self, content):
        """Download images and update their references in the content."""
        # ...

    def convert_to_markdown(self, content):
        """Convert HTML content to Markdown format suitable for DEV.to."""
        # ...

    def generate_frontmatter(self, title, date):
        """Generate DEV.to frontmatter."""
        # ...

    def convert(self):
        """Convert the Medium post to DEV.to markdown format."""
        # ...

    def publish_to_devto(self, title, markdown_content):
        """Publish the converted markdown as a draft post to DEV.to."""
        # ...

Amazon Q also created the necessary supporting files:

  • requirements.txt with the required dependencies
  • README.md with usage instructions

Improving the Solution

After initial testing, I noticed some issues with the generated markdown:

  1. Author metadata was still being included
  2. The script wasn't properly extracting content images
  3. Medium-specific UI elements like "Listen", "Share", etc. were appearing in the output

I asked Amazon Q to improve the solution, and it made several key enhancements:

Better Content Extraction: Improved the HTML parsing to focus only on the actual article content

   # Create a new div to hold only the content we want
   content_div = soup.new_tag('div')

   # Find all the content sections (paragraphs, headings, code blocks, images)
   content_elements = article_tag.find_all(['p', 'h2', 'h3', 'h4', 'pre', 'figure', 'img', 'blockquote', 'ul', 'ol'])

Metadata Removal: Added more robust filtering to remove author bylines, claps, and other Medium-specific UI elements

   # Skip elements with author info, claps, etc.
   if element.find(string=re.compile(r'clap|follow|min read|sign up|bookmark|Listen|Share')):
       continue

   # Skip elements that just contain "--" or numbers at the beginning
   if element.name == 'p' and re.match(r'^\s*--\s*$|^\s*\d+\s*$', element.text.strip()):
       continue

Enhanced Image Handling: Improved the image extraction and downloading process

   # For Medium images, try to get the full-size version
   if 'miro.medium.com' in img_url:
       # Remove size constraints from URL to get original image
       img_url = re.sub(r'/resize:[^/]+/', '/', img_url)

Better Markdown Cleanup: Added post-processing to clean up Medium-specific elements

   # Remove Medium-specific footer text and links
   markdown = re.sub(r'\n\s*\[.*?\]\(https?://medium\.com/.*?\)\s*\n', '\n\n', markdown)

   # Remove clap indicators and other Medium UI elements
   markdown = re.sub(r'\d+\s*claps?', '', markdown)

CLI Enhancements: Added command-line options for publishing directly to DEV.to

   parser.add_argument('-p', '--publish', action='store_true', help='Publish to DEV.to as draft')
   parser.add_argument('-k', '--api-key', help='DEV.to API key (if not set via DEVTO_API_KEY environment variable)')

DEV.to Integration: Added functionality to publish directly to DEV.to as a draft post

   def publish_to_devto(self, title, markdown_content):
       """Publish the converted markdown as a draft post to DEV.to."""
       if not self.api_key:
           logger.error("No DEV.to API key provided. Skipping publish.")
           return False

       logger.info("Publishing to DEV.to as draft...")

       api_url = "https://dev.to/api/articles"
       headers = {
           "api-key": self.api_key,
           "Content-Type": "application/json"
       }

       # Prepare the article data
       article_data = {
           "article": {
               "title": title,
               "body_markdown": markdown_content,
               "published": False  # Set as draft
           }
       }

       try:
           response = requests.post(api_url, headers=headers, json=article_data)
           response.raise_for_status()
           article_data = response.json()
           logger.info(f"Successfully published draft to DEV.to!")
           return True
       except requests.RequestException as e:
           logger.error(f"Error publishing to DEV.to: {e}")
           return False

Word Count Comparison: Added functionality to compare word counts between Medium and DEV.to versions

   # Calculate the word count of the original content
   content_text = ' '.join([element.get_text() for element in content_div.contents])
   self.medium_word_count = len(content_text.split())
   logger.info(f"Original Medium content word count: {self.medium_word_count}")

   # Calculate DEV.to word count
   devto_word_count = len(re.sub(r'---.*?---\n', '', markdown_content, flags=re.DOTALL).split())

   # Display comparison when publishing
   print("\nWord Count Comparison:")
   print("| Platform | Word Count |")
   print("|----------|------------|")
   print(f"| Medium   | {converter.medium_word_count} |")
   print(f"| DEV.to   | {devto_word_count} |")

Testing the Solution

I tested the improved solution with the provided Medium article URL:

python3 medium2dev.py https://medium.com/aws-in-plain-english/aws-resource-tag-compliance-with-automation-64ae16e42a11

The script successfully:

  1. Downloaded the article content
  2. Removed author metadata and Medium-specific UI elements
  3. Converted the HTML content to clean markdown
  4. Generated a properly formatted DEV.to markdown file with valid tags

I also tested with a different URL format to ensure the solution is robust:

python3 medium2dev.py https://medium.com/@vivek-aws/aws-resource-tag-compliance-with-automation-64ae16e42a11

The tool worked perfectly with both URL formats, demonstrating its flexibility.

Challenges and Solutions

During development, Amazon Q addressed several challenges:

Medium's Dynamic Content: Used proper headers and request parameters to ensure content was fully loaded

   headers = {
       'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
       'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
       # Additional headers...
   }

Content Extraction: Developed a robust approach to extract only the relevant content

   # Try multiple approaches to find the content
   article_tag = soup.find('article')
   if not article_tag:
       article_tag = soup.select_one('div.section-content')
   if not article_tag:
       article_tag = soup.find('div', class_='postArticle-content')

Metadata Removal: Created comprehensive filters to remove Medium-specific UI elements

   # Remove Medium-specific UI elements and metadata
   for element in content.select('.postMetaLockup, .graf--pullquote, .section-divider'):
       if element:
           element.decompose()

DEV.to Integration: Added support for publishing directly to DEV.to using their API

   # Get API key from environment or command line
   api_key = args.api_key or os.environ.get('DEVTO_API_KEY')

DEV.to Tag Format: Fixed tag format to comply with DEV.to requirements

   # Convert "aws in plain english" to "awsinplainenglish"
   potential_tag = re.sub(r'[^a-zA-Z0-9]', '', potential_tag)

Word Count Comparison: Added functionality to compare word counts between platforms without modifying the output file

   # Calculate word counts and display comparison
   medium_word_count = converter.medium_word_count
   devto_word_count = len(re.sub(r'---.*?---\n', '', markdown_content, flags=re.DOTALL).split())

Conclusion

Using Amazon Q Developer CLI, I was able to quickly create a functional tool that solves a real problem for technical writers. The entire process from idea to implementation took just minutes, demonstrating how Amazon Q can accelerate development workflows.

This tool saves significant time for writers who publish on multiple platforms, eliminating the need for manual reformatting and ensuring consistent presentation across platforms. The addition of direct publishing to DEV.to makes the workflow even more streamlined, allowing writers to go from Medium article to DEV.to draft with a single command.