<?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.us-east-2.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>From a Personal Hermes Memory Plugin to a Skill, MCP Server, and Official Listings in Just 8 Days</title>
      <dc:creator>Kang</dc:creator>
      <pubDate>Wed, 14 Oct 2026 01:54:29 +0000</pubDate>
      <link>https://dev.to/cnkang/from-a-personal-hermes-memory-plugin-to-a-skill-mcp-server-and-official-listings-in-just-8-days-2g31</link>
      <guid>https://dev.to/cnkang/from-a-personal-hermes-memory-plugin-to-a-skill-mcp-server-and-official-listings-in-just-8-days-2g31</guid>
      <description>&lt;p&gt;I originally just wanted to switch to a long-term memory solution for the Hermes Agent to address the issue of running out of free quota on Mem0 SaaS. But the project kept expanding, and in the end, I ended up developing a Hermes plugin, a standalone runtime, an Agent Skill, an MCP server, and publishing it all on PyPI. The plugin was even listed in the official Hermes directory, and the MCP made it into the Official MCP Registry.&lt;/p&gt;

&lt;p&gt;It took about 8 days—from creating the repository on October 6 to basically finishing all this work on October 13.&lt;/p&gt;

&lt;p&gt;Looking back, it’s actually quite interesting, so I’ll write a chronological account to document how this project snowballed into something much bigger.&lt;/p&gt;

&lt;p&gt;Here’s the main project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cnkang/hermes-plugin-qdrant-memory" rel="noopener noreferrer"&gt;https://github.com/cnkang/hermes-plugin-qdrant-memory&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background: I originally just wanted to save on SaaS costs
&lt;/h2&gt;

&lt;p&gt;I usually use Hermes Agent to assist with development. Over time, it has learned a lot about project context, technical decisions, and my personal preferences. This feature is actually quite useful, especially when working across sessions—I don’t have to explain the background all over again every time.&lt;/p&gt;

&lt;p&gt;I used to use Mem0 Platform, but after accumulating several thousand entries, I ran out of free storage.&lt;/p&gt;

&lt;p&gt;It’s not that Mem0 isn’t useful—it’s just that I feel long-term memory is something best kept under my own control. Models can be replaced, and agents can be switched, but it’s best not to have my accumulated memories locked into a specific SaaS platform.&lt;/p&gt;

&lt;p&gt;So I started looking into alternatives.&lt;/p&gt;

&lt;p&gt;For the local embedding model, I chose Ollama’s &lt;code&gt;qwen3-embedding:4b&lt;/code&gt;, and for storage, I went with Qdrant. Qdrant works on local machines, self-hosted environments, and in the cloud, making it easy to switch deployment methods later on.&lt;/p&gt;

&lt;p&gt;Exporting from Mem0 took some effort—I ran into pagination, rate limits, and HTTP 429 errors—but I eventually managed to compile over 2,700 unique records.&lt;/p&gt;

&lt;p&gt;My initial plan was simply to migrate these memories to Qdrant and then find a way for Hermes to read and write them.&lt;/p&gt;

&lt;p&gt;Later, while researching Hermes’ plugin mechanism, I discovered that it already provides a native &lt;code&gt;MemoryProvider&lt;/code&gt; interface, and model calls can reuse Hermes’ own configuration.&lt;/p&gt;

&lt;p&gt;So there was no need to go through an extra layer of Mem0.&lt;/p&gt;

&lt;p&gt;I decided to just build a native plugin instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 1 | October 6: First, build the foundation
&lt;/h2&gt;

&lt;p&gt;The goal for the first day was pretty simple: get Hermes to store long-term memory in Qdrant so it could be retrieved even after switching sessions.&lt;/p&gt;

&lt;p&gt;I created the repository cnkang/hermes-plugin-qdrant-memory and started working on the first version.&lt;/p&gt;

&lt;p&gt;I stuck to a few design principles:&lt;/p&gt;

&lt;p&gt;First, I wouldn’t touch Hermes’ core code and would stick to its public plugin interfaces as much as possible. That way, when Hermes is updated in the future, I won’t have to modify the main project every time.&lt;/p&gt;

&lt;p&gt;Second, we won’t configure a separate LLM; by default, it will inherit the model currently used by Hermes. Embeddings, however, will be configured independently, with local Ollama as the preferred option.&lt;/p&gt;

&lt;p&gt;Furthermore, Qdrant should not be limited to a single deployment method. It must support local embedded deployment, self-hosted servers, and Qdrant Cloud.&lt;/p&gt;

&lt;p&gt;The first version quickly established the basic framework: automatic memory extraction, vectorization, storage, search, updates, deletion, and data migration from Mem0.&lt;/p&gt;

&lt;p&gt;It seems to be working already.&lt;/p&gt;

&lt;p&gt;But upon closer reflection*&lt;em&gt;, being able to store memories and being able to confidently entrust long-term memories to it are two different things.&lt;/em&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 2 | October 7: Started fixing those subtle but critical issues
&lt;/h2&gt;

&lt;p&gt;This day was mainly spent tinkering with various edge cases.&lt;/p&gt;

&lt;p&gt;For example, an AI Agent’s conversations aren’t necessarily limited to a single human user—they might also involve bots, other agents, or shared sessions.&lt;/p&gt;

&lt;p&gt;If a bot’s message is mistakenly saved as a human user’s message—or if it directly inherits the memory permissions of the previous user—that’s a problem.&lt;/p&gt;

&lt;p&gt;Long-term memory is different from ordinary chat context—a single mistake could affect every subsequent conversation.&lt;/p&gt;

&lt;p&gt;So we began implementing identity verification, memory scope isolation, and permission boundaries between different sessions.&lt;/p&gt;

&lt;p&gt;At the same time, we discovered that configuration initialization, write locks, and CLI error messages also needed refinement.&lt;/p&gt;

&lt;p&gt;For example, in Qdrant Embedded mode, two processes should not be allowed to simultaneously access the same storage. When lock conflicts occur, we should clearly inform the user which part of the process went wrong, rather than just throwing out a bunch of Python tracebacks.&lt;/p&gt;

&lt;p&gt;My takeaway from this day is: once the first version of the features is built, real software engineering has only just begun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 3 | October 8: Writing data to memory isn’t hard—the hard part is not losing it
&lt;/h2&gt;

&lt;p&gt;I began focusing on persistence and exception recovery.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;/p&gt;

&lt;p&gt;An Agent retrieved a memory and was preparing to write it to Qdrant when the network connection was lost. What should happen the next time it starts up?&lt;/p&gt;

&lt;p&gt;Another example: Qdrant has successfully written the memory, but the program exits before it has a chance to record the success status. If the operation is executed again after a restart, will it generate duplicate memories?&lt;/p&gt;

&lt;p&gt;Here’s an even more interesting scenario:&lt;/p&gt;

&lt;p&gt;A user has just deleted a memory, but a previous failed write task is still waiting to be retried. After a restart, that old memory gets written back.&lt;/p&gt;

&lt;p&gt;This is a bit unsettling. We clearly made the AI forget, yet it remembered again.&lt;/p&gt;

&lt;p&gt;Finally, we introduced and continuously refined the SQLite WAL Ledger to record pending events, write operations, and execution status. Combined with deterministic IDs, idempotent writes, and delete barriers, this allows operations to recover after abnormal interruptions while minimizing the risk of old writes bringing back memories that have already been deleted.&lt;/p&gt;

&lt;p&gt;The migration process was also refined.&lt;/p&gt;

&lt;p&gt;Importing Mem0 cannot simply involve a loop of write operations; it must also support dry-run mode, progress display, resume-from-breakpoint, failure retries, and final validation.&lt;/p&gt;

&lt;p&gt;After completing these tasks, it feels like this project is no longer just a simple Qdrant wrapper; it’s starting to take on the characteristics of a standalone Memory Runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 4 | October 9: Since others will be using this, we need to get it right
&lt;/h2&gt;

&lt;p&gt;The first three days were mainly about ensuring “it works properly.”&lt;/p&gt;

&lt;p&gt;On the fourth day, I started thinking about whether “others would dare to use it.”&lt;/p&gt;

&lt;p&gt;First up was memory management.&lt;/p&gt;

&lt;p&gt;Originally, you could search, modify, and delete items by ID, but without a complete list, it’s hard to manage the system effectively—especially if you want to know exactly what it’s keeping track of.&lt;/p&gt;

&lt;p&gt;So we added a storage list, portable JSON export, and the ability to perform bulk deletions scoped by user or agent.&lt;/p&gt;

&lt;p&gt;I believe that, in the long term, a memory system must at least allow users to see what has been stored, export that data, and delete it explicitly.&lt;/p&gt;

&lt;p&gt;Next, I continued to examine the relationship between deletion and restoration to prevent certain unfinished tasks from bypassing the deletion process.&lt;/p&gt;

&lt;p&gt;Next came CI, security scans, dependency checks, cross-platform compatibility, and various documentation.&lt;/p&gt;

&lt;p&gt;Linux, macOS, Windows, Python 3.11, 3.14, and different versions of Hermes all needed to be taken into account.&lt;/p&gt;

&lt;p&gt;I also went through the English documentation to format it in the Simplified Technical English style, striving to make the installation steps and operating instructions as straightforward and unambiguous as possible.&lt;/p&gt;

&lt;p&gt;Coding Agent was a huge help during this phase, but I’m increasingly realizing that as AI becomes faster at writing code, we need to devote more effort to reviewing and validating design boundaries.&lt;/p&gt;

&lt;p&gt;Don’t just release something the moment the CI runs green.&lt;/p&gt;

&lt;p&gt;Especially with something like this that stores user data—it never hurts to be a little more cautious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 5 | October 10: Released the first version and submitted it for official inclusion
&lt;/h2&gt;

&lt;p&gt;I finally felt ready to release v0.1.0 for others to try out.&lt;/p&gt;

&lt;p&gt;Of course, I didn’t dare call it a mature, production-grade memory system—I still classified it as a Limited Technical Preview.&lt;/p&gt;

&lt;p&gt;I put together Chinese and English README files, installation instructions, and usage examples, and even recorded a terminal GIF demonstrating how to save a preference in Hermes, start a new session, and then retrieve it from Qdrant.&lt;/p&gt;

&lt;p&gt;I’d always just been testing it over and over in my own development environment, but this time I gave it some serious thought: Could someone who’s never installed this plugin before get it up and running smoothly by following the documentation?&lt;/p&gt;

&lt;p&gt;Then I applied to join the Plugin Catalog on Hermes’ upstream project.&lt;/p&gt;

&lt;p&gt;Hermes actually already has other Qdrant-related memory plugins, so I needed to make it clear that mine isn’t just a re-packaging of an existing one.&lt;/p&gt;

&lt;p&gt;The main difference is that it uses an LLM to extract facts and preferences rather than simply saving the raw conversation; it supports three storage methods—Embedded, Server, and Cloud; and it also offers capabilities such as persistent ledger, fault recovery, and Mem0 migration.&lt;/p&gt;

&lt;p&gt;Of course, there are trade-offs—for example, automatic extraction increases LLM calls, and the overall implementation is more complex.&lt;/p&gt;

&lt;p&gt;Different approaches have their own use cases; it’s not a matter of one being inherently better than the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 6 | October 11: Officially Listed in Hermes, but We Still Need to Verify If the Installation Actually Works Well
&lt;/h2&gt;

&lt;p&gt;One particularly exciting development today was that Qdrant Memory was officially added to the Hermes Plugin Catalog.&lt;/p&gt;

&lt;p&gt;Note that it’s a Community Plugin—we’ll continue maintaining the code ourselves; it hasn’t been merged into the Hermes core.&lt;/p&gt;

&lt;p&gt;But at least users won’t have to mess around with the plugin directory themselves anymore; they can simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hermes plugins &lt;span class="nb"&gt;install &lt;/span&gt;qdrant-memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to install it from the official catalog.&lt;/p&gt;

&lt;p&gt;However, being included doesn’t mean the job is done.&lt;/p&gt;

&lt;p&gt;I specifically used a brand-new Hermes Profile to run through the entire process again—from installation from the official repository, to activation, dependency preparation, initialization, actual data writing, cross-session recall, recovery after a restart, and deletion.&lt;/p&gt;

&lt;p&gt;Just because it runs in the development environment doesn’t mean users will have no issues when installing from scratch.&lt;/p&gt;

&lt;p&gt;The official repository also has its own version review and commit locking mechanisms; future upgrades must follow its rules—you can’t simply assume that a new version on PyPI means the official repository has been updated.&lt;/p&gt;

&lt;p&gt;After going through this entire process, I actually came to realize that it’s quite necessary.&lt;/p&gt;

&lt;p&gt;Releasing open-source software isn’t just about creating a tag; you also need to be able to clearly explain exactly which version users are actually installing.&lt;/p&gt;

&lt;p&gt;At this point, I thought the matter was pretty much settled.&lt;/p&gt;

&lt;p&gt;But then a new idea popped into my head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 7 | October 12: Why is it limited to Hermes?
&lt;/h2&gt;

&lt;p&gt;Taking a closer look at what I’ve built so far—Qdrant storage, embedding, deduplication, Ledger, fault recovery, and migration—most of these features are already in place.&lt;/p&gt;

&lt;p&gt;These capabilities don’t necessarily have to rely on Hermes.&lt;/p&gt;

&lt;p&gt;I also use development tools like Codex and OpenCode on a regular basis. Wouldn’t it be more interesting if different agents could share this memory infrastructure?&lt;/p&gt;

&lt;p&gt;So I started expanding it again.&lt;/p&gt;

&lt;p&gt;First, I created a generic &lt;strong&gt;Agent Skill&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cnkang/skills/tree/main/qdrant-memory" rel="noopener noreferrer"&gt;https://github.com/cnkang/skills/tree/main/qdrant-memory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Skill doesn’t directly implement memory storage; instead, it tells the Agent how to search for, save, maintain, and migrate memories; which operations require confirmation; how to prevent unauthorized access; and how to select available tool interfaces.&lt;/p&gt;

&lt;p&gt;Subsequently, we separated the runtime and added a CLI and a standard stdio MCP interface that do not depend on Hermes.&lt;/p&gt;

&lt;p&gt;This allows other MCP-compatible agents to manipulate memories using four tools: Search, Add, Update, and Delete.&lt;/p&gt;

&lt;p&gt;Multiple interfaces share the same Runtime, eliminating the need to duplicate storage and recovery logic.&lt;/p&gt;

&lt;p&gt;Of course, there are limitations: Hermes’ native plugins can automatically extract memories using their own lifecycle, whereas standard MCPs only provide explicit operations and do not automatically remember every round of conversation.&lt;/p&gt;

&lt;p&gt;Different Agents can reuse the memory provided that authorization and scope are consistent, but the current version still requires coordinated writes; it should not be treated as a distributed service where any number of Agents can write simultaneously.&lt;/p&gt;

&lt;p&gt;This day effectively expanded the original Hermes-specific plugin into a memory foundation that can be used by different agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 8 | October 13: Going All Out—Skill, MCP, and PyPI Are All Set
&lt;/h2&gt;

&lt;p&gt;At this stage, since the Runtime can already run independently of Hermes, the distribution method should keep pace.&lt;/p&gt;

&lt;p&gt;First, we officially released the Runtime to PyPI:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hermes-plugin-qdrant-memory&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then, I set up a separate MCP distribution repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cnkang/qdrant-memory-mcp" rel="noopener noreferrer"&gt;https://github.com/cnkang/qdrant-memory-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of duplicating the Python business logic code here, we made the MCP distribution package depend on the original Runtime. This way, when fixing storage or memory handling issues in the future, we won’t need to constantly synchronize changes between two separate implementations.&lt;/p&gt;

&lt;p&gt;As it turned out, an issue was actually detected during release preparations.&lt;/p&gt;

&lt;p&gt;If the similarity calculated by the embedding for two different text segments is very high—even 1.0—the original deduplication mechanism might mistakenly treat them as the same memory.&lt;/p&gt;

&lt;p&gt;When automatically extracting memories from a conversation, the LLM can determine whether facts are duplicated or conflicting. However, when a user explicitly calls &lt;code&gt;Add&lt;/code&gt; to save a piece of content, different text segments should not be merged without authorization.&lt;/p&gt;

&lt;p&gt;So we made another round of changes, separating the deduplication rules for automatically extracted and explicitly written content.&lt;/p&gt;

&lt;p&gt;Finally, we released Runtime 0.2.1 and MCP 0.1.1.&lt;/p&gt;

&lt;p&gt;The General-Purpose Skill was also updated to version 0.2.4, adding operational guidelines for standalone installation, MCP usage, migration, and cross-agent memory sharing.&lt;/p&gt;

&lt;p&gt;The MCP Server was subsequently added to the Official MCP Registry.&lt;/p&gt;

&lt;p&gt;All in all, from the Hermes Plugin, Runtime, CLI, and Skills to MCP, PyPI, and the official catalog, we’ve essentially covered all available distribution channels.&lt;/p&gt;

&lt;p&gt;The only remaining task is to finalize the version lock for the official Hermes Catalog. The 0.2.1 update has been submitted to the upstream repository and is still undergoing the review process.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Looking back, these eight days of development didn’t strictly follow the initial plan.&lt;/p&gt;

&lt;p&gt;On the first day, I just wanted to replace Mem0.&lt;/p&gt;

&lt;p&gt;On the second day, I realized that memory permissions and identity isolation couldn’t be handled carelessly.&lt;/p&gt;

&lt;p&gt;On the third day, I started worrying about crashes, duplicate writes, and data recovery after deletion.&lt;/p&gt;

&lt;p&gt;On the fourth day, I realized that since we were going open source, we needed to address data management, security, and testing.&lt;/p&gt;

&lt;p&gt;On the fifth day, we were finally ready to release it.&lt;/p&gt;

&lt;p&gt;On the sixth day, it was added to the official Hermes repository.&lt;/p&gt;

&lt;p&gt;On the seventh day, I realized that since I had a standalone foundation, it would be a waste not to let other agents use it.&lt;/p&gt;

&lt;p&gt;On the eighth day, I went ahead and set up Skill, MCP, PyPI, and the Registry all at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughout the process, it was basically a case of solving one problem only to discover the next one worth tackling.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also had one pretty obvious realization: when developing with Coding Agent these days, the code itself is often no longer the most time-consuming part.&lt;/p&gt;

&lt;p&gt;What really takes mental effort is clarifying requirements, identifying edge cases, defining behavioral boundaries, and determining whether the implementation generated by the AI is actually reliable.&lt;/p&gt;

&lt;p&gt;Sometimes a feature can be written in just a few tens of minutes, but to ensure it works without issues after a restart, timeout, repeated operations, version upgrades, or cross-platform installation, it may require many rounds of review.&lt;/p&gt;

&lt;p&gt;This week, I’ve gone through the cycle of writing code, reviewing it, adding tests, updating documentation, and reviewing it again—I’ve lost count of how many times.&lt;/p&gt;

&lt;p&gt;Of course, it’s still too early to say the system is fully mature.&lt;/p&gt;

&lt;p&gt;The single-writer restriction is still in place; Hybrid Search, Rerank, and the Web UI haven’t been implemented yet, and there’s still plenty of room for improvement in the recall quality of long-term memory.&lt;/p&gt;

&lt;p&gt;However, at least for me personally, that initial problem has been solved: there’s no longer a need to completely tie the agent’s long-term memory to a single SaaS service.&lt;/p&gt;

&lt;p&gt;What’s even more interesting is that what started as a small “reinventing the wheel” project for Hermes has ultimately turned into a universal foundation that other agents can also integrate with.&lt;/p&gt;

&lt;p&gt;Building your own wheels—it’s definitely easy to get carried away.&lt;/p&gt;

&lt;p&gt;The project link is at the end. If you’re interested, feel free to give it a try, share your suggestions, or drop a star.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/cnkang/hermes-plugin-qdrant-memory" rel="noopener noreferrer"&gt;Hermes Qdrant Memory (main project)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://hermes-agent.nousresearch.com/docs/plugins/qdrant-memory" rel="noopener noreferrer"&gt;Hermes Official Plugin Directory&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/cnkang/skills/tree/main/qdrant-memory" rel="noopener noreferrer"&gt;Qdrant Memory Skill&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/cnkang/qdrant-memory-mcp" rel="noopener noreferrer"&gt;Qdrant Memory MCP&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://registry.modelcontextprotocol.io/?q=io.github.cnkang%2Fqdrant-memory" rel="noopener noreferrer"&gt;Official MCP Registry&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pypi.org/project/hermes-plugin-qdrant-memory/" rel="noopener noreferrer"&gt;PyPI Runtime&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pypi.org/project/qdrant-memory-mcp/" rel="noopener noreferrer"&gt;PyPI MCP&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All open source, MIT License.&lt;/p&gt;

&lt;p&gt;Next, I’ll continue to refine it bit by bit. After all, when it comes to long-term memory, simply remembering something is just the first step—remembering it correctly, being able to forget it when necessary, and being able to transfer that knowledge are the parts truly worth the effort.&lt;/p&gt;

</description>
      <category>hermes</category>
      <category>agentskills</category>
      <category>mcp</category>
      <category>vibecoding</category>
    </item>
    <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;&amp;nbsp;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;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;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>
