<?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: Malvin</title>
    <description>The latest articles on DEV Community by Malvin (@malvinx).</description>
    <link>https://dev.to/malvinx</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%2F3941787%2Fcf0cdead-3deb-4c3e-aad6-64f0b02690a7.png</url>
      <title>DEV Community: Malvin</title>
      <link>https://dev.to/malvinx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/malvinx"/>
    <language>en</language>
    <item>
      <title>Quack: the AI coding assistant that can't touch your code</title>
      <dc:creator>Malvin</dc:creator>
      <pubDate>Sun, 25 Oct 2026 10:34:48 +0000</pubDate>
      <link>https://dev.to/malvinx/quack-the-ai-coding-assistant-that-cant-touch-your-code-51ij</link>
      <guid>https://dev.to/malvinx/quack-the-ai-coding-assistant-that-cant-touch-your-code-51ij</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Quack is a rubber duck that lives in your terminal.&lt;/p&gt;

&lt;p&gt;You tell it what's broken. It finds the handful of lines in your repo that matter, asks you &lt;strong&gt;one&lt;/strong&gt; question about your own code, explains the likely cause with a &lt;code&gt;file:line&lt;/code&gt;, and writes a &lt;code&gt;TASKS.md&lt;/code&gt; for &lt;em&gt;you&lt;/em&gt; to work through. Then it stops.&lt;/p&gt;

&lt;p&gt;It cannot edit your source files. That isn't a promise in the README, it's a check in the code: every write goes through one function that allows &lt;code&gt;TASKS.md&lt;/code&gt;, &lt;code&gt;QUACK_HISTORY.md&lt;/code&gt; and &lt;code&gt;.quack/&lt;/code&gt;, and nothing else. Afterwards, &lt;code&gt;quack verify&lt;/code&gt; proves nothing changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;quack ask &lt;span class="s2"&gt;"why does session_valid reject valid sessions?"&lt;/span&gt;
pytest &lt;span class="nt"&gt;-x&lt;/span&gt; 2&amp;gt;&amp;amp;1 | quack ask &lt;span class="s2"&gt;"why does this fail?"&lt;/span&gt; &lt;span class="nt"&gt;--stdin&lt;/span&gt;   &lt;span class="c"&gt;# files in the traceback are read first&lt;/span&gt;
quack verify                                                &lt;span class="c"&gt;# PASS: no source files were modified.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most coding assistants optimise for the patch. I wanted the opposite for one specific moment: when I want to &lt;em&gt;understand&lt;/em&gt; the bug, not outsource it. The part of debugging I'm happy to skip is the hunt (grep, open five files, forget which one had the clue). Quack does the hunt and leaves the thinking to me.&lt;/p&gt;

&lt;p&gt;It fits the theme literally. A sloth conserves energy; Quack conserves tokens. Indexing costs zero, only small line windows are ever sent, and a repeated question is answered from a local cache for free.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Landing page: [&lt;a href="https://quack.wasmer.app" rel="noopener noreferrer"&gt;https://quack.wasmer.app&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&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%2Fkibsbx67qvd7deoq2cnd.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%2Fkibsbx67qvd7deoq2cnd.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The part of Quack that costs nothing, run for real on the source of pydantic 2.14.1 (107 files), with no model involved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ quack index
files: 107  symbols: 2518  mentions: 33979  todos: 63  time: 569 ms  tokens: 0
$ quack index --changed          # nothing changed since
files: 107  symbols: 2518  mentions: 33979  todos: 63  time: 7 ms  tokens: 0
$ quack ask "how does model validation handle extra fields" --dry-run
Dry run. The duck would read:
  pydantic/config.py (score 12.8768)
  pydantic/fields.py (score 12.4624)
  pydantic/root_model.py (score 12.2613)
context: 9893 chars (~2473 tokens), depth 2, model ollama:gemma3:4b
would write: TASKS.md, QUACK_HISTORY.md, .quack/ (index, cache, history)
source files: never.
$ quack verify
PASS: no source files were modified.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the model half, on a repo I know: [[FILL: paste your real &lt;code&gt;quack ask&lt;/code&gt; output here, with the footer line showing tokens and model]]&lt;/p&gt;

&lt;p&gt;Try it in three minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="o"&gt;[&lt;/span&gt;https://gitlab.com/quack-tutor/quack-hopper] &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;quack-tutor
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
ollama pull gemma3:4b
quack &lt;span class="nt"&gt;-C&lt;/span&gt; tests/fixtures/mini_repo ask &lt;span class="s2"&gt;"why does session_valid reject valid sessions?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;[&lt;a href="https://gitlab.com/quack-tutor/quack-hopper" rel="noopener noreferrer"&gt;https://gitlab.com/quack-tutor/quack-hopper&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;MIT licensed. &lt;code&gt;quack/&lt;/code&gt; is the CLI, &lt;code&gt;web/&lt;/code&gt; is the Astro landing page, and &lt;code&gt;tests/&lt;/code&gt; holds 69 tests that run with no network and no model.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The open pieces.&lt;/strong&gt; The default model is Gemma 3 (4B) running locally through Ollama, using Ollama's structured output with the Pydantic schema so the answer arrives as validated JSON. Everything else is the standard library: SQLite, regex, and a hand-written BM25.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One step in six uses the model.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Index&lt;/strong&gt;: walk the repo, extract symbols, term counts and TODOs into SQLite. 0 tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve&lt;/strong&gt;: BM25, plus a bonus for matching symbol and file names. Traceback paths get pinned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window&lt;/strong&gt;: only line windows around matching symbols, capped at 30 / 80 / 150 lines per file by &lt;code&gt;--depth&lt;/code&gt;. Never a whole file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redact&lt;/strong&gt;: API keys, GitHub and Slack tokens, AWS keys and private keys are masked before indexing and before anything reaches a model. &lt;code&gt;.env&lt;/code&gt; files and key files are never indexed at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask&lt;/strong&gt;: one model call returns a question, a cause, tasks, concepts and a confidence level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ground&lt;/strong&gt;: tasks that point at files that don't exist are dropped and impossible line numbers are clamped, so a small model can't send you chasing an invented path.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Context is trimmed to fit a 5,000 token budget, and Quack warns if a run goes over. The targets below come from my build spec. The last column is measured.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Depth&lt;/th&gt;
&lt;th&gt;Lines per file&lt;/th&gt;
&lt;th&gt;Spec target&lt;/th&gt;
&lt;th&gt;Measured prompt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;~1,000 to 1,500&lt;/td&gt;
&lt;td&gt;~1,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 (default)&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;~2,000 to 3,000&lt;/td&gt;
&lt;td&gt;~2,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;td&gt;~4,000 to 5,000&lt;/td&gt;
&lt;td&gt;~4,300 (trimmed to fit)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Measured" is the system prompt plus retrieved context for the pydantic question above, estimated at 4 characters per token, before the model's answer. At depth 3 the raw context was about 5,100 tokens, so Quack trimmed it to fit the budget. Real usage varies by model, so &lt;code&gt;quack ask --json&lt;/code&gt; prints what your provider reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beyond one model.&lt;/strong&gt; Remote providers (OpenRouter, Anthropic, Groq, Gemini and a dozen others, plus any OpenAI-compatible endpoint) are supported but blocked until you opt in. &lt;code&gt;quack compare "..." -w ollama -w openrouter:google/gemma-3-27b-it&lt;/code&gt; asks the same question of several models and lays the answers side by side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I worked.&lt;/strong&gt; I wrote a build spec first (it's in the repo as &lt;code&gt;BUILD_SPEC.json&lt;/code&gt;) and built the project with an AI coding assistant against it, then tested and reviewed the result. The tests use a fake model and a local HTTP server, so the whole suite runs offline in about three seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it doesn't do well.&lt;/strong&gt; Symbols come from regex, not a parser, so unusual syntax can be missed. There are no embeddings; retrieval is keyword-based. A 4B model will sometimes be wrong, which is why the answer carries an honest confidence level, the grounding step exists, and the prompt tells it to say "show me another file" instead of guessing.&lt;/p&gt;

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

&lt;p&gt;Quack's whole job is reading code you haven't shipped yet: pre-launch features, customer fixtures, internal TODOs. That's exactly what people hesitate to paste into a hosted assistant.&lt;/p&gt;

&lt;p&gt;With open weights on localhost, "nothing leaves your machine" is a property I can test, not a policy I have to trust. A test asserts that Quack refuses to contact any non-local host until you opt in, and &lt;code&gt;quack verify --audit&lt;/code&gt; lists every place the program can write to disk. I could never write those tests against a closed API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private and free.&lt;/strong&gt; After &lt;code&gt;ollama pull gemma3:4b&lt;/code&gt; it runs on a laptop with no internet and no per-token bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable.&lt;/strong&gt; Changing the model is a config line, so I can compare a local 4B against a larger open model on the same bug and see where each one goes wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adjustable.&lt;/strong&gt; The prompts are plain strings in one file. Changing the duck's voice is an edit, not a feature request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off is honest: a local 4B model is less sharp than a frontier one. Open innovation is what lets me choose that trade per project (local by default, a bigger open model when I decide to), instead of having it chosen for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;The payoff is the hunt that no longer happens. For a bug in code I don't know well, the usual loop is: search, open files, read, guess, search again. Quack collapses that into one command and a short checklist, and the question it asks first often points at the right place before I've read the explanation.&lt;/p&gt;

&lt;p&gt;What I can measure is the cost side. On a codebase I didn't write (pydantic 2.14.1, 107 files), Quack indexed everything in about half a second for zero tokens, and re-indexing after a change took 7 ms. Choosing the files worth reading and cutting them down to line windows is plain Python over a local SQLite file, so the only tokens spent are the ~2,800 in the final prompt at the default depth. What it saves you in debugging time is something you'll feel on your own repo, so I won't invent a stopwatch number for it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 is the default model, run locally through Ollama, with Gemma 3 27B available through OpenRouter for comparison.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>ReceiptOwl — stop losing receipts, start keeping your margin</title>
      <dc:creator>Malvin</dc:creator>
      <pubDate>Sat, 17 Oct 2026 13:22:40 +0000</pubDate>
      <link>https://dev.to/malvinx/receiptowl-stop-losing-receipts-start-keeping-your-margin-57d</link>
      <guid>https://dev.to/malvinx/receiptowl-stop-losing-receipts-start-keeping-your-margin-57d</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;ReceiptOwl&lt;/strong&gt; is a local-first desktop app that turns a messy folder of receipts — PDFs, phone photos, screenshots — into tax-ready records, without any of it ever leaving your computer.&lt;/p&gt;

&lt;p&gt;It's built for the "side hustle" crowd this challenge is about: freelancers, small shop owners, anyone running a side business out of email attachments and a camera roll. You drop receipts into the app, it OCRs and extracts the vendor/amount/date/tax with a local AI model (falling back to a deterministic heuristic if no model is available), sorts them into your categories, flags anything that looks off (missing tax, duplicate charge, low-confidence read), and gives you a CSV, a tax-summary PDF, and an anomaly report — plus the organized folder underneath all of it.&lt;/p&gt;

&lt;p&gt;Five screens, one pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nest → Hunt → Perch → Treasure → Settings
drop    process   review/edit   totals &amp;amp; export   configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Honest disclaimer:&lt;/strong&gt; this isn't a finished v1. It's not perfect, and it still has flaws — some interface rough edges, limited test coverage, and also messy ui not responsive :]&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;(Attach a short screen recording or GIF of Nest → Hunt → Perch → Treasure here before publishing.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Screens, for now:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nest (upload)&lt;/th&gt;
&lt;th&gt;Hunt (live processing)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fqn9np31hfv386vpmvd4b.png" alt="Nest" width="800" height="380"&gt;&lt;/td&gt;
&lt;td&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%2Fjez73crpwbjukr3kc774.png" alt="Hunt" width="800" height="380"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Perch (review &amp;amp; edit)&lt;/th&gt;
&lt;th&gt;Treasure (totals &amp;amp; reports)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2F4dx10s1414qeksy1mt2m.png" alt="Perch" width="800" height="380"&gt;&lt;/td&gt;
&lt;td&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%2F7qfgsx2zz2zqx0ojeeci.png" alt="Treasure" width="800" height="380"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Repo: &lt;a href="https://github.com/Malvin555/ReceiptOwl" rel="noopener noreferrer"&gt;https://github.com/Malvin555/ReceiptOwl&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The app is Python + &lt;a href="https://www.gradio.dev/" rel="noopener noreferrer"&gt;Gradio&lt;/a&gt; for the UI, with a custom CSS theme layered on top so it doesn't look like a default Gradio demo. Everything runs locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OCR:&lt;/strong&gt; &lt;code&gt;pypdf&lt;/code&gt; reads a PDF's text layer directly when there is one (fast, no model needed); &lt;code&gt;pytesseract&lt;/code&gt; (Tesseract) handles images and scanned PDFs as a fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field extraction (the "AI" step):&lt;/strong&gt; a local &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; server, defaulting to &lt;strong&gt;Gemma 2 (2B)&lt;/strong&gt;, pulls vendor, amount, date, and tax out of the OCR'd text. If Ollama isn't running — no install, server down, whatever — extraction falls back automatically to a deterministic regex/heuristic extractor that still returns a usable result with a lower confidence score. The app never hard-fails because a model isn't available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Categorization&lt;/strong&gt; is a keyword match against categories the user actually configured, not a fixed taxonomy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt; is SQLite plus content-addressed copies of the original files on disk, all under one folder the user can see and open in a file explorer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trickiest part wasn't the AI pipeline — it was making the UI actually look and behave like a real app rather than a generic Gradio form: custom navigation between the five screens, a styled file-drop zone, editable tables, and (the bug that ate the most time) Gradio's theme auto-switching ~70 CSS variables based on the OS's light/dark setting, which made icons and text invisible on certain systems until every one of those variables got pinned to the app's own light design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; was part of the day-to-day workflow throughout — inline completions while writing the Python pipeline and CSS, plus Copilot Chat for quick debugging and "why is this not working" questions alongside the editor.&lt;/p&gt;

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

&lt;p&gt;Because the people this app is for are the ones least served by "send your documents to our cloud and we'll process them" SaaS tools — a freelancer's receipts are their financial life, and a lot of side-hustle tooling asks you to hand that over to a third party by default. Open-weight local models like Gemma make it possible to put real extraction intelligence &lt;em&gt;on the user's own machine&lt;/em&gt;, with no API key, no per-document cost, and no data leaving the laptop. That's the whole thesis of this build: the AI should come to your data, not the other way around. Open-source OCR and open-weight LLMs are what make that actually achievable for a solo developer in a few weeks, instead of something only a funded company could build.&lt;/p&gt;

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

&lt;p&gt;Built with two AI tools working different parts of the job. &lt;strong&gt;GitHub Copilot&lt;/strong&gt; sat in the editor the whole time — inline completions and Copilot Chat for the moment-to-moment "write this function," "why does this throw" work. &lt;strong&gt;Copilot also&lt;/strong&gt; handled the bigger, multi-step chunks: scaffolding the Gradio screens and routing, writing the OCR/extraction/categorization pipeline, debugging the dark-mode CSS variable issue across the whole UI (tracing a theme bug across ~70 CSS custom properties by diffing computed styles in a real browser context), generating fixture receipts and screenshots for this write-up, and drafting this very post.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;Maybe Me if it counted:] — I've been using it while building it, which is how the "missing tax" and duplicate-detection flags ended up in Perch; I kept making the exact mistakes the app now catches.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — &lt;code&gt;gemma2:2b&lt;/code&gt; is ReceiptOwl's default local extraction model (&lt;code&gt;config.py: DEFAULT_MODEL = MODEL_OPTIONS[0]&lt;/code&gt;), run through a local Ollama server. It's the first thing the pipeline tries before falling back to the heuristic extractor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Copilot&lt;/strong&gt; — used throughout development for inline code completion and Copilot Chat debugging, alongside project&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Still rough in places — see the repo's README for known limitations — but the core idea works, and I wanted to put it out there now rather than wait.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>TouchGrass 🌿 — Turn Screen Time into Green Time</title>
      <dc:creator>Malvin</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:33:25 +0000</pubDate>
      <link>https://dev.to/malvinx/touchgrass-turn-screen-time-into-green-time-3i1o</link>
      <guid>https://dev.to/malvinx/touchgrass-turn-screen-time-into-green-time-3i1o</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;TouchGrass is a mobile-first app that encourages people to step away from their screens, explore the outdoors, and build a habit of noticing the world around them.&lt;/p&gt;

&lt;p&gt;Instead of endlessly scrolling, users can choose from three activities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚶 &lt;strong&gt;Walk&lt;/strong&gt; — Take a break and pay attention to your surroundings.&lt;/li&gt;
&lt;li&gt;🌱 &lt;strong&gt;Nature&lt;/strong&gt; — Photograph something in nature and use on-device AI image labeling to help identify what's in the picture.&lt;/li&gt;
&lt;li&gt;🧭 &lt;strong&gt;Explore&lt;/strong&gt; — Discover something new, capture the moment, and keep it in your personal journal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Completed activities contribute to progress tracking, helping users turn small outdoor moments into a lasting habit.&lt;/p&gt;

&lt;p&gt;The idea is simple: technology should sometimes encourage us to put our phones away and experience the real world.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Screenshots:&lt;/strong&gt; See the &lt;a href="https://github.com/Malvin555/touch-grass" rel="noopener noreferrer"&gt;TouchGrass repository&lt;/a&gt; for more previews.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Intro 1–3&lt;/th&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2F67e3uleimn1vsr7915vw.png" alt="TouchGrass Intro" width="800" height="1648"&gt;&lt;/td&gt;
&lt;td&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%2F4rsjsqqmeynkjojsm434.png" alt="TouchGrass Home" width="800" height="1648"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The camera and native AI features are intended for the Android app.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;🌿 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Malvin555/touch-grass" rel="noopener noreferrer"&gt;TouchGrass&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built as a Vue application with Capacitor for native Android integration.&lt;/p&gt;

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

&lt;p&gt;I built TouchGrass with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vue 3 + Vite&lt;/strong&gt; for the application and development workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UnoCSS&lt;/strong&gt; for styling a mobile-first interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pinia&lt;/strong&gt; for activity, onboarding, and progress state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue Router&lt;/strong&gt; for navigation between activities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacitor&lt;/strong&gt; to integrate native Android capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google ML Kit Image Labeling via &lt;code&gt;@capacitor-mlkit/image-labeling&lt;/code&gt;&lt;/strong&gt; for on-device image labeling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacitor Filesystem&lt;/strong&gt; to prepare local image files for native image analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Nature activity captures a photo and passes it to the native image-labeling plugin. The returned labels and confidence scores help the app process the discovery. Explore uses photos to preserve outdoor moments, while Walk focuses on completing a simple outdoor activity.&lt;/p&gt;

&lt;p&gt;The core experience does not require a custom backend. This keeps the architecture relatively lightweight and reduces the need to send users' photos to a remote service.&lt;/p&gt;

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

&lt;p&gt;Open innovation gives developers the freedom to build experiences that aren't entirely dependent on a remote AI API.&lt;/p&gt;

&lt;p&gt;For TouchGrass, native on-device image labeling offers a path toward analyzing photos without uploading them to a third-party inference service. It also makes the AI feature part of the app's real-world activity flow rather than a chatbot that keeps users on their screens.&lt;/p&gt;

&lt;p&gt;Using open-source frameworks and native integrations lets me shape the experience around mobile constraints, privacy, and the goal of spending more time outdoors.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>vue</category>
    </item>
  </channel>
</rss>
