<?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: Gavindu achintha</title>
    <description>The latest articles on DEV Community by Gavindu achintha (@gavinduachintha).</description>
    <link>https://dev.to/gavinduachintha</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%2F1636782%2F85203a0a-1ecc-4d37-a7cc-2ec273c13fa0.png</url>
      <title>DEV Community: Gavindu achintha</title>
      <link>https://dev.to/gavinduachintha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gavinduachintha"/>
    <language>en</language>
    <item>
      <title>MailTriage: a local inbox desk a freelancer would pay for</title>
      <dc:creator>Gavindu achintha</dc:creator>
      <pubDate>Sat, 17 Oct 2026 06:23:42 +0000</pubDate>
      <link>https://dev.to/gavinduachintha/mailtriage-a-local-inbox-desk-a-freelancer-would-pay-for-55dp</link>
      <guid>https://dev.to/gavinduachintha/mailtriage-a-local-inbox-desk-a-freelancer-would-pay-for-55dp</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;MailTriage&lt;/strong&gt; is a local triage desk for freelancers. You connect Gmail, the app sorts client mail by what actually needs you, and a local model drafts a reply in your voice. You edit it. Nothing is sent until you save it to Gmail Drafts yourself.&lt;/p&gt;

&lt;p&gt;Categories: urgent, waiting on a client, invoice, new lead, low priority. Each message also gets a priority score, a one-line summary, and a suggested action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay:&lt;/strong&gt; a freelancer whose inbox is invoices, “just circling back”, real deadlines, and new leads — and who will not paste client names and rates into a hosted chatbot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; cloud copilots want the whole thread. MailTriage keeps inference on the laptop (Ollama + Gemma 3). Gmail tokens stay in a signed cookie. Postgres only stores writing-style examples and the last triage per message, not the inbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gmail  --read only--&amp;gt;  MailTriage  --local Gemma--&amp;gt;  draft
                            |
                       Postgres
                  (style + last triage)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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%2Fduvjsf1ylc0wm5jfh5lo.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%2Fduvjsf1ylc0wm5jfh5lo.png" alt=" " width="800" height="379"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2h6gopu2vfz37m3nioqa.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%2F2h6gopu2vfz37m3nioqa.png" alt=" " width="695" height="705"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxaspt4kc1jcuv000f4v0.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%2Fxaspt4kc1jcuv000f4v0.png" alt=" " width="668" height="208"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv50h58ipo7ote4uxv57.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%2Ffv50h58ipo7ote4uxv57.png" alt=" " width="648" height="289"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa3y2js4kldjfhl35f7th.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%2Fa3y2js4kldjfhl35f7th.png" alt=" " width="661" height="525"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&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/Gavinduachintha" rel="noopener noreferrer"&gt;
        Gavinduachintha
      &lt;/a&gt; / &lt;a href="https://github.com/Gavinduachintha/mailtriage" rel="noopener noreferrer"&gt;
        mailtriage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;This is a &lt;a href="https://nextjs.org" rel="nofollow noopener noreferrer"&gt;Next.js&lt;/a&gt; project bootstrapped with &lt;a href="https://nextjs.org/docs/app/api-reference/cli/create-next-app" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;create-next-app&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;First, run the development server:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm run dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
yarn dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
pnpm dev
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
bun dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; with your browser to see the result.&lt;/p&gt;
&lt;p&gt;You can start editing the page by modifying &lt;code&gt;app/page.tsx&lt;/code&gt;. The page auto-updates as you edit the file.&lt;/p&gt;
&lt;p&gt;This project uses &lt;a href="https://nextjs.org/docs/app/building-your-application/optimizing/fonts" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;next/font&lt;/code&gt;&lt;/a&gt; to automatically optimize and load &lt;a href="https://vercel.com/font" rel="nofollow noopener noreferrer"&gt;Geist&lt;/a&gt;, a new font family for Vercel.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Learn More&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;To learn more about Next.js, take a look at the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs" rel="nofollow noopener noreferrer"&gt;Next.js Documentation&lt;/a&gt; - learn about Next.js features and API.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/learn" rel="nofollow noopener noreferrer"&gt;Learn Next.js&lt;/a&gt; - an interactive Next.js tutorial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check out &lt;a href="https://github.com/vercel/next.js" rel="noopener noreferrer"&gt;the Next.js GitHub repository&lt;/a&gt; - your feedback and contributions are welcome!&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy on Vercel&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The easiest way to deploy your Next.js app is to use the &lt;a href="https://vercel.com/new?utm_medium=default-template&amp;amp;filter=next.js&amp;amp;utm_source=create-next-app&amp;amp;utm_campaign=create-next-app-readme" rel="nofollow noopener noreferrer"&gt;Vercel Platform&lt;/a&gt; from the creators of Next.js.&lt;/p&gt;

&lt;p&gt;Check out our &lt;a href="https://nextjs.org/docs/app/building-your-application/deploying" rel="nofollow noopener noreferrer"&gt;Next.js deployment documentation&lt;/a&gt; for more…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Gavinduachintha/mailtriage" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Open-source AI is the core, not a plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local inference:&lt;/strong&gt; Ollama on &lt;code&gt;127.0.0.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-weight model:&lt;/strong&gt; Gemma 3 (&lt;code&gt;OLLAMA_MODEL=gemma3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The model does the whole job: category, priority, summary, suggested action, and the draft. Style examples you save (past replies) are loaded from Postgres and sent with the prompt so the next draft sounds like you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of the stack is a desk around that call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16&lt;/strong&gt; — inbox list, retractable sidebar, draft panel, settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail API&lt;/strong&gt; — read-only inbox, save to Drafts (never auto-send)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL + &lt;code&gt;pg&lt;/code&gt;&lt;/strong&gt; — no ORM. Tables are &lt;code&gt;mailtriage_users&lt;/code&gt;, &lt;code&gt;mailtriage_style_examples&lt;/code&gt;, &lt;code&gt;mailtriage_analyses&lt;/code&gt; so they do not collide with an existing &lt;code&gt;users&lt;/code&gt; table. Queries are plain &lt;code&gt;SELECT&lt;/code&gt; / &lt;code&gt;INSERT&lt;/code&gt; / &lt;code&gt;UPDATE&lt;/code&gt; / &lt;code&gt;DELETE&lt;/code&gt;. Saving an analysis is an update, then an insert if nothing matched.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Next.js pool is one &lt;code&gt;pg.Pool&lt;/code&gt; on &lt;code&gt;globalThis&lt;/code&gt; so hot reload does not leak connections. If &lt;code&gt;DATABASE_URL&lt;/code&gt; changes, the pool is rebuilt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A freelancer will not pay for a tool that uploads their clients to a hosted model. Rates, NDAs, and “can you invoice this week” do not belong in someone else’s cache.&lt;/p&gt;

&lt;p&gt;A closed mail API would have been easier to demo (paste a key, ship a URL) and worse to sell. Open-weight Gemma on Ollama is what makes the side hustle honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt; — the thread is inferred on the same machine that already has the inbox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; — no per-token bill for “sorry for the delay,” so the product can be a one-time or cheap local tool&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control&lt;/strong&gt; — swap the model with &lt;code&gt;OLLAMA_MODEL&lt;/code&gt; instead of rebuilding around one vendor’s price and terms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The open pieces are why someone would pay &lt;em&gt;this&lt;/em&gt; instead of pasting the email into ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="GitHub Copilot"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Building MailTriage: local Gemma triage, Postgres, Week 2 post&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Analyze the UI. It looks like the usual LLM dashboard. Make it professional for freelancers, keep it consistent, no fancy gradients.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Replaced the indigo/serif LLM look with warm stone neutrals, a single muted teal accent, IBM Plex Sans/Mono wired correctly, and a calmer inbox/sidebar/draft panel. No gradients.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-searchreplace"&gt;search_replace&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;app/globals.css palette + font-sans&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;app/globals.css palette + font-sans&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Stone surfaces, teal primary, Geist mismatch fixed to Plex&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Add a settings option and make the sidebar retractable.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Desktop sidebar collapses to an icon rail (saved on the device). Settings dialog covers theme, writing style, local model status, and Gmail disconnect.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-write"&gt;write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;components/sidebar.tsx + settings-dialog.tsx&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sidebar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;Collapse&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;Settings&lt;/span&gt; &lt;span class="nx"&gt;under&lt;/span&gt; &lt;span class="nx"&gt;Workspace&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Add an avatar in the header. The application name is MailTriage.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Header shows initials from the Gmail address and a menu (settings, disconnect). Renamed InboxGuard to MailTriage in the UI, metadata, landing copy, and model prompt.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Add a database. Using Postgres. Keep it simple, no ORM.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Added pg Pool + raw SQL. Tables: users, style examples, analyses. Gmail tokens stay in the cookie. Inbox still comes from Gmail.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-write"&gt;write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;lib/db/pool.ts, lib/db/queries.ts, db/schema.sql&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;lib/db/pool.ts, lib/db/queries.ts, db/schema.sql&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Parameterized queries, no Drizzle&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Keep the db query simple. I need the full query including creating the tables.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Flattened to SELECT/INSERT/UPDATE/DELETE. Save path is UPDATE then INSERT. Gave the full CREATE TABLE script.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;CREATE TABLE users skipped (already exists). style_examples failed: column email referenced in foreign key does not exist.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;They were on postgres.public. An existing users table had no email column. Renamed tables to mailtriage_users, mailtriage_style_examples, mailtriage_analyses.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-write"&gt;write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;prefixed table names&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;prefixed table names&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;FK now references mailtriage_users(email)&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;password authentication failed for user postgres&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;A direct pg client connected. Next.js was still using a pool created with the old DATABASE_URL. Pool now rebuilds when the URL changes and prefers &lt;span class="agent-session-redacted"&gt;[REDACTED]&lt;/span&gt;. Restart the dev server after env changes.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-write"&gt;write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;lib/db/pool.ts recreate on URL change&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="nx"&gt;recreate&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Stale&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;longer&lt;/span&gt; &lt;span class="nx"&gt;keeps&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Create the markdown file. Add aesthetics. This is for Hacktoberfest Week 2. Put it on DEV too. Here is the official submission template.&lt;/p&gt;

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

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Wrote README.md for the Side Hustle pitch. Drafted a DEV post on the linked account with the Week 2 template, repo embed, and Best Use of Gemma. Left it unpublished for review.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-createarticle"&gt;create_article&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;title MailTriage Week 2 draft, published false&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;title MailTriage Week 2 draft, published false&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Draft saved; edit_url handed to user&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        16 of 16 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Build log from this session: UI pass, retractable sidebar and settings, MailTriage rename, Postgres without an ORM, the &lt;code&gt;users&lt;/code&gt; table collision, and the stale connection-pool password bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — triage and drafts run on Gemma 3 through Ollama. No hosted LLM.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Gavindu achintha</dc:creator>
      <pubDate>Mon, 12 Oct 2026 13:20:03 +0000</pubDate>
      <link>https://dev.to/gavinduachintha/-43ea</link>
      <guid>https://dev.to/gavinduachintha/-43ea</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43" class="crayons-story__hidden-navigation-link"&gt;I built a fully offline nature field guide with Gemma 3&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿&lt;/p&gt;

&lt;/a&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="/gavinduachintha" 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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1636782%2F85203a0a-1ecc-4d37-a7cc-2ec273c13fa0.png" alt="gavinduachintha profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/gavinduachintha" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Gavindu achintha
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Gavindu achintha
                
                
              
              &lt;div id="story-author-preview-content-4831817" 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="/gavinduachintha" 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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1636782%2F85203a0a-1ecc-4d37-a7cc-2ec273c13fa0.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Gavindu achintha&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/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 11&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/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43" id="article-link-4831817"&gt;
          I built a fully offline nature field guide with Gemma 3
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hf26challenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hf26challenge&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/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43" 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&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&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/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43#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;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built a fully offline nature field guide with Gemma 3</title>
      <dc:creator>Gavindu achintha</dc:creator>
      <pubDate>Sun, 11 Oct 2026 08:02:51 +0000</pubDate>
      <link>https://dev.to/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43</link>
      <guid>https://dev.to/gavinduachintha/i-built-a-fully-offline-nature-field-guide-with-gemma-3-1i43</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Trailside is an on-device nature field guide. Point your camera at a plant, bird, insect, or fungus — or upload a photo you already took — and get field notes in seconds. No signal, no cloud, no account. Everything runs locally with a small vision model, so it works on the trail, in the woods, or anywhere the cell bars disappear.&lt;/p&gt;

&lt;p&gt;You can use it from your laptop or phone: open the app in the browser, connect your device camera to snap something in the moment, or drop in a local image from your gallery. Optionally add a quick note of what you already know, and Trailside returns structured field notes. Save the ones that matter to a personal LogBook so the memory stays with the place, not the feed.&lt;/p&gt;

&lt;p&gt;It is built for people who want to look closer at the living world instead of scrolling past it: hikers, gardeners, kids on weekend walks, anyone who has ever wondered “what is that?” and wished for a quiet answer in their pocket.&lt;/p&gt;

&lt;p&gt;The goal is simple: get people off the screen and back into the world — with just enough help to notice more of what is already around them.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://youtu.be/t1g7IHkAPrk" rel="noopener noreferrer"&gt;Video Demo&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwfmmcngkds26zcix275.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%2Ftwfmmcngkds26zcix275.png" alt="Trailside home screen" width="800" height="384"&gt;&lt;/a&gt;&lt;br&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%2Frigu5u1d3p49h7tya63i.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%2Frigu5u1d3p49h7tya63i.png" alt="Field notes in progress" width="799" height="381"&gt;&lt;/a&gt;&lt;br&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%2Fhiam5ojb8trhdblutt95.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%2Fhiam5ojb8trhdblutt95.png" alt="LogBook view" width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Gavinduachintha/Trailside" rel="noopener noreferrer"&gt;https://github.com/Gavinduachintha/Trailside&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Trailside is built around &lt;strong&gt;Ollama&lt;/strong&gt; running the open-weight &lt;strong&gt;Gemma 3&lt;/strong&gt; vision model (&lt;code&gt;gemma3:latest&lt;/code&gt;) entirely on-device. There is no cloud API and no remote inference — the model lives on the user’s machine, so identification works offline on the trail.&lt;/p&gt;

&lt;p&gt;The app is a &lt;strong&gt;Streamlit&lt;/strong&gt; front end that handles camera capture or file upload, lightly processes the image with &lt;strong&gt;Pillow&lt;/strong&gt; (EXIF orientation, resize, JPEG encode), then streams the photo and an optional user hint into Ollama’s chat API. A system prompt and a small prompt builder shape the model’s reply into readable field notes. Responses are streamed token-by-token into a journal-style panel so the experience feels immediate.&lt;/p&gt;

&lt;p&gt;Saved sightings (image + notes + optional hint) are stored in a local database via a thin &lt;code&gt;config.db&lt;/code&gt; layer, and the LogBook view lets users browse, open, and delete entries. The whole stack — UI, local model, and storage — stays on the device so the tool remains useful where signal is weak or absent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Open innovation is what makes Trailside possible as a real field tool instead of another phone feature that dies when the bars disappear.&lt;/p&gt;

&lt;p&gt;Because the model is open-weight and runs locally through Ollama, every photo stays on the device. There is no upload, no account, no usage quota, and no dependency on a remote API that could change price, rate limits, or terms overnight. That privacy and reliability matter when you are identifying something in the woods — not because the data is sensitive in a corporate sense, but because the tool should work where you actually need it: offline, on a trail, with whatever hardware you already have.&lt;/p&gt;

&lt;p&gt;A closed vision API would have forced a constant network connection, sent images off-device, and tied the project to someone else’s pricing and availability. Open models and local inference remove that gate. They let a small, single-purpose app treat the model as a quiet companion on the path rather than a service you rent by the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Trailside is built around Gemma 3 running locally through Ollama. The model does the full multimodal identification: it receives the photo (and optional user hint), streams the field notes, and never leaves the device. No other AI or partner runtime is involved.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>My friends refuse to write Dockerfiles, so I built an AI that does it for them. You're welcome, lazy people. 🐳</title>
      <dc:creator>Gavindu achintha</dc:creator>
      <pubDate>Sun, 04 Oct 2026 07:01:37 +0000</pubDate>
      <link>https://dev.to/gavinduachintha/dockforge-ai-powered-dockerfile-generator-3hm1</link>
      <guid>https://dev.to/gavinduachintha/dockforge-ai-powered-dockerfile-generator-3hm1</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I built &lt;strong&gt;DockForge&lt;/strong&gt;, an AI-powered Docker engineer that turns a software repository into a working Docker setup.&lt;/p&gt;

&lt;p&gt;I built it for a friend who often works on projects that &lt;em&gt;run perfectly on their machine&lt;/em&gt; but become painful to containerize.&lt;/p&gt;

&lt;p&gt;Instead of manually figuring out the runtime, dependencies, package manager, ports, build commands, system packages, and startup configuration, DockForge analyzes the repository and generates a Docker setup tailored to the project.&lt;/p&gt;

&lt;p&gt;The goal isn't just to generate a Dockerfile that &lt;em&gt;looks correct&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;DockForge is designed around a &lt;strong&gt;generate → build → verify → fix&lt;/strong&gt; workflow.&lt;/p&gt;

&lt;p&gt;It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyze the repository structure and identify the technology stack&lt;/li&gt;
&lt;li&gt;Detect dependencies and package managers&lt;/li&gt;
&lt;li&gt;Determine likely build and start commands&lt;/li&gt;
&lt;li&gt;Generate a Dockerfile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository → Analyze → AI → Dockerfile → Build&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Demo
&lt;/h1&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%2F2g9gl1xoat5298wfg5yt.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%2F2g9gl1xoat5298wfg5yt.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;


&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__body flex items-center justify-between"&gt;
        &lt;a href="https://dockforge-chi.vercel.app/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;dockforge-chi.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The demo shows DockForge taking an existing repository and automatically producing a Docker configuration instead of requiring the developer to manually write one from scratch.&lt;/p&gt;




&lt;h1&gt;
  
  
  Code
&lt;/h1&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/Gavinduachintha" rel="noopener noreferrer"&gt;
        Gavinduachintha
      &lt;/a&gt; / &lt;a href="https://github.com/Gavinduachintha/Dockforge" rel="noopener noreferrer"&gt;
        Dockforge
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Ai powered docker file generating companion
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🐳 DockForge&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/33a0fbb7385ea6fca5fb85fd74473ce9a85fe57be2ade4a38de628ac774d00ce/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f636b466f7267652d76312e302e302d626c61636b3f7374796c653d666f722d7468652d6261646765266c6f676f3d646f636b6572"&gt;&lt;img src="https://camo.githubusercontent.com/33a0fbb7385ea6fca5fb85fd74473ce9a85fe57be2ade4a38de628ac774d00ce/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f636b466f7267652d76312e302e302d626c61636b3f7374796c653d666f722d7468652d6261646765266c6f676f3d646f636b6572" alt="DockForge Banner"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/54b851e11ebc5897a8066fe02fb773b2682a5e4003d35b5ad5027bf55283a0d2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e6578742e6a732d31352d626c61636b3f7374796c653d666f722d7468652d6261646765266c6f676f3d6e6578742e6a73"&gt;&lt;img src="https://camo.githubusercontent.com/54b851e11ebc5897a8066fe02fb773b2682a5e4003d35b5ad5027bf55283a0d2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e6578742e6a732d31352d626c61636b3f7374796c653d666f722d7468652d6261646765266c6f676f3d6e6578742e6a73" alt="Next.js"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e68ca76fd7bc972f242e145e867b9ed75166bf9a3d5a8242b675e8e07cd91bd1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d506f77657265642d4646384145323f7374796c653d666f722d7468652d6261646765266c6f676f3d6f70656e6169"&gt;&lt;img src="https://camo.githubusercontent.com/e68ca76fd7bc972f242e145e867b9ed75166bf9a3d5a8242b675e8e07cd91bd1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d506f77657265642d4646384145323f7374796c653d666f722d7468652d6261646765266c6f676f3d6f70656e6169" alt="AI Powered"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/aeab11b25054de160642d99db104f65cbf5306958288f6ac44b67f197ae1c4bc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d737563636573733f7374796c653d666f722d7468652d6261646765"&gt;&lt;img src="https://camo.githubusercontent.com/aeab11b25054de160642d99db104f65cbf5306958288f6ac44b67f197ae1c4bc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d737563636573733f7374796c653d666f722d7468652d6261646765" alt="License"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/50c57b723324fa657a266e0955883955609d6fa967cecb8597cc8e72b672914c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032342d6f72616e67653f7374796c653d666f722d7468652d6261646765"&gt;&lt;img src="https://camo.githubusercontent.com/50c57b723324fa657a266e0955883955609d6fa967cecb8597cc8e72b672914c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032342d6f72616e67653f7374796c653d666f722d7468652d6261646765" alt="Hacktoberfest"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-Powered Dockerfile Generator for Modern Applications&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Analyze any GitHub repository and get an optimized, production-ready Dockerfile in seconds.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Gavinduachintha/Dockforge#-quick-start" rel="noopener noreferrer"&gt;🚀 Quick Start&lt;/a&gt; • &lt;a href="https://github.com/Gavinduachintha/Dockforge/SETUP_GUIDE.md" rel="noopener noreferrer"&gt;📖 Setup Guide&lt;/a&gt; • &lt;a href="https://github.com/Gavinduachintha/Dockforge#-demo" rel="noopener noreferrer"&gt;🎮 Demo&lt;/a&gt; • &lt;a href="https://github.com/Gavinduachintha/Dockforge#-features" rel="noopener noreferrer"&gt;🛠️ Features&lt;/a&gt; • &lt;a href="https://github.com/Gavinduachintha/Dockforge#-contributing" rel="noopener noreferrer"&gt;🤝 Contributing&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎯 What is DockForge?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;DockForge is an intelligent tool that analyzes your GitHub repository and automatically generates production-ready Dockerfiles tailored to your project's specific needs. No more copy-pasting generic Dockerfiles or spending hours optimizing Docker configurations!&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why DockForge?&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="25%"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;⚡ Fast&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Generate Dockerfiles in seconds, not hours&lt;/p&gt;
&lt;/td&gt;
&lt;td width="25%"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🧠 Smart&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;AI-powered analysis and optimization&lt;/p&gt;
&lt;/td&gt;
&lt;td width="25%"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🎯 Accurate&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Framework and dependency detection&lt;/p&gt;
&lt;/td&gt;
&lt;td width="25%"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✨ Modern&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Best practices and security built-in&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Quick Start&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Get DockForge running in &lt;strong&gt;5 minutes&lt;/strong&gt;:&lt;/p&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; 1️⃣ Clone the repository&lt;/span&gt;
git clone https://github.com/yourusername/dockforge.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; dockforge
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 2️⃣ Install dependencies&lt;/span&gt;
npm install

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 3️⃣ Set up environment variables&lt;/span&gt;
cp .env.example .env
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Add your OpenRouter API key to .env&lt;/span&gt;

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 4️⃣ Start the development server&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Gavinduachintha/Dockforge" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
DockForge is built as an open-source project, so the implementation can be inspected, modified, and extended by other developers.


&lt;h1&gt;
  
  
  How I Built It
&lt;/h1&gt;

&lt;p&gt;I built the whole application with Next.js and used the OpenRouter API to connect it with an LLM.&lt;/p&gt;

&lt;p&gt;For the AI side, I'm using &lt;strong&gt;Qwen3.8-27B&lt;/strong&gt; whick is free. DockForge takes the project information and asks the model to figure out what kind of Docker setup the project needs and generate the Dockerfile.&lt;/p&gt;

&lt;p&gt;I wanted to keep the idea simple: instead of spending time figuring out how to Dockerize a project manually, you can give it to DockForge and let the AI handle the first draft for you.&lt;/p&gt;

&lt;p&gt;OpenRouter also made it really easy to experiment with different models without having to change the whole application.&lt;/p&gt;

&lt;p&gt;The process is roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository
    ↓
Repository Analysis
    ↓
Project Detection
    ↓
AI Planning
    ↓
Dockerfile Generation
    ↓
Docker Build
    ↓
Build Errors / Verification
    ↓
AI Fixes
    ↓
Working Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI is responsible for reasoning about the project and generating the configuration, while Docker provides the real-world feedback.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;A language model can generate a Dockerfile that &lt;em&gt;sounds right&lt;/em&gt; while still being completely broken. By actually attempting to build the image, DockForge can use Docker's output as feedback and iterate on the configuration.&lt;/p&gt;

&lt;p&gt;For the AI layer, I used &lt;strong&gt;open-weight models through OpenRouter&lt;/strong&gt;, allowing the underlying model to be replaced without redesigning the application around a single proprietary AI provider.&lt;/p&gt;

&lt;p&gt;This also means the architecture can evolve toward &lt;strong&gt;local inference and self-hosted models&lt;/strong&gt; instead of being permanently tied to one closed AI API.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h1&gt;

&lt;p&gt;Dockerization is one of those problems where there are many different valid solutions depending on the project.&lt;/p&gt;

&lt;p&gt;A Python application, a Node.js application, a Go service, and a project with native system dependencies all require different decisions.&lt;/p&gt;

&lt;p&gt;Open AI models make it possible to build tools around that reasoning without treating a single proprietary AI provider as the foundation of the product.&lt;/p&gt;

&lt;p&gt;For DockForge, open innovation matters in a few important ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model freedom
&lt;/h3&gt;

&lt;p&gt;The application isn't fundamentally tied to one model.&lt;/p&gt;

&lt;p&gt;As better open-weight coding models become available, DockForge can adopt them without rebuilding the entire product around a proprietary API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local and self-hosted possibilities
&lt;/h3&gt;

&lt;p&gt;The same architecture can evolve toward local inference and self-hosted models.&lt;/p&gt;

&lt;p&gt;That opens the door to using DockForge in environments where source code cannot or should not be sent to an external AI provider.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Build an AI email summarizer with event planning</title>
      <dc:creator>Gavindu achintha</dc:creator>
      <pubDate>Sat, 13 Sep 2025 15:56:16 +0000</pubDate>
      <link>https://dev.to/gavinduachintha/build-an-ai-email-summarizer-with-event-planning-4e1e</link>
      <guid>https://dev.to/gavinduachintha/build-an-ai-email-summarizer-with-event-planning-4e1e</guid>
      <description>&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%2Fmgrl0ow86hckdzd7wvg3.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%2Fmgrl0ow86hckdzd7wvg3.png" alt=" " width="799" height="382"&gt;&lt;/a&gt;&lt;br&gt;
Built a full-stack email dashboard with Gmail API integration and Google Calendar event planning using React, Node.js,OpenRouter and Kiro AI to accelerate development for the Code with Kiro hackathon.&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
