<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kang</title>
    <description>The latest articles on DEV Community by Kang (@cnkang).</description>
    <link>https://dev.to/cnkang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780816%2F11926dcf-a801-498b-9ea0-6a6ccdfe554b.jpeg</url>
      <title>DEV Community: Kang</title>
      <link>https://dev.to/cnkang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cnkang"/>
    <language>en</language>
    <item>
      <title>Early prototype: serve a Markdown variant for bots/agents without touching your app. NGINX converts HTML Markdown only for explicit clients; not intended for APIs/streaming/auth pages. Looking for feedback.</title>
      <dc:creator>Kang</dc:creator>
      <pubDate>Mon, 02 Mar 2026 07:59:52 +0000</pubDate>
      <link>https://dev.to/cnkang/early-prototype-serve-a-markdown-variant-for-botsagents-without-touching-your-app-nginx-converts-4fn0</link>
      <guid>https://dev.to/cnkang/early-prototype-serve-a-markdown-variant-for-botsagents-without-touching-your-app-nginx-converts-4fn0</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7" class="crayons-story__hidden-navigation-link"&gt;I tried Cloudflare’s “Markdown for Agents” idea in NGINX (Rust module) — early prototype&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/cnkang" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780816%2F11926dcf-a801-498b-9ea0-6a6ccdfe554b.jpeg" alt="cnkang profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/cnkang" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Kang
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Kang
                
              
              &lt;div id="story-author-preview-content-3300944" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/cnkang" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780816%2F11926dcf-a801-498b-9ea0-6a6ccdfe554b.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Kang&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 2&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7" id="article-link-3300944"&gt;
          I tried Cloudflare’s “Markdown for Agents” idea in NGINX (Rust module) — early prototype
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nginx"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nginx&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/rust"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;rust&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/markdown"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;markdown&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>showdev</category>
      <category>nginx</category>
      <category>rust</category>
      <category>markdown</category>
    </item>
    <item>
      <title>I tried Cloudflare’s “Markdown for Agents” idea in NGINX (Rust module) — early prototype</title>
      <dc:creator>Kang</dc:creator>
      <pubDate>Mon, 02 Mar 2026 07:21:00 +0000</pubDate>
      <link>https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7</link>
      <guid>https://dev.to/cnkang/i-tried-cloudflares-markdown-for-agents-idea-in-nginx-rust-module-early-prototype-53c7</guid>
      <description>&lt;p&gt;Cloudflare recently shipped &lt;a href="https://blog.cloudflare.com/markdown-for-agents/" rel="noopener noreferrer"&gt;&lt;strong&gt;Markdown for Agents&lt;/strong&gt;&lt;/a&gt;: if a client sends &lt;code&gt;Accept: text/markdown&lt;/code&gt;, Cloudflare can fetch your HTML and return a Markdown variant.&lt;/p&gt;

&lt;p&gt;That idea stuck with me, so I did some &lt;em&gt;vibe coding&lt;/em&gt; over a few evenings and built a &lt;strong&gt;self-hostable NGINX dynamic module&lt;/strong&gt; that does something similar on your own infra.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;very early / starter prototype&lt;/strong&gt; — mainly meant to help people try the workflow and share feedback.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/cnkang/nginx-markdown-for-agents" rel="noopener noreferrer"&gt;https://github.com/cnkang/nginx-markdown-for-agents&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Only when the client explicitly asks for Markdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser (&lt;code&gt;Accept: text/html&lt;/code&gt;) → original HTML&lt;/li&gt;
&lt;li&gt;Agent (&lt;code&gt;Accept: text/markdown&lt;/code&gt;) → NGINX converts upstream HTML to Markdown and returns &lt;code&gt;text/markdown&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No application changes. This sits entirely at the reverse-proxy layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; docs / blogs / news / KB pages&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Not for:&lt;/strong&gt; APIs, streaming responses, or authenticated pages (unless you really know what you’re doing with caching).&lt;/p&gt;


&lt;h2&gt;
  
  
  Why you might care
&lt;/h2&gt;

&lt;p&gt;Agents and LLM tools often fetch full HTML and end up spending tokens on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigation / footer / cookie banners&lt;/li&gt;
&lt;li&gt;layout markup&lt;/li&gt;
&lt;li&gt;scripts and noisy attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Markdown variant can make downstream parsing cheaper and more predictable.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick try (2 minutes)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1) Install (prebuilt module)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/cnkang/nginx-markdown-for-agents/main/tools/install.sh | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: &lt;strong&gt;dynamic modules must match your exact NGINX patch version&lt;/strong&gt; (&lt;code&gt;nginx -v&lt;/code&gt;). If there isn’t a matching build, you may need to compile.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  2) Verify content negotiation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Markdown variant&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: text/markdown"&lt;/span&gt; http://localhost:8080/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'content-type|vary'&lt;/span&gt;
&lt;span class="c"&gt;# expect:&lt;/span&gt;
&lt;span class="c"&gt;# content-type: text/markdown; charset=utf-8&lt;/span&gt;
&lt;span class="c"&gt;# vary: Accept&lt;/span&gt;

&lt;span class="c"&gt;# HTML variant&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: text/html"&lt;/span&gt; http://localhost:8080/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'content-type'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3) See the body
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: text/markdown"&lt;/span&gt; http://localhost:8080/ | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-40&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Minimal NGINX config
&lt;/h2&gt;

&lt;p&gt;Start small — enable it on one route first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;load_module&lt;/span&gt; &lt;span class="nc"&gt;modules/ngx&lt;/span&gt;&lt;span class="s"&gt;_http_markdown_filter_module.so&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;markdown_filter&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/docs/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;markdown_filter&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;# Recommended: avoid upstream compression for clean conversion&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  A few knobs (optional)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fail open&lt;/strong&gt; (recommended for trials): if conversion fails, return original HTML
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;  &lt;span class="k"&gt;markdown_on_error&lt;/span&gt; &lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limit work&lt;/strong&gt; (avoid huge pages)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;  &lt;span class="k"&gt;markdown_max_size&lt;/span&gt; &lt;span class="mi"&gt;10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;markdown_timeout&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metrics endpoint&lt;/strong&gt; (localhost only)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;  &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/markdown-metrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;markdown_metrics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If you cache&lt;/strong&gt; at NGINX/CDN: make sure variants split by &lt;code&gt;Accept&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;  &lt;span class="k"&gt;proxy_cache_key&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$scheme$request_method$host$request_uri$http_accept&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Things that are rough / WIP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It’s early: edge cases will exist (weird HTML, giant pages, odd encodings).&lt;/li&gt;
&lt;li&gt;It’s focused on &lt;strong&gt;HTML → Markdown&lt;/strong&gt; only (not PDFs, not arbitrary binaries).&lt;/li&gt;
&lt;li&gt;Caching needs care (variant keys + auth-aware behavior).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  If you try it… feedback welcome 🙏
&lt;/h2&gt;

&lt;p&gt;If you run into a broken page, a really slow page, or a caching gotcha, I’d appreciate an issue/report with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a sample URL (or anonymized HTML)&lt;/li&gt;
&lt;li&gt;your &lt;code&gt;nginx -v&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;whether upstream is compressed&lt;/li&gt;
&lt;li&gt;any cache/CDN in front&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/cnkang/nginx-markdown-for-agents" rel="noopener noreferrer"&gt;https://github.com/cnkang/nginx-markdown-for-agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare inspiration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blog: &lt;a href="https://blog.cloudflare.com/markdown-for-agents/" rel="noopener noreferrer"&gt;https://blog.cloudflare.com/markdown-for-agents/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/" rel="noopener noreferrer"&gt;https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>showdev</category>
      <category>nginx</category>
      <category>rust</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Showdev: An AI-assisted skill to batch messy changes into Conventional Commits</title>
      <dc:creator>Kang</dc:creator>
      <pubDate>Thu, 19 Feb 2026 10:12:51 +0000</pubDate>
      <link>https://dev.to/cnkang/showdev-an-ai-assisted-skill-to-batch-messy-changes-into-conventional-commits-4965</link>
      <guid>https://dev.to/cnkang/showdev-an-ai-assisted-skill-to-batch-messy-changes-into-conventional-commits-4965</guid>
      <description>&lt;p&gt;Hi DEV community 👋&lt;/p&gt;

&lt;p&gt;I’d like to share a small project I’ve been tinkering with: &lt;strong&gt;conventional-commit-batcher&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is my first “skill” I built with the help of AI tools. I originally made it for myself, then a few programmer friends tried it on real work‑in‑progress branches and told me it helped them turn messy, mixed changes into a cleaner commit history—so I decided to open‑source it and invite more people to try it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: It helps you turn a messy, mixed working tree into a clean, reviewable commit history.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan first&lt;/strong&gt; (generate a Commit Plan by intent)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit in batches&lt;/strong&gt; (often with &lt;code&gt;git add -p&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate commit messages&lt;/strong&gt; (Conventional Commits)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What problem does it try to solve?
&lt;/h2&gt;

&lt;p&gt;When a working tree mixes refactors + fixes + docs + chores, it’s easy (especially for junior devs) to end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one giant “misc” commit&lt;/li&gt;
&lt;li&gt;vague commit messages&lt;/li&gt;
&lt;li&gt;a history that’s hard to review, revert, or &lt;code&gt;git bisect&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted something that nudges me toward a more reviewable, traceable history—without turning it into a big ceremony.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does (in one line)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Plan-first commit batching + commit message validation&lt;/strong&gt; — it helps you organize mixed diffs into logical, reviewable Conventional Commits, and validates messages before they land.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works (high level)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan first&lt;/strong&gt;: produce a &lt;em&gt;Commit Plan&lt;/em&gt; that groups changes by intent (e.g., refactor vs fix vs docs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch staging&lt;/strong&gt;: stage changes batch‑by‑batch (often with &lt;code&gt;git add -p&lt;/code&gt;) instead of “add everything”.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message validation&lt;/strong&gt;: enforce Conventional Commits formatting with an executable validator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety gates&lt;/strong&gt;: stop early if something looks risky (conflict markers, sensitive strings, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: a Commit Plan (by intent)
&lt;/h3&gt;

&lt;p&gt;Here’s a tiny example of what “plan first” looks like:&lt;/p&gt;

&lt;p&gt;1) &lt;code&gt;refactor(auth): simplify token parsing&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changes: &lt;code&gt;src/auth/*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;code&gt;fix(api): handle empty response safely&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changes: &lt;code&gt;src/api/*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;code&gt;docs: update setup notes&lt;/code&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changes: &lt;code&gt;docs/*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repo / quick try
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/cnkang/conventional-commit-batcher" rel="noopener noreferrer"&gt;https://github.com/cnkang/conventional-commit-batcher&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use the Vercel &lt;code&gt;skills&lt;/code&gt; ecosystem, you can try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add cnkang/conventional-commit-batcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer no agent tooling? You can still use the validator and commit‑msg hook examples in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tiny before/after example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before (typical messy branch)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;update stuff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fixes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wip&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After (same work, clearer history)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;refactor(auth): simplify token parsing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix(api): handle empty response safely&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs: update setup notes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(You can imagine how much easier it becomes to review, revert, or bisect.)&lt;/p&gt;

&lt;h2&gt;
  
  
  A small favor: if you have a minute to try it…
&lt;/h2&gt;

&lt;p&gt;I’d really appreciate any thoughts (especially if you can try it on a “messy branch”):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Batching by intent:&lt;/strong&gt; does it actually help split mixed changes into logical commits?&lt;br&gt;&lt;br&gt;
If not, what kinds of changes are hardest to separate (tests + refactor, formatting + logic, lockfiles…)?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Team conventions:&lt;/strong&gt; what rules do you follow (types/scopes, subject length, issue references, breaking changes)?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Junior-friendly guardrails:&lt;/strong&gt; which checks feel helpful, and which feel too strict?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-language commit messages:&lt;/strong&gt; do you write commits in languages other than English?&lt;br&gt;&lt;br&gt;
If yes, would you prefer &lt;code&gt;type(scope)&lt;/code&gt; in English + localized subject/body, or fully localized messages (and how should validation adapt)?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for taking a look 🙇&lt;br&gt;&lt;br&gt;
If you try it and it helps (or fails), I’d love to hear what happened—either way.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>git</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
