<?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: David Nizeyimana</title>
    <description>The latest articles on DEV Community by David Nizeyimana (@david_nize).</description>
    <link>https://dev.to/david_nize</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%2F3807873%2F0536a0c0-d464-4afa-8a90-6b0525d0480c.jpg</url>
      <title>DEV Community: David Nizeyimana</title>
      <link>https://dev.to/david_nize</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/david_nize"/>
    <language>en</language>
    <item>
      <title>Sloth: a lazy local AI tool that tidies files and summarizes chats, built with Gemma, Ollama and GitHub Copilot</title>
      <dc:creator>David Nizeyimana</dc:creator>
      <pubDate>Sat, 24 Oct 2026 22:19:55 +0000</pubDate>
      <link>https://dev.to/david_nize/sloth-a-lazy-local-ai-tool-that-tidies-files-and-summarizes-chats-built-with-gemma-ollama-and-158b</link>
      <guid>https://dev.to/david_nize/sloth-a-lazy-local-ai-tool-that-tidies-files-and-summarizes-chats-built-with-gemma-ollama-and-158b</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;I built &lt;strong&gt;Sloth&lt;/strong&gt;, a small command-line tool that does two things for me so I don't have to.&lt;/p&gt;

&lt;p&gt;The first one is &lt;code&gt;sloth tidy &amp;lt;folder&amp;gt;&lt;/code&gt;. It takes a messy folder, like Downloads, and sorts the files into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documents&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;li&gt;Audio&lt;/li&gt;
&lt;li&gt;Archives&lt;/li&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;li&gt;Other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It shows me what it plans to do first. It only actually moves the files when I use &lt;code&gt;--apply&lt;/code&gt;, and it never overwrites an existing file.&lt;/p&gt;

&lt;p&gt;The second one is &lt;code&gt;sloth digest &amp;lt;file&amp;gt;&lt;/code&gt;. It takes an exported chat or notes file and gives me a short two-line summary: what the conversation is about and whether there is a decision, task, or deadline.&lt;/p&gt;

&lt;p&gt;Both commands use &lt;strong&gt;Gemma 3 (1B)&lt;/strong&gt; through &lt;strong&gt;Ollama&lt;/strong&gt;, running directly on my laptop. So there is no cloud service, no API key and no extra cost. It is basically a little AI tool running on my own computer to help me be lazy.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/MQNpCGfZZpU" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is some real output from my machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node src/index.js tidy /tmp/messy
app.js -&amp;gt; Code/app.js
budget.xyz -&amp;gt; Code/budget.xyz
holiday-selfie -&amp;gt; Documents/holiday-selfie
lecture-recording -&amp;gt; Videos/lecture-recording
notes.txt -&amp;gt; Documents/notes.txt
photo.jpg -&amp;gt; Images/photo.jpg
report.pdf -&amp;gt; Documents/report.pdf
song.mp3 -&amp;gt; Audio/song.mp3

$ node src/index.js digest chat.txt
Amina and Joe will meet at 3pm, with the report deadline being Friday noon.
No action needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And honestly, not all of this output is correct. I'll explain that below.&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/davidnize" rel="noopener noreferrer"&gt;
        davidnize
      &lt;/a&gt; / &lt;a href="https://github.com/davidnize/sloth-cli" rel="noopener noreferrer"&gt;
        sloth-cli
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
         A lazy local AI command-line tool: sort files and summarize chats with Gemma and Ollama. No cloud, no API keys.
    &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;sloth-cli&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A lazy local AI command-line tool powered by Gemma and Ollama. It does small chores for you, entirely on your own machine: no cloud, no API keys, no cost.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; and keep it running.&lt;/li&gt;
&lt;li&gt;Pull the model: &lt;code&gt;ollama pull gemma3:1b&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install Node.js 18 or newer, clone this repo, and run commands from its folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&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;node src/index.js &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;command&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tidy &amp;lt;folder&amp;gt; [--apply]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;digest &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;

&lt;p&gt;Sorts the files in a folder into category subfolders (Documents, Images, Videos, Audio, Archives, Code, Other). The local model picks each category, with a fallback to the file extension if its answer is invalid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;node src/index.js tidy &amp;lt;folder&amp;gt;&lt;/code&gt; is a dry run and only prints the planned moves.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node src/index.js tidy &amp;lt;folder&amp;gt; --apply&lt;/code&gt; creates the folders and moves the files.&lt;/li&gt;
&lt;li&gt;Existing files are never overwritten. The model runs with temperature 0 and a fixed seed, so a dry run and…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidnize/sloth-cli" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The AI
&lt;/h3&gt;

&lt;p&gt;I used &lt;strong&gt;Gemma 3 (1B)&lt;/strong&gt; through Ollama. The tool talks to Ollama's &lt;code&gt;/api/generate&lt;/code&gt; endpoint running on &lt;code&gt;localhost&lt;/code&gt;. The project itself is just plain Node.js with no dependencies.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;tidy&lt;/code&gt;, the AI looks at each file and chooses one category from a fixed list. If the AI gives me something that isn't one of those categories, or if Ollama isn't available, the program falls back to using the file extension.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;digest&lt;/code&gt;, I send the last 6000 characters of the file to Gemma because, for chats especially, the most recent messages are usually the most useful. Then I take the first two non-empty lines from the response.&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%2Fi7wboo4z33jmm2txe2vc.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%2Fi7wboo4z33jmm2txe2vc.png" alt=" " width="800" height="857"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  How GitHub Copilot fits in
&lt;/h3&gt;

&lt;p&gt;I wrote very little of the actual code myself. Instead, I created GitHub issues and assigned them to &lt;strong&gt;GitHub Copilot's coding agent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is what I did:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Issue #1&lt;/strong&gt; — CLI skeleton and the Ollama helper → PR #2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue #3&lt;/strong&gt; — &lt;code&gt;tidy&lt;/code&gt; command → PR #4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue #6&lt;/strong&gt; — bug I found while testing → PR #7&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue #8&lt;/strong&gt; — &lt;code&gt;digest&lt;/code&gt; command → PR #9&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I still tested every pull request myself before merging it. I wasn't just letting Copilot write everything and hoping it worked.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Bug
&lt;/h3&gt;

&lt;p&gt;While testing &lt;code&gt;tidy&lt;/code&gt;, I found something I didn't like. I ran it as a preview first, and &lt;code&gt;notes.txt&lt;/code&gt; was supposed to go into &lt;code&gt;Documents&lt;/code&gt;. Then I ran it again with &lt;code&gt;--apply&lt;/code&gt;, and the AI decided to put the same file in &lt;code&gt;Other&lt;/code&gt;. The model was giving a different answer each time, which meant the preview wasn't actually a promise of what was going to happen.&lt;/p&gt;

&lt;p&gt;So I opened Issue #6 and had Copilot fix it by making the model deterministic, using a temperature of &lt;code&gt;0&lt;/code&gt; and a fixed seed. After that, the preview and the actual run gave me the same plan.&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%2Flve6t4s2g4rwa2fj8itf.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%2Flve6t4s2g4rwa2fj8itf.png" alt=" " width="799" height="504"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/2qvsrw9avbs11n67ppdw.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Something Copilot Got Wrong
&lt;/h3&gt;

&lt;p&gt;There was another thing I caught while reviewing the fix. Copilot originally put &lt;code&gt;temperature&lt;/code&gt; and &lt;code&gt;seed&lt;/code&gt; at the top level of the Ollama request, but Ollama expects those settings inside an &lt;code&gt;options&lt;/code&gt; object. So they were basically being ignored.&lt;/p&gt;

&lt;p&gt;The funny part is that Copilot's own test still passed because the test was checking the wrong place too. I caught this while reviewing the pull request, commented on it, and Copilot fixed it in another commit.&lt;/p&gt;

&lt;p&gt;That was one of the more interesting parts of building this because it reminded me that even when an AI writes the code, I still need to understand what the code is actually doing.&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%2Fzmyp9mrpshttls2jdoro.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%2Fzmyp9mrpshttls2jdoro.png" alt=" " width="800" height="1063"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  When the Credits Ran Out
&lt;/h3&gt;

&lt;p&gt;While working on the &lt;code&gt;digest&lt;/code&gt; pull request, Copilot ran out of its monthly credits. It couldn't fix an old test that was still expecting &lt;code&gt;digest&lt;/code&gt; to say &lt;code&gt;"not implemented yet"&lt;/code&gt;, and a Copilot review request also failed because of the quota. So I fixed that test myself.&lt;/p&gt;

&lt;p&gt;I also added a GitHub Actions workflow myself so that every push and pull request runs the full test suite.&lt;/p&gt;

&lt;p&gt;I had also planned to build a third command called &lt;code&gt;cards&lt;/code&gt;, but I decided not to force it into the project just because I had planned it. I'd rather leave it out than ship something half-finished.&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%2Fambzttd4befbfp6v800h.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%2Fambzttd4befbfp6v800h.png" alt=" " width="800" height="864"&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%2F2g681n6hsl1e0mxznpez.jpeg" 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%2F2g681n6hsl1e0mxznpez.jpeg" alt=" " width="800" height="852"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;For me, the biggest reason is that the AI actually stays on my computer. &lt;code&gt;tidy&lt;/code&gt; needs to see my file names and &lt;code&gt;digest&lt;/code&gt; needs to read my chats or notes. I don't really want to send those things to some random online API just to organize them. With Ollama and Gemma, they stay local.&lt;/p&gt;

&lt;p&gt;It also means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No API key&lt;/li&gt;
&lt;li&gt;No API bill&lt;/li&gt;
&lt;li&gt;No internet connection needed once the model is downloaded&lt;/li&gt;
&lt;li&gt;I can control the model settings myself&lt;/li&gt;
&lt;li&gt;I can change the model without changing the whole project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model name is basically in one place, so trying a bigger Gemma should be as simple as changing that.&lt;/p&gt;

&lt;p&gt;Of course, there is a downside. &lt;strong&gt;The 1B model isn't that good.&lt;/strong&gt; You can see that from my own output. It put &lt;code&gt;holiday-selfie&lt;/code&gt; into Documents and &lt;code&gt;budget.xyz&lt;/code&gt; into Code. The &lt;code&gt;digest&lt;/code&gt; command also said &lt;code&gt;"No action needed"&lt;/code&gt; even though the chat clearly had tasks in it.&lt;/p&gt;

&lt;p&gt;So I'm not going to pretend this is some perfect AI assistant. A larger Gemma might do much better, but I haven't tested that yet.&lt;/p&gt;

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

&lt;p&gt;I didn't use DevRelay for this. The whole process is visible in the GitHub repository through the issues and pull requests. You can see Copilot's plans, commits, session information and my review comments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue #1 / PR #2: &lt;a href="https://github.com/davidnize/sloth-cli/pull/2" rel="noopener noreferrer"&gt;https://github.com/davidnize/sloth-cli/pull/2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tidy&lt;/code&gt;: &lt;a href="https://github.com/davidnize/sloth-cli/pull/4" rel="noopener noreferrer"&gt;https://github.com/davidnize/sloth-cli/pull/4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bug fix: &lt;a href="https://github.com/davidnize/sloth-cli/pull/7" rel="noopener noreferrer"&gt;https://github.com/davidnize/sloth-cli/pull/7&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;digest&lt;/code&gt;: &lt;a href="https://github.com/davidnize/sloth-cli/pull/9" rel="noopener noreferrer"&gt;https://github.com/davidnize/sloth-cli/pull/9&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I haven't tested Sloth enough to say something like "it saved me 10 hours." I only tested it with sample files and a short sample chat, so I don't want to make up a number.&lt;/p&gt;

&lt;p&gt;What I do like is the preview-first approach. I can look at what Sloth plans to do, make sure it looks right, and then use &lt;code&gt;--apply&lt;/code&gt;. So instead of manually sorting everything myself, I just check the plan once and let Sloth do the boring part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I wanted to be a little lazier.&lt;/strong&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Best Use of GitHub Copilot
&lt;/h3&gt;

&lt;p&gt;Most of the project was built by creating issues and assigning them to Copilot's coding agent. I also included the parts where Copilot wasn't perfect: the bug it fixed, the mistake I caught in its fix, and the point where it ran out of credits and I had to take over. I think that shows the actual process better than pretending the AI wrote perfect code from the beginning.&lt;/p&gt;

&lt;p&gt;I also added GitHub Actions myself so the tests run automatically on every push and pull request.&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%2F9mixuuhgbh080pnhab2t.jpeg" 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%2F9mixuuhgbh080pnhab2t.jpeg" alt=" " width="800" height="852"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Use of Gemma
&lt;/h3&gt;

&lt;p&gt;Gemma 3 (1B) is the only AI model in the project. It runs locally through Ollama and is used for both &lt;code&gt;tidy&lt;/code&gt; and &lt;code&gt;digest&lt;/code&gt;. Nothing is sent to a cloud AI API.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Pocket Ledger, a local expense log for freelancers</title>
      <dc:creator>David Nizeyimana</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:29:40 +0000</pubDate>
      <link>https://dev.to/david_nize/pocket-ledger-a-local-expense-log-for-freelancers-372b</link>
      <guid>https://dev.to/david_nize/pocket-ledger-a-local-expense-log-for-freelancers-372b</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;I built &lt;strong&gt;Pocket Ledger&lt;/strong&gt;, a local expense log for freelancers.&lt;/p&gt;

&lt;p&gt;The idea is simple. Freelancers have a lot of small expenses: a train ticket, lunch with a client, office supplies, a software subscription, or a utility payment.&lt;/p&gt;

&lt;p&gt;You enter a description, an optional merchant, an amount, and a date. Pocket Ledger saves the expense, lists your entries, shows the total, and lets you remove an entry when you need to.&lt;/p&gt;

&lt;p&gt;The part I wanted to add was automatic categorization without sending those expense details to a hosted AI service.&lt;/p&gt;

&lt;p&gt;When &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; is running, Pocket Ledger uses &lt;code&gt;qwen2.5:3b&lt;/code&gt; locally to choose one of these categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advertising&lt;/li&gt;
&lt;li&gt;Meals&lt;/li&gt;
&lt;li&gt;Office supplies&lt;/li&gt;
&lt;li&gt;Software&lt;/li&gt;
&lt;li&gt;Travel&lt;/li&gt;
&lt;li&gt;Utilities&lt;/li&gt;
&lt;li&gt;Other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also generates a short note explaining the categorization.&lt;/p&gt;

&lt;p&gt;A freelancer could pay for a tool like this because their expense descriptions and merchant names stay on their own computer instead of being sent to a hosted chat account.&lt;/p&gt;

&lt;p&gt;I haven't charged anyone for it.&lt;/p&gt;

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

&lt;p&gt;Pocket Ledger is live on Render:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pocket-ledger-vepy.onrender.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Open Pocket Ledger →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try the expense ledger directly in the browser.&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%2Fmrzpt8y57uc5s058rve9.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%2Fmrzpt8y57uc5s058rve9.png" alt="Pocket-ledger-web" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app is designed to run locally on your computer.&lt;/p&gt;

&lt;p&gt;To run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;ollama pull qwen2.5:3b
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then open:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If Ollama is not running, the ledger still works. You can choose a category yourself and save the expense.&lt;/p&gt;

&lt;p&gt;The app does not download an AI model for you. You install Ollama and the model locally.&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/davidnize" rel="noopener noreferrer"&gt;
        davidnize
      &lt;/a&gt; / &lt;a href="https://github.com/davidnize/Side-Hustle" rel="noopener noreferrer"&gt;
        Side-Hustle
      &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;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Pocket Ledger&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A tiny, local-first expense sorter for freelancers. Add an expense and an open-weight AI model assigns a category and gives a short explanation. Review spending totals, keep a local record, and remove entries when needed.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open AI?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Pocket Ledger sends expense descriptions only to the Ollama service running on your own computer. The app does not call a hosted AI API, and the expense ledger is stored in &lt;code&gt;data/expenses.json&lt;/code&gt; on that computer. That keeps client and business details out of another company's account, avoids per-request model fees, and lets you change the model with an environment variable. Once the model has been downloaded, categorization can run without an internet connection.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Node.js 18 or newer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com/" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; installed and running&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run it&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;npm install
ollama pull qwen2.5:3b
npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then visit &lt;a href="http://127.0.0.1:3000" rel="nofollow noopener noreferrer"&gt;http://127.0.0.1:3000&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ollama is the only AI service used. The first model download requires an internet connection…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidnize/Side-Hustle" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The main parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server.js&lt;/code&gt; — the Express server and expense logic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;public/&lt;/code&gt; — the frontend&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data/expenses.json&lt;/code&gt; — the local expense data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ledger starts with an empty JSON array and stores the expenses on the same computer running the application.&lt;/p&gt;

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

&lt;p&gt;Pocket Ledger is built with &lt;strong&gt;Node.js, Express, HTML, CSS, and  JavaScript&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is no hosted AI API.&lt;/p&gt;

&lt;p&gt;When Ollama is running, the server sends the expense description and optional merchant to Ollama at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:11434
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qwen2.5:3b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is asked to return JSON containing a category and a short note.&lt;/p&gt;

&lt;p&gt;The server then checks that the category is one of the seven categories supported by the application.&lt;/p&gt;

&lt;p&gt;If Ollama cannot be reached, I choose the category myself from the same list. The expense is still saved, and the app records that the category was chosen manually.&lt;/p&gt;

&lt;p&gt;I also added some basic validation around the amount. It has to be a valid positive decimal, and the value stored by the application is rounded to cents.&lt;/p&gt;

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

&lt;p&gt;This is probably the part of the project I care about the most.&lt;/p&gt;

&lt;p&gt;The expense description and merchant name stay on my computer. The AI call goes to Ollama running locally on &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means I don't need to send someone's expense details to a closed AI API just to get a category.&lt;/p&gt;

&lt;p&gt;Once &lt;code&gt;qwen2.5:3b&lt;/code&gt; is downloaded, the categorization can also work without an internet connection.&lt;/p&gt;

&lt;p&gt;Another thing I like about using a local model is that the application is not completely tied to one provider. I can point &lt;code&gt;OLLAMA_MODEL&lt;/code&gt; at another local model that can return the required JSON without changing the frontend.&lt;/p&gt;

&lt;p&gt;And if the AI is unavailable, the actual expense tracker still works.&lt;/p&gt;

&lt;p&gt;That was important to me. The AI should help with the job, not become a requirement for the basic ledger.&lt;/p&gt;

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

&lt;p&gt;I haven't had a paying user yet.&lt;/p&gt;

&lt;p&gt;The project is still at the prototype stage, so I don't want to pretend that someone has paid for it when they haven't.&lt;/p&gt;

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

&lt;p&gt;Pocket Ledger is a small project, but I wanted it to solve a real problem in a simple way.&lt;/p&gt;

&lt;p&gt;The main idea is not just &lt;em&gt;"use AI to categorize expenses."&lt;/em&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Use AI locally when the information being processed doesn't need to leave your computer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is still a lot I could improve, but the basic workflow works: enter an expense, categorize it with a local model or manually, save it, and keep the data on your own machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's Pocket Ledger.
&lt;/h2&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Tonight’s Sky: a local Gemma model that tells you when to go outside</title>
      <dc:creator>David Nizeyimana</dc:creator>
      <pubDate>Mon, 12 Oct 2026 03:19:01 +0000</pubDate>
      <link>https://dev.to/david_nize/tonights-sky-a-local-gemma-model-that-tells-you-when-to-go-outside-2dcc</link>
      <guid>https://dev.to/david_nize/tonights-sky-a-local-gemma-model-that-tells-you-when-to-go-outside-2dcc</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;&lt;strong&gt;Tonight's Sky&lt;/strong&gt; is a small web page that tells you when to step outside and look up. It works out today's sunset time and the moon's phase, then a local AI model turns those numbers into a short, friendly tip, like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Head out a little before sunset at 18:05. It's a new moon, so the sky will get very dark about an hour after, perfect for stargazing. Bring a flashlight.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's for anyone who keeps meaning to go outside and watch the sky but never knows when. Open the page, read three sentences, and go.&lt;/p&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%2Fw5pbexnc3f43gfe68fwm.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%2Fw5pbexnc3f43gfe68fwm.png" alt="Two night-sky photos with stars above the Tonight's Sky app showing sunset 18:05, a 2% moon and the plan" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The top of the image is the night sky I photographed, and the bottom is the app on the same night.&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/davidnize" rel="noopener noreferrer"&gt;
        davidnize
      &lt;/a&gt; / &lt;a href="https://github.com/davidnize/touch-grass" rel="noopener noreferrer"&gt;
        touch-grass
      &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;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🌙 Tonight's Sky&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A small web page that tells you when to step outside and look up.&lt;/p&gt;
&lt;p&gt;It works out today's sunset time and the moon's phase, then a local AI model (Gemma, running through Ollama) turns those facts into a short, friendly tip:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Head out a little before sunset at 18:05. It's a new moon, so the sky will get very dark about an hour after, perfect for stargazing. Bring a flashlight.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;suncalc&lt;/code&gt; calculates sunset, moon phase and moonrise from your coordinates and the clock.&lt;/li&gt;
&lt;li&gt;The code turns those numbers into one clear sentence (for example "It is a new moon" or "The moon is 60% full and rises at 20:15").&lt;/li&gt;
&lt;li&gt;Gemma (&lt;code&gt;gemma3:1b&lt;/code&gt;) writes the tip from that sentence, running locally through Ollama.&lt;/li&gt;
&lt;li&gt;Express and EJS show the result on one page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidnize/touch-grass" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sky math:&lt;/strong&gt; &lt;code&gt;suncalc&lt;/code&gt; calculates sunset, moon phase and moonrise from my coordinates and the clock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-weight AI:&lt;/strong&gt; Gemma (&lt;code&gt;gemma3:1b&lt;/code&gt;) runs locally through Ollama and writes the tip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App:&lt;/strong&gt; Node.js with Express, EJS templates and Axios to call Ollama.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flow is: &lt;code&gt;suncalc&lt;/code&gt; produces the facts, my code turns them into one clear sentence (for example "The moon is 60% full and rises at 20:15", or "It is a new moon"), and Gemma writes the tip from that.&lt;/p&gt;

&lt;p&gt;Two things I learned along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A small model needs an example, not rules.&lt;/strong&gt; My first prompts said things like "never say I" and "don't invent details", and Gemma ignored them. One example tip in the prompt fixed the tone and made it include the sunset time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the facts before asking the AI.&lt;/strong&gt; Early on, it told me to go and see a moon that had set hours earlier. Now my code works out whether the moon is really visible tonight and only gives Gemma true facts.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The whole thing runs on my own computer. With an open-weight model I don't need an API key, an account or a paid plan, and nothing about where I live or when I go outside is sent to a third party. Setup needs the internet once (installing packages, Ollama and the model), and after that the app doesn't call any online service. That suits a project about getting away from screens and connections.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; every tip on the page is written by Gemma (&lt;code&gt;gemma3:1b&lt;/code&gt;), an open-weight model that runs locally on my own computer through Ollama. The app works out the real sunset and moon facts first, and Gemma turns them into a short plan for the reader.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Between Us: a private diary for two people who live far apart</title>
      <dc:creator>David Nizeyimana</dc:creator>
      <pubDate>Mon, 05 Oct 2026 04:18:29 +0000</pubDate>
      <link>https://dev.to/david_nize/between-us-a-private-diary-for-two-people-who-live-far-apart-c3a</link>
      <guid>https://dev.to/david_nize/between-us-a-private-diary-for-two-people-who-live-far-apart-c3a</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;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Between Us&lt;/strong&gt; is a small private diary for me and my pen pal, someone I've loved for a very long time who now lives far away. We used to talk face to face. Now we write to each other here, in one shared feed, instead of in a chat app. We each log in with a name and a shared passphrase. My entries appear on the right and theirs on the left.&lt;/p&gt;

&lt;p&gt;One rule shaped everything: &lt;strong&gt;your words stay yours.&lt;/strong&gt; Entries are saved exactly as typed. The AI never rewrites or "improves" them, because the whole point is to hear each other's real voice.&lt;/p&gt;

&lt;p&gt;A local AI adds three small things around the entries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A question of the day&lt;/strong&gt; in the entry box, in case you don't know where to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A verse&lt;/strong&gt; in the header each day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A short recap every Saturday night&lt;/strong&gt;, written by Gemma and marked as AI-written. Sunday to Friday, there is no recap.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1mGz4T9oyBE" width="710" height="399"&gt;
  &lt;/iframe&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/davidnize" rel="noopener noreferrer"&gt;
        davidnize
      &lt;/a&gt; / &lt;a href="https://github.com/davidnize/build-for-a-friend" rel="noopener noreferrer"&gt;
        build-for-a-friend
      &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;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Between Us&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Scars leave beautiful traces.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A small private diary for two people who used to talk face to face and now live far apart. Each of you writes entries, and you read each other's in one shared feed.&lt;/p&gt;
&lt;p&gt;Built for the Hacktoberfest Weekend Challenge: &lt;strong&gt;Build for a Friend&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your words stay yours.&lt;/strong&gt; Entries are saved exactly as you type them. No AI rewrites or edits them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A question of the day.&lt;/strong&gt; The grey placeholder in the entry box is a short question, written fresh each day by Gemma. You can answer it or write about something else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"This week between us."&lt;/strong&gt; Every Saturday at 9 pm, Gemma reads the week's entries and writes a short recap. It is always labelled as AI-written, so it is never confused with your own words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private.&lt;/strong&gt; You log in with your name and a shared passphrase.&lt;/li&gt;
&lt;li&gt;Your entries on…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidnize/build-for-a-friend" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;The app is Node.js and Express, with EJS templates, plain CSS and PostgreSQL. The AI is &lt;strong&gt;Gemma 3, an open-weight model, running locally through Ollama.&lt;/strong&gt; Nothing is sent to an AI service.&lt;/p&gt;

&lt;p&gt;Gemma does three jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Questions.&lt;/strong&gt; It writes a fresh question each day. After someone posts, it writes the next question for that person in the background. A backup question shows up instantly, so nobody waits for the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verses.&lt;/strong&gt; Gemma only picks a &lt;em&gt;reference&lt;/em&gt;, like Psalm 34:18. The text itself comes from a Bible API, so the model never writes scripture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recaps.&lt;/strong&gt; Every Saturday at 9 pm, it reads the week's entries and writes a short recap. The prompt tells it not to invent anything, give advice, or rewrite anyone's words.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Background jobs run when the server starts and every 15 minutes after that, so if my computer was off at the scheduled time, the missing question, verse or recap is created when it's back on.&lt;/p&gt;

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

&lt;p&gt;A diary is the most private thing you can write, and this one is between two people. Running an open-weight model on my own computer means the entries never leave it. A closed API would have meant sending our private words to a server I don't control just to get a recap.&lt;/p&gt;

&lt;p&gt;It also costs nothing to run and needs no account or API key. And because the model is a single setting (&lt;code&gt;ollamaModel&lt;/code&gt;), I can swap it for another one by changing one line.&lt;/p&gt;

&lt;p&gt;To be exact about what goes online: the only outside request is the verse lookup, and it contains a Bible reference, never anything we wrote.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
