<?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: Evgenii Zinner</title>
    <description>The latest articles on DEV Community by Evgenii Zinner (@evgenii_zinner).</description>
    <link>https://dev.to/evgenii_zinner</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%2F1903224%2F3c77445f-e972-493c-a96a-e4962b3c7bb2.jpg</url>
      <title>DEV Community: Evgenii Zinner</title>
      <link>https://dev.to/evgenii_zinner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evgenii_zinner"/>
    <language>en</language>
    <item>
      <title>How I Built an Invisible Telegram CRM for My Girlfriend's Nail Studio on Cloudflare Workers, D1, and KV</title>
      <dc:creator>Evgenii Zinner</dc:creator>
      <pubDate>Mon, 17 Aug 2026 12:20:24 +0000</pubDate>
      <link>https://dev.to/evgenii_zinner/how-i-built-an-invisible-telegram-crm-for-my-girlfriends-nail-studio-on-cloudflare-workers-d1-1338</link>
      <guid>https://dev.to/evgenii_zinner/how-i-built-an-invisible-telegram-crm-for-my-girlfriends-nail-studio-on-cloudflare-workers-d1-1338</guid>
      <description>&lt;p&gt;A few months ago, my girlfriend Anna started running into a bottleneck that hits almost every solo service business: managing client bookings manually over personal chat was eating up hours of her day.&lt;/p&gt;

&lt;p&gt;Instead of signing up for an off-the-shelf booking SaaS with clunky external forms or hacking together an expensive LLM bot, I built a lightweight, serverless CRM that runs directly inside Telegram Business. It uses Cloudflare Workers, D1 (edge SQLite), KV, and a vanilla TypeScript Mini App.&lt;/p&gt;

&lt;p&gt;The system processes bookings deterministically in under a millisecond, costs practically zero to run, and works silently inside normal client conversations without slash commands or bot menus.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real-World Friction: Lost Leads, Ghost Clients, and Calendar Juggling
&lt;/h2&gt;

&lt;p&gt;On paper, running a private manicure studio is simple: do great nails, get booked, get paid. In reality, handling bookings through direct messages gets messy fast.&lt;/p&gt;

&lt;p&gt;When you run local ads, inquiries land at all hours. People message asking for prices or open dates while Anna is in the middle of a 3-hour gel set, working out, or asleep. When an inquiry sits unanswered for two hours, potential clients just move to the next salon on Instagram. If you're paying for traffic, slow response times directly burn your ad budget.&lt;/p&gt;

&lt;p&gt;Once a conversation starts, two bigger headaches appear:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Constant Context Switching&lt;/strong&gt;: Scheduling meant constantly jumping out of Telegram into Google Calendar, checking open slots, typing out times, waiting for replies, hopping back to block the slot, and setting manual reminders. Doing this across 20+ active chats a day destroys your focus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Ghost Client" Problem&lt;/strong&gt;: Telegram lets users wipe entire chat histories for both sides with a single tap. Problematic clients would book prime Saturday slots, no-show, and immediately delete the chat. Two weeks later, they'd message again as if nothing happened. Because the conversation history was gone, Anna had no way to track repeat offenders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The initial plan was modest: set up a quick auto-responder to send a price list photo album when Anna was away so new leads wouldn't bounce.&lt;/p&gt;

&lt;p&gt;Once I started digging into the Telegram Business API, though, I realized we could do much more. We could turn the bot into an invisible assistant that sits directly on top of her normal conversations—logging client history, detecting double-bookings, and rendering an in-app daily schedule.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Telegram Business Connected Bots Change the UX
&lt;/h2&gt;

&lt;p&gt;Most Telegram bots require clients to search for a bot handle (&lt;code&gt;@my_salon_bot&lt;/code&gt;), click &lt;strong&gt;Start&lt;/strong&gt;, and tap through interactive menus. That works for large businesses, but for a private service master, it feels impersonal. Clients want to feel like they are talking directly to the artist.&lt;/p&gt;

&lt;p&gt;With Telegram Business Connected Bots, the integration connects directly to Anna’s personal Telegram account.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5oywmhih99xc9kya9zm0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5oywmhih99xc9kya9zm0.png" alt="Process" width="799" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the client's perspective, it's just a normal conversation with Anna. Behind the scenes, the Cloudflare Worker intercepts incoming webhook events to handle the background logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a new lead asks for pricing while Anna is busy, the bot automatically drops a friendly greeting and the price list.&lt;/li&gt;
&lt;li&gt;When Anna types a routine confirmation like &lt;em&gt;"Booked for tomorrow at 3:00 PM, manicure with removal, 2.5h, $55"&lt;/em&gt;, the bot parses the message, creates a database record, checks for calendar overlaps, and calculates her rest window.&lt;/li&gt;
&lt;li&gt;The client never sees command prompts, raw bot messages, or awkward third-party links.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architectural Separation: Relational Storage (D1) vs. Ephemeral State (KV)
&lt;/h2&gt;

&lt;p&gt;A common mistake with chat automations is trying to shove everything into a single database, or relying entirely on in-memory state that disappears whenever a serverless container spins down.&lt;/p&gt;

&lt;p&gt;Pairing Cloudflare D1 with Cloudflare KV gives a clean split based on data lifecycle:&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyat5teuelzt3ny2304en.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyat5teuelzt3ny2304en.png" alt="Data locations" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cloudflare D1 for Persistent CRM History
&lt;/h3&gt;

&lt;p&gt;D1 holds everything that must persist indefinitely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client Records&lt;/strong&gt;: Maps immutable Telegram user IDs to names, phone numbers, lifetime spend, visit counts, and no-show flags. Even if a client wipes the chat history, their record in D1 remains untouched. If a serial no-show reaches out again, the bot immediately flags them in Anna’s private notes so she can require a 50% non-refundable deposit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appointment Ledger&lt;/strong&gt;: Stores timestamps, durations, services, prices, payment methods, and statuses (&lt;code&gt;CONFIRMED&lt;/code&gt;, &lt;code&gt;PENDING_REMINDER&lt;/code&gt;, &lt;code&gt;NO_SHOW&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config &amp;amp; Media Cache&lt;/strong&gt;: Stores working hours, buffer preferences, and cached Telegram &lt;code&gt;file_id&lt;/code&gt; strings so the bot doesn't waste bandwidth re-uploading media over webhooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Cloudflare KV for Ephemeral Coordination
&lt;/h3&gt;

&lt;p&gt;KV handles short-lived state where built-in Time-To-Live (TTL) auto-expiration eliminates the need for cleanup jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active Master Cooldown (&lt;code&gt;interaction:{chatId}:ANYA_ACTIVITY&lt;/code&gt;, 1-hour TTL)&lt;/strong&gt;: Whenever Anna sends a manual message in any chat, the Worker writes this key. For the next 60 minutes, the bot stays completely quiet in that chat. This guarantees the bot never talks over Anna while she is actively texting a client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Spam Throttling (&lt;code&gt;interaction:{chatId}:GREETING&lt;/code&gt;, 2-hour TTL)&lt;/strong&gt;: Prevents duplicate greeting messages if an undecided client sends four short texts in a row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Digest Pointers (&lt;code&gt;appt_digest_key:{apptId}&lt;/code&gt;, 12-hour TTL)&lt;/strong&gt;: When the morning reminder cron runs, it maps each appointment ID back to a single summary message sent to Anna's private chat. When a client confirms via button, the Worker updates the existing summary message in place instead of spamming her with new notifications.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deterministic Parsing: Why I Skipped LLMs
&lt;/h2&gt;

&lt;p&gt;When developers hear "parse natural language chat messages," the default reaction is to reach for OpenAI or Claude. For this use case, calling an LLM on every incoming message was the wrong move:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Running an LLM across every message in dozens of active chats adds up fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: External LLM calls add 1 to 3 seconds of overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Generative models can hallucinate dates, drop numbers, or subtly misinterpret prices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because booking confirmations follow predictable phrasing, the parser runs entirely on deterministic regex and simple date arithmetic inside the Worker in under 1 millisecond.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Affirmative Verb Gating
&lt;/h3&gt;

&lt;p&gt;To make sure the bot doesn't accidentally record a booking when Anna is just discussing availability, the parser requires an affirmative confirmation keyword (e.g., &lt;em&gt;Booked&lt;/em&gt;, &lt;em&gt;Scheduled&lt;/em&gt;, &lt;em&gt;Reserved&lt;/em&gt;, &lt;em&gt;Set&lt;/em&gt;). &lt;/p&gt;

&lt;p&gt;Phrases containing exploratory words (&lt;em&gt;can do&lt;/em&gt;, &lt;em&gt;open slots&lt;/em&gt;, &lt;em&gt;what time works&lt;/em&gt;) are ignored.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Time, Date, and Duration Normalization
&lt;/h3&gt;

&lt;p&gt;The parser handles conversational variations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Times&lt;/strong&gt;: Converts standard 24h formats (&lt;code&gt;15:30&lt;/code&gt;, &lt;code&gt;14.00&lt;/code&gt;) as well as relative conversational hours (turning afternoon single digits like &lt;code&gt;3&lt;/code&gt; or &lt;code&gt;5&lt;/code&gt; into &lt;code&gt;15:00&lt;/code&gt; and &lt;code&gt;17:00&lt;/code&gt;, while preserving &lt;code&gt;10&lt;/code&gt; and &lt;code&gt;11&lt;/code&gt; as morning slots).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dates&lt;/strong&gt;: Resolves relative terms (&lt;code&gt;today&lt;/code&gt;, &lt;code&gt;tomorrow&lt;/code&gt;, &lt;code&gt;day after tomorrow&lt;/code&gt;), ordinal suffixes (&lt;code&gt;26th&lt;/code&gt;, &lt;code&gt;28th&lt;/code&gt;), and weekdays (&lt;code&gt;this Friday&lt;/code&gt;, &lt;code&gt;next Monday&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durations&lt;/strong&gt;: Extracts explicit values (&lt;code&gt;2.5h&lt;/code&gt;, &lt;code&gt;2h 30min&lt;/code&gt;, &lt;code&gt;90 min&lt;/code&gt;), falling back to default durations per service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt;: Normalizes mixed notation (&lt;code&gt;$55&lt;/code&gt;, &lt;code&gt;55$&lt;/code&gt;, &lt;code&gt;55 usd&lt;/code&gt;, &lt;code&gt;55 bucks&lt;/code&gt;, or raw numbers like &lt;code&gt;55&lt;/code&gt;) into clean integer values.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Overlap Detection and Break Calculations
&lt;/h2&gt;

&lt;p&gt;Once an appointment is parsed, the engine checks whether the slot conflicts with existing bookings and calculates the downtime between clients.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdvn83l56il90h8i50vz9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdvn83l56il90h8i50vz9.png" alt="Overlaping logic" width="720" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If an overlap is detected, the Worker sends an alert directly to Anna's private chat:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;🚨 WARNING: Time overlap with Client A (11:00 AM - 1:30 PM)!&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the slot is clear, it calculates the rest gap until the next client. If that gap matches or exceeds her buffer setting (e.g., 30 minutes), it logs the confirmation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;✅ Booking saved: August 26 at 2:15 PM. ☕ Rest window until next client: 45 min.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  In-Chat Timeline via Telegram Mini App
&lt;/h2&gt;

&lt;p&gt;To eliminate switching between Telegram and Google Calendar, Anna can open her full daily schedule directly inside Telegram via a lightweight Mini App.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpk0kwi52vg2kq7rsq2d.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpk0kwi52vg2kq7rsq2d.png" alt="UI Screenshot" width="367" height="844"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of bringing in React, Vue, or build tooling, the Mini App is a single-file TypeScript template. The HTML, CSS, and client-side JS are inlined straight into Worker memory and served as a cached single-page app (&lt;code&gt;/app&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSS Grid Timeline&lt;/strong&gt;: A clean vertical grid for the day (08:00 to 22:00).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Offsets&lt;/strong&gt;: Minimal vanilla JS calculates the exact pixel height and top margin for appointment blocks based on start time and duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Requests pass &lt;code&gt;window.Telegram.WebApp.initData&lt;/code&gt;. The Worker verifies the HMAC-SHA256 signature against the bot token, ensuring only Anna can access the view.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Interactive 24-Hour Reminders with In-Place Updates
&lt;/h2&gt;

&lt;p&gt;Checking next-day appointments and texting reminders used to take 20 minutes every morning. Now, a Cloudflare Cron Trigger runs daily at 11:00 AM local time (&lt;code&gt;0 4 * * *&lt;/code&gt; UTC).&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Reminders
&lt;/h3&gt;

&lt;p&gt;The cron pulls confirmed bookings for the next day and sends interactive inline buttons to each client:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hi! Just reminding you about our appointment tomorrow (August 18) at 2:00 PM for Manicure + Removal 💅"&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;[ ✅ I will be on time ]   [ 🔄 Reschedule ]   [ ❌ Can't make it ]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Updating the Daily Digest Without Notification Spam
&lt;/h3&gt;

&lt;p&gt;When reminders go out, the bot also sends Anna a consolidated morning digest:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;📅 Tomorrow's Schedule (August 18):&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;❓ 10:00 AM — Alina (Manicure + Removal) (Waiting for reply)&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;❓ 2:00 PM — Katya (Pedicure) (Waiting for reply)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Worker saves this digest to KV (&lt;code&gt;daily_digest:{timestamp}&lt;/code&gt;) and creates pointer keys for each appointment (&lt;code&gt;appt_digest_key:{apptId}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When a client taps &lt;code&gt;[ ✅ I will be on time ]&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webhook reads &lt;code&gt;appt_digest_key:{apptId}&lt;/code&gt; to find the message ID.&lt;/li&gt;
&lt;li&gt;It flips that client's icon from &lt;code&gt;❓&lt;/code&gt; to &lt;code&gt;✅&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It calls Telegram’s &lt;code&gt;editMessageText&lt;/code&gt; API to update the digest message in place.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anna can check her morning summary at any point during the day and see real-time updates without her phone buzzing every time someone confirms.&lt;/p&gt;




&lt;h2&gt;
  
  
  External Calendar Sync with ETag Caching
&lt;/h2&gt;

&lt;p&gt;For instances where Anna wants her schedule synced to a desktop calendar app, the Worker exposes an RFC 5545 &lt;code&gt;.ics&lt;/code&gt; feed over &lt;code&gt;webcal://&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;External calendar clients poll feeds aggressively, which can trigger unnecessary D1 reads. To keep overhead minimal, the endpoint generates an ETag based on the active booking count and the most recent modification timestamp:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ETag: "{appointmentCount}_{latestModifiedTimestamp}"&lt;/code&gt; (e.g. &lt;code&gt;"14_1723982400000"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When a client polls with a matching &lt;code&gt;If-None-Match&lt;/code&gt; header, the Worker immediately responds with &lt;code&gt;304 Not Modified&lt;/code&gt;, skipping data serialization and body transfer completely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;Building this setup reinforced a few practical rules for indie edge tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep users in their existing workflow&lt;/strong&gt;: Building on top of Telegram Business meant Anna and her clients didn't have to learn a new app or deal with third-party booking portals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right tool for the right lifespan&lt;/strong&gt;: D1 handles permanent relational records (clients, revenue, history), while KV handles short-lived state (cooldowns, message pointers) with zero manual cleanup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't overcomplicate with AI when deterministic rules work&lt;/strong&gt;: A few well-crafted regex patterns run in under a millisecond, cost nothing, and don't hallucinate dates or prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lean frontends on the edge&lt;/strong&gt;: Serving vanilla TypeScript and CSS straight from edge memory gives sub-100ms load times without managing client-side build pipelines.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>automation</category>
      <category>architecture</category>
    </item>
    <item>
      <title>MCP Server to generate custom QR codes directly in Cursor and Claude</title>
      <dc:creator>Evgenii Zinner</dc:creator>
      <pubDate>Mon, 20 Jul 2026 05:39:29 +0000</pubDate>
      <link>https://dev.to/evgenii_zinner/mcp-server-to-generate-custom-qr-codes-directly-in-cursor-and-claude-1cmf</link>
      <guid>https://dev.to/evgenii_zinner/mcp-server-to-generate-custom-qr-codes-directly-in-cursor-and-claude-1cmf</guid>
      <description>&lt;p&gt;If you use Cursor, Claude Desktop, or Cline as your daily driver, you know how annoying it is to leave your editor just to generate simple project assets. &lt;/p&gt;

&lt;p&gt;Last week, I was testing print layouts for a physical marketing project that needed scannable codes. I was tired of opening browser tabs, fighting spammy online generators that lock vector downloads behind paywalls, or getting forced into a subscription just to get an SVG.&lt;/p&gt;

&lt;p&gt;So, I built an MCP (Model Context Protocol) server for my &lt;a href="https://ez-qr-generator.com" rel="noopener noreferrer"&gt;QR Generator&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now, my AI assistant can generate high-resolution, customized QR codes (including clean vectors, custom shapes, and embedded logos) directly inside my IDE.&lt;/p&gt;

&lt;p&gt;Here is how it works, what it does, and how to hook it up to your environment.&lt;/p&gt;




&lt;h3&gt;
  
  
  What it does (and why standard PNGs suck)
&lt;/h3&gt;

&lt;p&gt;Most basic QR generators output low-resolution PNGs. If you try to print them on packaging or signage, they pixelate and scan terribly under bad lighting. &lt;/p&gt;

&lt;p&gt;To solve this, the MCP server exposes a single, highly flexible tool: &lt;code&gt;generate_qr_code&lt;/code&gt;. It lets you control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Format:&lt;/strong&gt; Choose between standard &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;svg&lt;/code&gt; (perfect for print), or raw &lt;code&gt;json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Design Physics:&lt;/strong&gt; Custom dot styles like &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;hex&lt;/code&gt; (hexagon), and &lt;code&gt;diamond&lt;/code&gt; instead of boring old squares.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Color &amp;amp; Branding:&lt;/strong&gt; Direct primary hex color mapping (e.g., &lt;code&gt;#00f0ff&lt;/code&gt;) and custom logo uploads inside the data grid.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Setting it up in your IDE
&lt;/h3&gt;

&lt;p&gt;Because the server runs over Server-Sent Events (SSE) hosted on our cloud backend, you don't need to install local runtimes or configure Python/Node environments. &lt;/p&gt;

&lt;p&gt;To add it to your &lt;strong&gt;Claude Desktop&lt;/strong&gt; or &lt;strong&gt;Cursor&lt;/strong&gt; configuration, open your &lt;code&gt;claude_desktop_config.json&lt;/code&gt; (or your Cursor MCP settings) and paste this snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ez-qr-generator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://ez-qr-generator.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sse"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once saved, your AI agent will automatically detect the connection.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to use it in practice
&lt;/h3&gt;

&lt;p&gt;You don't need to write any JSON arguments manually. Just tell your assistant what you want in plain English. For example, if you are working in Cursor, you can prompt your agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hey, generate a hexagonal green QR code pointing to &lt;a href="https://ez-qr-generator.com" rel="noopener noreferrer"&gt;https://ez-qr-generator.com&lt;/a&gt; and place it in my assets folder as a SVG."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The assistant calls the server, processes the base64 or SVG output, and writes the file directly to your workspace. No context switching, no signups, and no paywalls.&lt;/p&gt;

&lt;p&gt;If you want to test the raw JSON-RPC endpoint yourself or see the official registry, you can check out the &lt;a href="https://smithery.ai/servers/evgenii-zinner/ez-qr-generator" rel="noopener noreferrer"&gt;Smithery page&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Give it a spin, and let me know if you run into any schema or connection issues!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>qr</category>
      <category>agents</category>
    </item>
    <item>
      <title>Technical Architecture of EZ EDGE CMS</title>
      <dc:creator>Evgenii Zinner</dc:creator>
      <pubDate>Tue, 17 Mar 2026 20:58:02 +0000</pubDate>
      <link>https://dev.to/evgenii_zinner/technical-architecture-of-ez-edge-cms-1hj0</link>
      <guid>https://dev.to/evgenii_zinner/technical-architecture-of-ez-edge-cms-1hj0</guid>
      <description>&lt;p&gt;I developed &lt;a href="https://ez-edge-cms.ezinner.com/" rel="noopener noreferrer"&gt;EZ EDGE CMS&lt;/a&gt; with a specific goal: to create a content management system that runs entirely on Cloudflare Workers without the bloat of traditional frameworks. By moving styling, image processing, and SEO logic to the edge, I’ve eliminated the need for origin servers and complex build pipelines.&lt;/p&gt;

&lt;p&gt;Here is the technical breakdown of the engine and the trade-offs I made to keep it fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Zero-CSS Middleware (with Theme Constraints)
&lt;/h2&gt;

&lt;p&gt;EZ EDGE CMS has no static CSS files. I integrated a Just-In-Time (JIT) &lt;a href="https://unocss.dev/" rel="noopener noreferrer"&gt;UnoCSS&lt;/a&gt; middleware that runs at the edge. When a request is made:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The middleware scans the HTML content.&lt;/li&gt;
&lt;li&gt;It generates the minimal utility classes required for that specific page.&lt;/li&gt;
&lt;li&gt;It injects the CSS directly into the .
The Restriction: To maintain this performance, you cannot upload custom CSS files. You are limited to built-in themes where you can only customize colors and fonts. This ensures the JIT engine stays lightweight and prevents slowing down the edge isolate.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Edge-Native Image Processing
&lt;/h2&gt;

&lt;p&gt;Instead of relying on external S3 buckets or heavy image transformation services, I handle assets directly within the Worker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebP Conversion &amp;amp; Resizing: When you upload an image, the CMS automatically resizes it and converts it to WebP format. This ensures maximum compression and faster loading times for end-users.&lt;/li&gt;
&lt;li&gt;KV Binary Storage: Images are stored as Uint8Array binaries in Cloudflare KV. They are served from the same edge location as your content.&lt;/li&gt;
&lt;li&gt;Garbage Collection: When you save a page, I've implemented logic that identifies "orphaned" images (assets no longer used in your content) and deletes them from KV to keep your storage costs at zero or near-zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Automated SEO &amp;amp; JSON-LD Graphs
&lt;/h2&gt;

&lt;p&gt;I dont like to manually manage metadata, so I automated the SEO layer using Schema.org JSON-LD graphs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated SEO Layer: Replaced manual metadata management with a dynamic system built on Schema.org JSON-LD graphs to provide search engines with a high-accuracy site map.&lt;/li&gt;
&lt;li&gt;Persona Integration: Automatically injects identity data (Organization/Owner) directly into the page structure.&lt;/li&gt;
&lt;li&gt;Dynamic Breadcrumbs: Generates a real-time BreadcrumbList hierarchy that maps the page’s exact position.&lt;/li&gt;
&lt;li&gt;Automated Asset Delivery: Serves optimized Open Graph (OG) images and tailored meta tags without manual updates.&lt;/li&gt;
&lt;li&gt;Structured Metadata: Pulls standard SEO values from the CMS editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Script Injection for Interactivity
&lt;/h2&gt;

&lt;p&gt;Since the themes are constrained, I added the ability to inject Global and Page-Related Scripts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global Scripts: Useful for adding analytics (like Google Analytics or Plausible) or sitewide chat widgets.&lt;/li&gt;
&lt;li&gt;Page Scripts: Allows you to add specific functionality to a single page without bloating the rest of the site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Block-Based Editor
&lt;/h2&gt;

&lt;p&gt;To ensure the edge engine receives clean, predictable data, I integrated &lt;a href="https://editorjs.io/" rel="noopener noreferrer"&gt;Editor.js&lt;/a&gt;. Unlike traditional WYSIWYG editors we store and parse a clean JSON structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured Data: The CMS stores content as JSON blocks, which are then parsed at the edge into semantic HTML.&lt;/li&gt;
&lt;li&gt;Clean Output: This ensures that the JIT CSS engine (mentioned below) only scans valid, structured content, reducing processing overhead.&lt;/li&gt;
&lt;li&gt;Extensibility: I’ve plan to customize editor to handle specific blocks that sync perfectly with the edge-native theme.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Current Technical Restrictions
&lt;/h2&gt;

&lt;p&gt;Being honest about the limitations is part of the "Edge-Native" philosophy. If you use EZ EDGE CMS, you need to be aware of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design Limits: You cannot build a "from-scratch" custom layout. You must work within the parameters of my optimized built-in themes (Colors/Fonts only).&lt;/li&gt;
&lt;li&gt;25MB Value Limit: Cloudflare KV has a hard limit of 25MB per entry. This applies to individual pages and images.&lt;/li&gt;
&lt;li&gt;Propagation Delay: While the updateQueue prevents write errors, it can still take up to 60 seconds for a new post to appear in every single Cloudflare data center globally due to KV's native consistency model.&lt;/li&gt;
&lt;li&gt;Limitation for distributed teams: Currently recommended for solo work; without Durable Objects for atomic locking, simultaneous editing by multiple users is not possible to avoid data loss due to "last write wins" conflicts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is in a POC stage now, I need some time to work with it to shape a finalized vision and fix bugs, but i welcome everyone who willing to try or contribute&lt;br&gt;
See it in action: &lt;a href="https://ez-edge-cms.ezinner.com/" rel="noopener noreferrer"&gt;First website made on EZ EDGE CMS&lt;/a&gt;&lt;br&gt;
Explore the Code: &lt;a href="https://github.com/Evgenii-Zinner/ez-edge-cms" rel="noopener noreferrer"&gt;Source code&lt;/a&gt;&lt;br&gt;
Try it yourself: &lt;a href="https://deploy.workers.cloudflare.com/?url=https://github.com/Evgenii-Zinner/ez-edge-cms" rel="noopener noreferrer"&gt;Deploy to Cloudflare Workers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloudnative</category>
      <category>cms</category>
    </item>
    <item>
      <title>ez_commit: A Rust CLI for AI-Powered Conventional Commits</title>
      <dc:creator>Evgenii Zinner</dc:creator>
      <pubDate>Thu, 05 Feb 2026 18:44:20 +0000</pubDate>
      <link>https://dev.to/evgenii_zinner/ezcommit-a-rust-cli-for-ai-powered-conventional-commits-44on</link>
      <guid>https://dev.to/evgenii_zinner/ezcommit-a-rust-cli-for-ai-powered-conventional-commits-44on</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ez_commit&lt;/code&gt; is an interactive Rust-based CLI designed to take the friction out of writing &lt;strong&gt;Conventional Commits&lt;/strong&gt;. It combines a compact Terminal User Interface (TUI) for selecting and staging files with an AI-assisted generation engine.&lt;/p&gt;

&lt;p&gt;The tool enforces a &lt;strong&gt;human-in-the-loop&lt;/strong&gt; model: Copilot suggests a commit draft based on your staged changes, and you review or edit it before it ever touches your git history. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I built this:&lt;/strong&gt; Maintaining a clean, consistent commit history is vital for changelogs and team collaboration, but it’s often the first thing to slide when developers are in a rush. &lt;code&gt;ez_commit&lt;/code&gt; makes doing the "right thing" the fastest path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;You can find the full source code and installation instructions here:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Evgenii-Zinner" rel="noopener noreferrer"&gt;
        Evgenii-Zinner
      &lt;/a&gt; / &lt;a href="https://github.com/Evgenii-Zinner/ez_commit" rel="noopener noreferrer"&gt;
        ez_commit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      CLI tool for fast commiting using GitHub copilot
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ez_commit&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Interactive CLI tool for generating conventional commit messages with AI assistance.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A simple, fast, and human-in-the-loop commit workflow. Select files, let AI analyze your changes, review the message, and commit—all from the terminal.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;✨ &lt;strong&gt;Interactive file selection&lt;/strong&gt; - TUI-based multi-select of changed files&lt;br&gt;
🤖 &lt;strong&gt;AI-powered messages&lt;/strong&gt; - GitHub Copilot integration for smart commit messages&lt;br&gt;
📝 &lt;strong&gt;Conventional Commits&lt;/strong&gt; - Automatically formats to &lt;code&gt;type(scope): description&lt;/code&gt;&lt;br&gt;
✏️ &lt;strong&gt;Manual review&lt;/strong&gt; - Edit the generated message before committing&lt;br&gt;
⚡ &lt;strong&gt;Fallback analyzer&lt;/strong&gt; - Works without Copilot with built-in smart analysis&lt;br&gt;
🔍 &lt;strong&gt;Verbose mode&lt;/strong&gt; - Debug details with &lt;code&gt;-v&lt;/code&gt; flag&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick Start&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Build&lt;/span&gt;
cargo build --release

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Run&lt;/span&gt;
./target/release/ez_commit

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; With verbose output&lt;/span&gt;
./target/release/ez_commit -v&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Select files&lt;/strong&gt; - Arrow keys to navigate, Space to toggle, Enter to confirm&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review message&lt;/strong&gt; - AI generates a commit message with summary + bullet points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit (optional)&lt;/strong&gt; - Your editor opens for final tweaks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit&lt;/strong&gt; - Changes…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Evgenii-Zinner/ez_commit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&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%2Fvfi3v1kg17zpco5m35e1.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%2Fvfi3v1kg17zpco5m35e1.png" alt="*The interactive TUI for staging files.*"&gt;&lt;/a&gt;&lt;/p&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%2Fbkjefqg8vj5v8ccfyw8t.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%2Fbkjefqg8vj5v8ccfyw8t.png" alt="*The AI-assisted commit generation in action.*"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;I developed this project using an &lt;strong&gt;agentic loop&lt;/strong&gt;, where GitHub Copilot CLI handled much of the heavy lifting. My workflow looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Initial Design:&lt;/strong&gt; I started with a &lt;code&gt;README.md&lt;/code&gt; acting as a design doc. I described the problem, the required features, and my choice of Rust.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Agentic Cycle:&lt;/strong&gt; I used Copilot CLI to generate boilerplate and logic from prompts. It was a cycle of: &lt;em&gt;Generate → Build → Copilot read Errors → Fix.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fast Prototyping:&lt;/strong&gt; Copilot produced a working prototype remarkably fast. I spent roughly &lt;strong&gt;three hours total&lt;/strong&gt; on this project—including thinking, waiting for builds, and reviewing results.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The "Premium" Wall:&lt;/strong&gt; I wasn't read docs too thoroughly, so didn't notice that any usage of Copilot CLI count as premium calls, so i spent prompt budget really fast. &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Granularity of Feedback:&lt;/strong&gt; While Copilot is working on a file, it often reports the number of lines it is changing. However, it would be much more helpful to see the actual code diff directly in the dialogue rather than just numbers. Since it processes one file at a time, seeing the specific logic changes live would allow for much faster verification &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Overall Impression:&lt;/strong&gt; The speed of iteration is the killer feature here. Moving from a blank folder to a Rust CLI with TUI and AI integration in one afternoon felt like having a senior pair-programmer who never gets tired. And I used this tool after first iteration to create commit messages (so some errors are logged in history), it was fun experience.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to Use
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;To get started with &lt;code&gt;ez_commit&lt;/code&gt; locally:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cargo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--release&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Run the binary&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\target\release\ez_commit.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-v, --verbose&lt;/code&gt;: See exactly what's being sent to the AI.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-s, --skip-copilot&lt;/code&gt;: Use the local fallback analyzer if you're offline or out of credits.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d, --diff-limit&lt;/code&gt;: Control how much code context is sent to the model.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>I built an app and got tired of manual avatar struggles:
Inconsistent colors for the same user.
Two different users getting the same color.
The fix? Use a hash of the name to generate a color. 

https://pub.dev/packages/ez_circle_avatar</title>
      <dc:creator>Evgenii Zinner</dc:creator>
      <pubDate>Sat, 22 Nov 2025 02:31:52 +0000</pubDate>
      <link>https://dev.to/evgenii_zinner/i-built-an-app-and-got-tired-of-manual-avatar-struggles-inconsistent-colors-for-the-same-user-two-49mo</link>
      <guid>https://dev.to/evgenii_zinner/i-built-an-app-and-got-tired-of-manual-avatar-struggles-inconsistent-colors-for-the-same-user-two-49mo</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://pub.dev/packages/ez_circle_avatar" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub.dev%2Fstatic%2Fhash-19reohe7%2Fimg%2Fpub-dev-icon-cover-image.png" height="400" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://pub.dev/packages/ez_circle_avatar" rel="noopener noreferrer" class="c-link"&gt;
            ez_circle_avatar | Flutter package
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            A defensive, customizable avatar widget that automatically generates initials and colors from names.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub.dev%2Fstatic%2Fhash-19reohe7%2Fimg%2Fflutter-logo-32x32.png" width="32" height="32"&gt;
          pub.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
  </channel>
</rss>
