<?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: Hwani-Net</title>
    <description>The latest articles on DEV Community by Hwani-Net (@hwaninet).</description>
    <link>https://dev.to/hwaninet</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%2F3819699%2F34a9760a-14b2-4b6a-996b-d06449ea2a17.png</url>
      <title>DEV Community: Hwani-Net</title>
      <link>https://dev.to/hwaninet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hwaninet"/>
    <language>en</language>
    <item>
      <title>How I Built drift-guard: A CLI to Stop AI Agents from Destroying Your Design</title>
      <dc:creator>Hwani-Net</dc:creator>
      <pubDate>Thu, 12 Mar 2026 07:24:46 +0000</pubDate>
      <link>https://dev.to/hwaninet/how-i-built-drift-guard-a-cli-to-stop-ai-agents-from-destroying-your-design-3egc</link>
      <guid>https://dev.to/hwaninet/how-i-built-drift-guard-a-cli-to-stop-ai-agents-from-destroying-your-design-3egc</guid>
      <description>&lt;h2&gt;
  
  
  The Real Problem Nobody's Talking About
&lt;/h2&gt;

&lt;p&gt;It's 2026 and AI coding agents are everywhere. Cursor, Claude Code, Codex, GitHub Copilot, Cline — they can build features in minutes.&lt;/p&gt;

&lt;p&gt;But here's the dirty secret: &lt;strong&gt;they can't stop themselves from destroying your design.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You spend hours perfecting a landing page. Beautiful purple gradients. Carefully chosen Inter font. Consistent 8px border radius system. You hand it to an AI agent with a simple request: &lt;em&gt;"Add a contact form."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The form gets added. But your primary color is now blue. Your font is now system-ui. Your border radius is 4px on some elements, 12px on others. The header that was a semantic tag is now a generic div.&lt;/p&gt;

&lt;p&gt;I call this &lt;strong&gt;Design Drift&lt;/strong&gt; — and after experiencing it for the 50th time, I built a tool to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing drift-guard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;drift-guard&lt;/strong&gt; is a zero-config CLI that protects your UI from AI coding agents' design changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎮 &lt;strong&gt;&lt;a href="https://hwani-net.github.io/drift-guard/" rel="noopener noreferrer"&gt;Try the interactive demo →&lt;/a&gt;&lt;/strong&gt; See it in action, right in your browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fpzsme54vzl31h46tggqw.png" class="article-body-image-wrapper"&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%2Farticles%2Fpzsme54vzl31h46tggqw.png" alt="drift-guard CLI demo" width="800" height="930"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Lock your design&lt;/span&gt;
npx drift-guard init &lt;span class="nt"&gt;--from&lt;/span&gt; stitch-design.html

&lt;span class="c"&gt;# Step 2: Tell AI agents about the rules&lt;/span&gt;
npx drift-guard rules

&lt;span class="c"&gt;# Step 3: After AI makes changes, check for drift&lt;/span&gt;
npx drift-guard check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What It Protects
&lt;/h3&gt;

&lt;p&gt;drift-guard tracks &lt;strong&gt;7 categories&lt;/strong&gt; of design tokens plus &lt;strong&gt;DOM structure&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;What it locks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🎨 Colors&lt;/td&gt;
&lt;td&gt;color, background-color, border-color, CSS variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📝 Fonts&lt;/td&gt;
&lt;td&gt;font-family, font-size, font-weight, line-height&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📏 Spacing&lt;/td&gt;
&lt;td&gt;margin, padding, gap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌫️ Shadows&lt;/td&gt;
&lt;td&gt;box-shadow, text-shadow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⭕ Radius&lt;/td&gt;
&lt;td&gt;border-radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📐 Layout&lt;/td&gt;
&lt;td&gt;display, flex-direction, justify-content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✨ Effects&lt;/td&gt;
&lt;td&gt;backdrop-filter, filter, animation, transition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏗️ Structure&lt;/td&gt;
&lt;td&gt;Semantic tags, DOM depth, layout hash, child sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How It Works Under the Hood
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Token Extraction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;drift-guard uses &lt;a href="https://github.com/csstree/csstree" rel="noopener noreferrer"&gt;css-tree&lt;/a&gt; for CSS parsing and &lt;a href="https://github.com/cheeriojs/cheerio" rel="noopener noreferrer"&gt;cheerio&lt;/a&gt; for HTML. It walks the AST and extracts every design-relevant declaration into categorized tokens.&lt;/p&gt;

&lt;p&gt;For Stitch/Tailwind projects, it also parses inline Tailwind config tags to extract theme values — a pattern that pure CSS parsers miss completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Structure Fingerprinting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;New in v0.2.0 — drift-guard computes a structural fingerprint using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic tag counts (header, nav, main, section, footer)&lt;/li&gt;
&lt;li&gt;Max DOM nesting depth&lt;/li&gt;
&lt;li&gt;SHA-256 hash of flex/grid layout elements&lt;/li&gt;
&lt;li&gt;SHA-256 hash of body's direct child sequence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This catches AI agents that "flatten" your layout or replace semantic HTML with generic divs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AI Agent Rules Generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rules command generates protection files for 5 tools simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx drift-guard rules
&lt;span class="c"&gt;# ✅ .cursorrules&lt;/span&gt;
&lt;span class="c"&gt;# ✅ CLAUDE.md&lt;/span&gt;
&lt;span class="c"&gt;# ✅ AGENTS.md&lt;/span&gt;
&lt;span class="c"&gt;# ✅ .github/copilot-instructions.md&lt;/span&gt;
&lt;span class="c"&gt;# ✅ .clinerules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file contains your locked tokens in a format the AI tool understands, with instructions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;### Colors (DO NOT CHANGE)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;--tw-primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#8b5cf6&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;--tw-background-dark&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#101622&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The CLI vs MCP Decision
&lt;/h2&gt;

&lt;p&gt;This was the biggest architectural decision. When I started, I built both a CLI and an MCP (Model Context Protocol) server wrapper.&lt;/p&gt;

&lt;p&gt;Then I looked at the numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Token overhead&lt;/th&gt;
&lt;th&gt;Cost per month*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;~10,000+ tokens per tool&lt;/td&gt;
&lt;td&gt;~$55/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;0 tokens&lt;/td&gt;
&lt;td&gt;~$3/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Based on Scalekit's benchmark for equivalent task volume&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the ongoing MCP token bloat debate (55K+ tokens consumed before you start coding), keeping drift-guard as a pure CLI felt right. AI agents already know how to run shell commands — no schema registration needed.&lt;/p&gt;

&lt;p&gt;I wrote this up as &lt;strong&gt;ADR-007: CLI-First Strategy&lt;/strong&gt; in the project. The MCP code exists in the repo but isn't published to npm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stale Snapshot Warning (v0.2.2)
&lt;/h2&gt;

&lt;p&gt;A subtle but important feature: if your snapshot is 7+ days old, drift-guard warns you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️  Snapshot is 11 days old (created 2026-03-01).
   If your design has changed, run: drift-guard init --from latest.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Old snapshots = false positives. This nudge prevents teams from forgetting to update after intentional design changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD Integration
&lt;/h2&gt;

&lt;p&gt;drift-guard exits with code 1 when drift exceeds the threshold — perfect for CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/design-guard.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Design Guard&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;check-drift&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx drift-guard check --ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add drift-guard hook install locally for pre-commit blocking — drifted commits never even get pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Testing
&lt;/h2&gt;

&lt;p&gt;I tested drift-guard against a real Stitch-generated landing page (225 lines of HTML with Tailwind config):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initialize from Stitch HTML&lt;/td&gt;
&lt;td&gt;✅ 9 tokens + structure fingerprint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No change → check&lt;/td&gt;
&lt;td&gt;✅ 0% drift, exit 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change primary color&lt;/td&gt;
&lt;td&gt;✅ 11.11% drift detected, exit 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace semantic tag with div&lt;/td&gt;
&lt;td&gt;✅ Structure drift detected, exit 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate AI rules&lt;/td&gt;
&lt;td&gt;✅ 5 files for all major AI tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accept changes via snapshot update&lt;/td&gt;
&lt;td&gt;✅ New baseline, 0% drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale snapshot (11 days)&lt;/td&gt;
&lt;td&gt;✅ Warning displayed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;130 automated tests (81 unit + 49 E2E) passing on Node 18/20/22.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Figma/Stitch API integration&lt;/strong&gt; for automated snapshot updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold per category&lt;/strong&gt; (allow color drift but block font changes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tree diff for DOM&lt;/strong&gt; (show exactly which elements changed, not just hashes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code extension&lt;/strong&gt; with inline drift warnings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx drift-guard init &lt;span class="nt"&gt;--from&lt;/span&gt; your-design.html
npx drift-guard rules
npx drift-guard check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Demo&lt;/strong&gt;: &lt;a href="https://hwani-net.github.io/drift-guard/" rel="noopener noreferrer"&gt;hwani-net.github.io/drift-guard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Hwani-Net/drift-guard" rel="noopener noreferrer"&gt;Hwani-Net/drift-guard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/@stayicon/drift-guard" rel="noopener noreferrer"&gt;@stayicon/drift-guard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you experienced Design Drift with AI coding agents? I'd love to hear your stories and what you'd want from a tool like this.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
