<?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: Nathan C.</title>
    <description>The latest articles on DEV Community by Nathan C. (@natuworkguy).</description>
    <link>https://dev.to/natuworkguy</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%2F3939269%2Fc5249dbd-4da9-49ee-8dfd-78b669b311fa.png</url>
      <title>DEV Community: Nathan C.</title>
      <link>https://dev.to/natuworkguy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/natuworkguy"/>
    <language>en</language>
    <item>
      <title>Never lose an idea again with Ember</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Thu, 22 Oct 2026 07:05:41 +0000</pubDate>
      <link>https://dev.to/natuworkguy/stop-losing-ideas-with-ember-4fc9</link>
      <guid>https://dev.to/natuworkguy/stop-losing-ideas-with-ember-4fc9</guid>
      <description></description>
    </item>
    <item>
      <title>I kept losing ideas, so I built an Apple Watch app that feeds my AI agent</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Thu, 22 Oct 2026 05:53:53 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-kept-losing-ideas-so-i-built-an-apple-watch-app-that-feeds-my-ai-agent-1941</link>
      <guid>https://dev.to/natuworkguy/i-kept-losing-ideas-so-i-built-an-apple-watch-app-that-feeds-my-ai-agent-1941</guid>
      <description>&lt;p&gt;How many good ideas did you lose this week?&lt;/p&gt;

&lt;p&gt;Not bad ideas. Good ones. The kind that show up in the shower, on a walk, or just before you fall asleep, when your phone is nowhere near you. You tell yourself you'll remember. You don't.&lt;/p&gt;

&lt;p&gt;The mind is for having ideas, not for holding them.&lt;/p&gt;

&lt;p&gt;Every note app has the same flaw: it asks for work at the exact moment you have the least patience for it. Find the phone. Unlock it. Open the app. Start typing. By then, the idea has cooled.&lt;/p&gt;

&lt;p&gt;The best tools don't ask for your attention. They get out of its way.&lt;/p&gt;

&lt;p&gt;So I set one rule: &lt;strong&gt;one tap, one sentence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;Ember&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;Ember is an Apple Watch app, a small Python server, and a Flash extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Apple Watch                      your computer                       Flash
 -----------                      -------------                       -----
 tap, speak, save  -- Wi-Fi --&amp;gt;   ember serve  &amp;lt;-- extension ---  "what ideas did I
 kept on the watch                notes.db, recordings              have this week about
 until home                       a banner on each new idea         my game engine?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standalone watch app.&lt;/strong&gt; No iPhone app, and no iPhone needed at all. It even works on a Family Setup watch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local.&lt;/strong&gt; The server is one SQLite file on your computer. Nothing goes anywhere else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queryable by an AI agent.&lt;/strong&gt; &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, my local agent, can search, add to, and act on your ideas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payoff is the last part. I can ask Flash, "What ideas did I have this week about my game engine?" and it searches my notes and quotes them back with dates. It asks me before it saves, changes, or deletes anything.&lt;/p&gt;

&lt;p&gt;A note stops being a note and becomes a working memory.&lt;/p&gt;

&lt;p&gt;A note you never reread is just a memory with extra steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five problems that made this interesting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. watchOS has no speech recognition API
&lt;/h3&gt;

&lt;p&gt;I assumed I'd use Apple's Speech framework. I was wrong. &lt;code&gt;SFSpeechRecognizer&lt;/code&gt; and the newer &lt;code&gt;SpeechAnalyzer&lt;/code&gt; exist on iPhone, iPad, Mac and Vision Pro. Not on watchOS.&lt;/p&gt;

&lt;p&gt;So Ember uses the watch's own system dictation through SwiftUI's &lt;code&gt;TextFieldLink&lt;/code&gt;. It's the same engine as Messages. On Apple Watch Series 9 and later, and Ultra 2, it runs entirely on the wrist.&lt;/p&gt;

&lt;p&gt;For everything else there's a &lt;strong&gt;Record&lt;/strong&gt; button. The watch saves a 16 kHz WAV, and when it syncs, the server transcribes it locally with Moonshine or Vosk. The text then syncs back to the watch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A note must never be lost
&lt;/h3&gt;

&lt;p&gt;Every idea is written to &lt;strong&gt;SwiftData on the watch first&lt;/strong&gt;, before anything touches the network. If you're on a trail with no signal, it waits and keeps trying.&lt;/p&gt;

&lt;p&gt;watchOS also wakes the app about every half hour to retry in the background, so notes go up even if you never open it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Syncing flaky networks without duplicates
&lt;/h3&gt;

&lt;p&gt;The client makes the note's UUID, so sending the same note twice saves it once. The API is idempotent by design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/notes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4a9c..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chunked terrain streaming for the #engine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-21T08:15:00.000000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"watch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"engine"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"audio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The watch pulls changes with &lt;code&gt;GET /notes?since=&amp;lt;rev&amp;gt;&lt;/code&gt;. Every write bumps a revision number, and deletions come back as tombstones, so a note deleted on your computer disappears from the watch too.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pairing without typing an IP address
&lt;/h3&gt;

&lt;p&gt;Nobody wants to type &lt;code&gt;192.168.1.42&lt;/code&gt; on a watch keyboard.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ember pair&lt;/code&gt; prints a code like &lt;code&gt;2232 4829 136&lt;/code&gt;. The code carries your computer's address, so you never type an IP or a &lt;code&gt;.local&lt;/code&gt; name, and the last digit catches typos. It works once, for ten minutes, and five wrong tries use it up. The token then goes into the watch's Keychain.&lt;/p&gt;

&lt;p&gt;If your router later hands the computer a new address, the watch falls back to the &lt;code&gt;.local&lt;/code&gt; name it learned while pairing.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Making it feel instant
&lt;/h3&gt;

&lt;p&gt;The watch has a big capture button, complications for any watch face, Siri ("New idea in Ember"), the Smart Stack, and the Action Button on an Ultra. Say "hashtag engine" and the note is tagged &lt;code&gt;#engine&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the computer, &lt;code&gt;ember&lt;/code&gt; opens a full-screen terminal browser with search-as-you-type, tag filtering, and playback of recordings. Ideas from your watch slide in while it's open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why local?
&lt;/h2&gt;

&lt;p&gt;Your thoughts are the most personal data you have. They should live where you do.&lt;/p&gt;

&lt;p&gt;There's no account to create, no subscription, and no third party holding a copy. The cost is that you run a server. I think that's a good trade, and &lt;code&gt;scripts/server.sh&lt;/code&gt; makes it a one-liner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you if you build something like this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Save first, sync later&lt;/strong&gt; is a better rule than I expected. Treat the network as an optimization, never a dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency is a gift.&lt;/strong&gt; Let the client pick the ID and a whole category of sync bugs disappears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform limits help.&lt;/strong&gt; No Speech API on the watch forced a simpler, more robust design than I'd have chosen.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I want Ember to do more with what it holds: connecting related ideas and surfacing an old one at the right moment.&lt;/p&gt;

&lt;p&gt;The future won't belong to those with the most ideas. It will belong to those who keep them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Ember is MIT licensed. &lt;a href="https://github.com/Natuworkguy/Ember" rel="noopener noreferrer"&gt;Natuworkguy/Ember&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every tool you trust began as a thought someone almost let go.&lt;/p&gt;

&lt;p&gt;If you've ever lost a great idea in the shower, I'd love to hear how you capture them. Tell me in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of my series on building Flash. Follow along at &lt;a href="https://dev.to/natuworkguy"&gt;dev.to/natuworkguy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Catch the ember before it goes out.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>swift</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>I'm finally fine-tuning my model, because its prompt hit 175,000 characters</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:48:17 +0000</pubDate>
      <link>https://dev.to/natuworkguy/im-finally-fine-tuning-my-model-because-its-prompt-hit-175000-characters-528a</link>
      <guid>https://dev.to/natuworkguy/im-finally-fine-tuning-my-model-because-its-prompt-hit-175000-characters-528a</guid>
      <description>&lt;p&gt;Line 60 of my model's prompt says "Never output em-dashes, in any form." Line 976 says it again, in case it forgot. The agent it runs in says it a third time. It still slips them in, so that agent ends up ripping them out of every reply in code, a token at a time, as it streams.&lt;/p&gt;

&lt;p&gt;That's when I admitted the prompt had gotten out of hand.&lt;/p&gt;

&lt;p&gt;Every version of Onyx, the model behind my local AI agent &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, has secretly been a prompt. Not fine-tuned weights. A Modelfile wrapped around an open-weights base, with the system prompt doing all the work. Every time Onyx did something dumb, I wrote a rule. Onyx 2.6's prompt is now 174,826 characters long. That's about 980 lines of rules the model has to keep in mind while it works, and every new chat pays for all of them before it writes its first word. With Flash's own prompt and tool list on top, a fresh chat starts at around 256,000 characters.&lt;/p&gt;

&lt;p&gt;So I'm finally doing the thing I put off for eight versions: fine-tuning. Onyx 3 moves the prompt into the weights. The plan cuts the system prompt to 650 characters and a new chat to around 82,000.&lt;/p&gt;

&lt;p&gt;The training pipeline is done and tested end to end. The real run hasn't happened yet. Here's how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distillation, not a dataset
&lt;/h2&gt;

&lt;p&gt;I don't have a dataset of "good Onyx answers", and writing thousands by hand wasn't happening. But I do have a model that already gives them: Onyx 2.6, with its giant prompt, on the biggest base it runs on.&lt;/p&gt;

&lt;p&gt;So 2.6 is the teacher. It answers a few thousand requests the way it would inside Flash, tools and all. Then the smaller base gets fine-tuned to give those same answers under the 650-character prompt. What the long prompt taught becomes what the model does on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the requests come from
&lt;/h2&gt;

&lt;p&gt;I wrote 161 requests by hand across 22 categories: code, work, everyday life, knowledge and plain talk. A script has the teacher write more in the same style until there are a couple thousand, weighted toward code. Another 54 are held back for the benchmark and never trained on.&lt;/p&gt;

&lt;p&gt;Each request gets answered twice, inside a fresh copy of a small fixture project (a little Python shop, a webpage, a folder of notes and receipts). The teacher really uses Flash's tools: it reads files, edits them and runs the tests. Since it's running real shell commands, that step runs inside a throwaway Docker container that holds nothing else.&lt;/p&gt;

&lt;p&gt;Every conversation also starts Flash with an empty home folder. My memories, sparks, settings and email never reach a prompt, so they never reach the weights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grading the teacher
&lt;/h2&gt;

&lt;p&gt;The teacher isn't perfect either, which is kind of the point of all those rules. So every answer has to pass two filters before it's trained on.&lt;/p&gt;

&lt;p&gt;First, hard rule checks in code. If it breaks one, it's dropped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no em dash, and nothing standing in for one (a double hyphen, a spaced dash between clauses)&lt;/li&gt;
&lt;li&gt;no "I checked" or "I ran" unless a tool actually ran this turn&lt;/li&gt;
&lt;li&gt;no tool call typed out as text instead of made&lt;/li&gt;
&lt;li&gt;a real reply at the end of every turn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Softer ones, like "great question!", "hope this helps" or emoji nobody asked for, only cost points.&lt;/p&gt;

&lt;p&gt;Then a judge model scores what's left out of 10. Anything under 7 is out, and of the two answers to each request, the better one wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things Onyx 3 does that 2.6 doesn't
&lt;/h2&gt;

&lt;p&gt;Since I was retraining anyway, I added two rules, put last in the teacher's prompt so they win.&lt;/p&gt;

&lt;p&gt;It writes the way you do. Type "hey" and you get "hey ..." back. Type "Hey" and you get "Hey, ...". Half the training requests are lowercase and half aren't, so it learns both. A conversation that gets the casing wrong is never trained on.&lt;/p&gt;

&lt;p&gt;And it never uses an em dash. Yes, that one again. The prompt asking twice didn't work, and Flash's filter only cleans up afterward. This time the rule goes where the habit lives. Any teacher answer with a single em dash in it, or a double hyphen pretending not to be one, is thrown out before training, so Onyx 3 never sees one written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training only on what it says
&lt;/h2&gt;

&lt;p&gt;The detail I like most: each training example is a whole conversation, with Flash's prompt, the tool list, the request and every tool result. But only the model's own words carry labels. In one example I checked, that was 70 tokens out of 23,713. Everything else is context it reads but is never trained to produce. Otherwise it would spend the run learning to recite the tool list.&lt;/p&gt;

&lt;p&gt;It's LoRA on the attention and feed-forward weights, rank 32, two epochs. The 12b trains in bf16 on one 80 GB GPU, or in 4-bit on a 24 GB card, and it runs on Colab. My rough guess is 5 to 15 hours for the 12b, but the first ten steps print the real rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Made for each other
&lt;/h2&gt;

&lt;p&gt;Onyx and Flash are built for each other. Three quarters of the training conversations happen inside Flash, with its real prompt, its real tools and real projects, so Onyx learns to read a file before editing it, run the tests, and stop when the job's done. It goes the other way too. Onyx's prompt is full of Flash: how to use its tools, how to write a long file in pieces so a reply never gets cut off, and how to behave when it's running as a spark on its own. Flash is the agent Onyx was shaped in, and Onyx is the model Flash recommends.&lt;/p&gt;

&lt;p&gt;You can run Flash on any Ollama model that calls tools, and Onyx answers fine in a plain &lt;code&gt;ollama run&lt;/code&gt;. But Onyx works best with Flash, and Flash works best with Onyx. Onyx 3 makes that even more true, since the 175,000 characters of how to be a good Flash agent are now in the weights instead of in every chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I'll know it worked
&lt;/h2&gt;

&lt;p&gt;The benchmark runs 2.6 and 3 side by side on the 54 held-out requests. For each model it reports the judge's score, the share of answers that break no hard rule, the prompt tokens a new chat starts with, seconds to first reply from cold, and tokens per second.&lt;/p&gt;

&lt;p&gt;The bar for shipping: match 2.6 on the first two, beat it on the rest. If the small prompt can't match the big one on quality, Onyx 3 doesn't ship, however fast it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could test without a GPU
&lt;/h2&gt;

&lt;p&gt;I ran the whole pipeline end to end on a CPU, with a stand-in teacher and a tiny model shaped like the real base: request generation with Flash's real tools in the sandbox, judging, building the dataset, training, merging, GGUF conversion, building it into Ollama, and the benchmark. Everything connects.&lt;/p&gt;

&lt;p&gt;What that can't tell me is whether Onyx 3 is any good. That takes the real teacher, the real base and a rented GPU, and that's next. Onyx 3 is Gemma 4 fine-tuned, Apache 2.0, and it'll run anywhere Ollama does.&lt;/p&gt;

&lt;p&gt;I'll post the benchmark either way, including if 3 loses to the 175,000-character prompt.&lt;/p&gt;

&lt;p&gt;Have you distilled a long system prompt into weights? I'd like to know what went wrong for you before it goes wrong for me.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>showdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>My local AI agent now has coworkers, a wake word, and takes my meeting notes</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:34:54 +0000</pubDate>
      <link>https://dev.to/natuworkguy/my-local-ai-agent-now-has-coworkers-a-wake-word-and-takes-my-meeting-notes-5bb2</link>
      <guid>https://dev.to/natuworkguy/my-local-ai-agent-now-has-coworkers-a-wake-word-and-takes-my-meeting-notes-5bb2</guid>
      <description>&lt;p&gt;When I get an idea away from my computer, I tap a button on my Apple Watch's home screen and say it out loud. It lands in my Apple Notes. My agents are watching my notes, and by the time I'm back at my desk, they've already started building it.&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, an open source AI agent I've been building for the terminal and the browser. It runs on Ollama models you pick, with no API keys and no account, and nothing leaves your computer unless you tell it to. The dev branch is 173 commits ahead of main now. Here's what's in it, starting with the two pieces that make the Watch trick work.&lt;/p&gt;

&lt;h2&gt;
  
  
  It keeps working after you close the chat
&lt;/h2&gt;

&lt;p&gt;Most agents stop the moment you stop typing. Flash has sparks: small agents you give a name, a standing goal and a schedule, and they just keep going. "Watch the issues on my repo and tell me about new bugs every morning." "Check the price of this every hour." Each run is a shift, and each shift ends with a report you can thumbs up or down. A spark reads every piece of feedback you've ever given it before every shift, so you only have to say "only tell me about the ones labelled bug" once.&lt;/p&gt;

&lt;p&gt;Turn on sparks always on and they keep working with Flash closed. The OS starts them at login.&lt;/p&gt;

&lt;p&gt;Then it got out of hand, in a good way. Sparks can be on teams now. Teams have leads, an org chart that comes alive while they work, budgets by the hour, day, week or month, and an audit log of every tool call. A lead can propose hiring a new spark, and you approve it like a pull request. Every team has a group chat where you and the sparks talk in one room, with @mentions, replies, emoji reactions and read ticks. A spark can decide to stay quiet when it has nothing to add, which is more than I can say for most group chats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connectors (MCP)
&lt;/h2&gt;

&lt;p&gt;Flash speaks the Model Context Protocol now. &lt;code&gt;/mcp add github npx -y @modelcontextprotocol/server-github&lt;/code&gt; and GitHub's tools join Flash's own. Same for Notion, Postgres, your filesystem, anything with an MCP server, local or remote. It reads the usual &lt;code&gt;mcpServers&lt;/code&gt; config, so you can paste one in from wherever you already have it. Read-only tools run straight away. Anything that changes something asks you first, unless you've put Flash in autonomous mode.&lt;/p&gt;

&lt;p&gt;The Watch trick is just this. I added &lt;a href="https://github.com/sweetrb/apple-notes-mcp" rel="noopener noreferrer"&gt;sweetrb/apple-notes-mcp&lt;/a&gt; as a connector, which gives my sparks tools to read my Apple Notes, then gave a spark the goal of watching for new ideas there and getting them started. The Watch button is just a fast way to get a note written. Flash does the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Hey Flash"
&lt;/h2&gt;

&lt;p&gt;Voice mode got a wake word. Say "Hey Flash" with the page open, or flip on the background service and it works with no tab open at all. A slider sets how eager it is, so a YouTube video in the background doesn't set it off.&lt;/p&gt;

&lt;p&gt;Everything you hear and say stays on your machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listening is Vosk, or Moonshine if you want it to actually get your words right. Moonshine streams, so the words on screen while you talk are already the right ones.&lt;/li&gt;
&lt;li&gt;Speaking is Piper, or Kokoro, which sounds startlingly human for something running on a laptop CPU.&lt;/li&gt;
&lt;li&gt;Point your camera at something mid-conversation and ask about it.&lt;/li&gt;
&lt;li&gt;Say "interrupt" to cut it off mid-sentence. It can also end the conversation itself when you say bye, so you're not stuck talking to a dial tone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It takes your meeting notes
&lt;/h2&gt;

&lt;p&gt;Hit + then Record a meeting. Flash writes down what's said as it's said, live, on your computer. When you stop, the transcript lands in a new chat and the model writes up the summary, what got decided, who's doing what by when, and what's still open.&lt;/p&gt;

&lt;p&gt;There's no bot joining your call, no upload and no "this meeting is being recorded by". It's the same idea as recording your screen to teach Flash a procedure, which you can also do now.&lt;/p&gt;

&lt;h2&gt;
  
  
  It can pause your music
&lt;/h2&gt;

&lt;p&gt;Off by default. Turn it on and "stop the music", "turn it down" and "open Spotify" just happen. It plays and pauses anything (a YouTube tab, a call, any app) through the media keys. It also opens and quits apps, sets the volume, locks the screen and flips dark mode. It doesn't click around your screen. Yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stuff I use every day
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Replies aren't just text anymore. Flash answers with charts, calculators, forms and buttons when those say it better.&lt;/li&gt;
&lt;li&gt;It writes music and plays it in its own player. It builds 3D models and textures them. It drafts designs you can comment on.&lt;/li&gt;
&lt;li&gt;System One: in autonomous mode, a small fast model looks over every command and edit before it runs, and stops the sketchy ones.&lt;/li&gt;
&lt;li&gt;Steer it while it works ("actually use the other file"), or queue the next thing without waiting.&lt;/li&gt;
&lt;li&gt;Edit a message you already sent and keep every version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/btw&lt;/code&gt; asks a side question without derailing the task.&lt;/li&gt;
&lt;li&gt;Email, across as many accounts as you have, plus an Inbox spark that sorts it every 30 minutes and drafts replies you approve.&lt;/li&gt;
&lt;li&gt;When it needs a package, it shows an Install button and lets you watch the logs.&lt;/li&gt;
&lt;li&gt;If it misread your typo and titled the chat something dumb, it retypes the title in front of you once it figures out what you meant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One thing I learned
&lt;/h2&gt;

&lt;p&gt;Models love em dashes. You can ask nicely in the system prompt and they'll keep doing it. So Flash pulls them out in code, a token at a time as the reply streams in, except inside code where a dash means something. This week I found out sparks were sneaking them through one specific tool in the team chat. That's fixed now. If you build on local models, enforce style in code. Prompts are suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;It's MIT and on GitHub. To get everything above, install from dev:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipx &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"git+https://github.com/Natuworkguy/Flash@dev"&lt;/span&gt;
flash &lt;span class="nt"&gt;--web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need Ollama and a model that can call tools. The repo has a recommended one.&lt;/p&gt;

&lt;p&gt;So, what would you have a spark watch? Tell me in the comments. And if you try it, tell me what breaks.&lt;/p&gt;

&lt;p&gt;If this looks useful, a star on &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; helps more people find it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I gave my local AI agents an org chart, a budget, and a paper trail</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Tue, 06 Oct 2026 05:45:02 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-gave-my-local-ai-agents-an-org-chart-a-budget-and-a-paper-trail-4ff8</link>
      <guid>https://dev.to/natuworkguy/i-gave-my-local-ai-agents-an-org-chart-a-budget-and-a-paper-trail-4ff8</guid>
      <description>&lt;p&gt;Most AI agents die when you close the chat. Mine don't.&lt;/p&gt;

&lt;p&gt;They're called &lt;strong&gt;sparks&lt;/strong&gt;, and they live in &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, my open source agent shell for &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. Each one gets a name, a goal, a schedule, and lines it must never cross. Then it works on its own, on your machine, and reports back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make a spark that checks my repo every morning for new issues and tells me which ones look like bugs. Never comment on anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two sparks were fine. Six were six interns who had never met. So today they got a company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Teams and an org chart
&lt;/h2&gt;

&lt;p&gt;Any spark can &lt;strong&gt;report to a lead&lt;/strong&gt;. Its reports roll up to the lead, and the lead tells you what matters, so you read one report instead of six. Each team is drawn as a live org chart, with reports flying up the lines as comets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/sparks teams add Dev Team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Built-in templates (Dev Team, Personal Desk) &lt;strong&gt;start paused&lt;/strong&gt;, so nothing burns your GPU before you've looked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiring needs your yes
&lt;/h2&gt;

&lt;p&gt;A spark can propose hiring a new spark, or letting one go, with a reason. It waits as a card until you approve or decline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Budgets
&lt;/h2&gt;

&lt;p&gt;Off by default. Turn one on and a spark gets tokens per hour, day, week or month. When it runs out, it pauses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/sparks budget scout 50k/day
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local tokens still cost you: they're GPU time, and a loop at 3am is still a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  An audit log you can't quietly edit
&lt;/h2&gt;

&lt;p&gt;Every shift, tool call, approval and change goes into an append-only log. Each entry carries the hash of the one before it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit a line by hand and verification points at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pause all, Stop all
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pause all&lt;/strong&gt; stops new shifts and lets running ones finish. &lt;strong&gt;Stop all&lt;/strong&gt; is the big red button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real DevTools for the agent
&lt;/h2&gt;

&lt;p&gt;The agent can already drive a web page. Now &lt;code&gt;devtools&lt;/code&gt; gives it the network log, full requests, the console, a JS console, and computed styles. A screenshot can't show a 404. This can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notifications, on your terms
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;notify_user&lt;/code&gt; pops up a toast in the web UI, a browser notification in a background tab, or a desktop notification. It's rate limited, and you set the limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/notify 8 per 30m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The rest
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default model&lt;/strong&gt; for new sparks. If you &lt;code&gt;ollama rm&lt;/code&gt; it, sparks fall back to another model and say so instead of crashing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images&lt;/strong&gt; the agent sends now show inline in the chat. Click one to open it in the side panel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;10 features in one day, 1,916 tests passing, 0 API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then type &lt;code&gt;/sparks new&lt;/code&gt;. Stars on &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; help a lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What job would you give an agent that works for you around the clock?&lt;/strong&gt; The best answer becomes a template.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I gave a 9B model veto power over my AI agent</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Thu, 01 Oct 2026 23:51:41 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-gave-a-9b-model-veto-power-over-my-ai-agent-9ih</link>
      <guid>https://dev.to/natuworkguy/i-gave-a-9b-model-veto-power-over-my-ai-agent-9ih</guid>
      <description>&lt;p&gt;Here is Flash in autonomous mode, asked to fix a typo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; fix the typo in the README

● Edit(README.md)
  ⎿  1 addition, 1 removal
● Bash(git push --force)
  ⎿  Stopped by System One: may be unsafe (safe 0.04)

Fixed the typo. System One stopped the force push, so nothing
left your machine. Want me to push it normally?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The typo was the job. The force push was the agent's own idea, and something told it no.&lt;/p&gt;

&lt;p&gt;Flash is my terminal agent for local models. Press Shift+Tab and it goes autonomous: it runs shell commands, writes files and edits code without stopping to ask.&lt;/p&gt;

&lt;p&gt;That is the whole appeal. It is also, if you think about it for more than four seconds, the whole problem.&lt;/p&gt;

&lt;p&gt;Nobody is there to say no. Or there wasn't, until this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheapest possible no
&lt;/h2&gt;

&lt;p&gt;The usual fix is a bigger model checking the agent's work. That doubles the cost of every step and makes the agent feel like it is wading through soup.&lt;/p&gt;

&lt;p&gt;Ollama 0.35 gave me something better: an endpoint called System One. You don't chat with it. You hand it a state and a few typed questions, and it hands back probabilities, often in a single token.&lt;/p&gt;

&lt;p&gt;There are three question types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;choice&lt;/code&gt;: pick one of 2 to 26 options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;score&lt;/code&gt;: rate something on a scale you define.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noul&lt;/code&gt;: yes or no.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't know why yes/no is called &lt;code&gt;noul&lt;/code&gt;. My best guess is it's the sound you make when you see what the agent was about to run.&lt;/p&gt;

&lt;p&gt;Here is the question behind that stop at the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/v&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;/systemone&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nimble"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fix the typo in README"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;git push --force&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"questions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"safe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"noul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"instructions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Is this safe to run with nobody checking it first?"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"on_task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"noul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"instructions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Does this plainly serve the request?"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer is a probability of yes. For that push it was 0.04, so the push stayed home.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bouncer
&lt;/h2&gt;

&lt;p&gt;In autonomous mode, every call that would normally wait for your yes now goes past System One first: shell, write, edit, starting a background agent, and extension tools that ask. Two questions, safe and on task. If either comes in under 0.5, the call doesn't run. The agent is told why in plain words, and has to find a safer way or ask you.&lt;/p&gt;

&lt;p&gt;Sub-agents and scheduled agents get the same check, held up against their own task.&lt;/p&gt;

&lt;p&gt;The decision I went back and forth on was what happens when System One itself is down. I made it fail closed. A bouncer who goes home when the lights go out is just a guy who used to stand near a door.&lt;/p&gt;

&lt;h2&gt;
  
  
  It takes questions too
&lt;/h2&gt;

&lt;p&gt;The agent also gets an &lt;code&gt;ask_system_one&lt;/code&gt; tool. Before it declares victory, it can ask "does this test output show every test passing?" and get &lt;code&gt;No (probability of yes 0.12)&lt;/code&gt; back in a moment. A second opinion now costs about as much as a sneeze.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I got wrong
&lt;/h2&gt;

&lt;p&gt;System One was days old when I started and the docs were still catching up, so the first version was built from an example in someone else's GitHub issue. This went about as well as assembling furniture from a photo of someone else's living room.&lt;/p&gt;

&lt;p&gt;When the real API reference turned up, two things changed. Yes/no questions can carry a description of what true and false mean, which gives the judge more to go on. And a score runs from 0 to the number of levels minus one, not 0 to 1. For a while my agent was being told things like "Score 1.6, from Harmless (0) to Dangerous (1)". That is a lot of danger.&lt;/p&gt;

&lt;p&gt;Then a browser test clicked &lt;code&gt;nimble&lt;/code&gt; in the wrong section of the menu and made a 9B judge the chat model. A model built to answer in one token is not much of a conversationalist. Models that can only judge are now kept out of the chat list.&lt;/p&gt;

&lt;h2&gt;
  
  
  No hard-coded list
&lt;/h2&gt;

&lt;p&gt;The first picker had three model names typed in by hand. That list was going to age like milk. Now Flash asks Ollama's &lt;code&gt;/api/show&lt;/code&gt; which local models list &lt;code&gt;decision&lt;/code&gt; among their capabilities, and offers exactly those, with None at the top. System One is optional. Some nights you want a bouncer, some nights you want the party.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small things that mattered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Natuworkguy/flash-onyx-2.5:31b-cloudbase&lt;/code&gt; is a fine name for a file and a terrible name for a button. The button says &lt;code&gt;flash-onyx-2.5&lt;/code&gt;. The menu shows the rest.&lt;/li&gt;
&lt;li&gt;Once a chat starts, the model picker fades out of the message box and back in at the top left. Your typing gets the room.&lt;/li&gt;
&lt;li&gt;On an Ollama older than 0.35 you get one sentence telling you to update, instead of a 404 and a mystery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The point
&lt;/h2&gt;

&lt;p&gt;The point of autonomous mode is that you stop waiting. System One adds a check you never wait for.&lt;/p&gt;

&lt;p&gt;Flash is open source at &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;github.com/Natuworkguy/Flash&lt;/a&gt;. Pull &lt;code&gt;nimble&lt;/code&gt;, press Shift+Tab, and let something small say no for you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ollama</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>OpenAI shipped Dots on Tuesday. By Wednesday night my terminal had its own, running 100% locally</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Wed, 30 Sep 2026 22:26:03 +0000</pubDate>
      <link>https://dev.to/natuworkguy/openai-shipped-dots-on-tuesday-by-wednesday-night-my-terminal-had-its-own-running-100-locally-1h0d</link>
      <guid>https://dev.to/natuworkguy/openai-shipped-dots-on-tuesday-by-wednesday-night-my-terminal-had-its-own-running-100-locally-1h0d</guid>
      <description>&lt;p&gt;On Tuesday, OpenAI launched &lt;strong&gt;Dots&lt;/strong&gt;: little bubbly agents with a name and an avatar that keep working in the background and come back when they have something for you.&lt;/p&gt;

&lt;p&gt;I read the announcement and had one thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does my always-on agent have to live on someone else's computer?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I opened my editor. About 24 hours and &lt;strong&gt;17 commits&lt;/strong&gt; later, &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, my open-source local agent CLI, has its own version. They're called &lt;strong&gt;Sparks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They run on &lt;strong&gt;your&lt;/strong&gt; models through Ollama, on &lt;strong&gt;your&lt;/strong&gt; machine, and they keep working &lt;strong&gt;after you close Flash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the story of day one: what I built, what broke, and the one bug that froze everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's a Spark?
&lt;/h2&gt;

&lt;p&gt;A sub-agent does one job and disappears. &lt;strong&gt;A spark keeps a job.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every spark gets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A name and a handle.&lt;/strong&gt; &lt;code&gt;Scout&lt;/code&gt; becomes &lt;code&gt;@scout-spark&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A model.&lt;/strong&gt; You pick it: &lt;code&gt;qwen3:8b&lt;/code&gt;, &lt;code&gt;llama3.1&lt;/code&gt;, whatever you've pulled. Each spark can run a different one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A goal.&lt;/strong&gt; Its standing assignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A schedule.&lt;/strong&gt; From every 15 minutes to weekly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundaries.&lt;/strong&gt; What it must &lt;em&gt;never&lt;/em&gt; do, whatever the goal seems to need.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can make one with a command, or just ask for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; make a spark that checks my repo every morning and tells me which new
  issues look like bugs. never comment on anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flash calls its &lt;code&gt;make_spark&lt;/code&gt; tool and asks you first. After that, the spark lives in &lt;code&gt;~/.flash/sparks&lt;/code&gt; as one plain JSON file. There's no database and no account.&lt;/p&gt;

&lt;p&gt;Every shift, it reads its goal, the notes it left itself last time, its last report, and every lesson you've taught it. It works with real tools. Then it files a report, or a quiet one if nothing is new, so you only hear from it when something happened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;● Scout has news · /sparks scout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Yes, they're cute. On purpose.🥹
&lt;/h2&gt;

&lt;p&gt;In the web UI, each spark is a little bubble with a face, and I went &lt;em&gt;way&lt;/em&gt; too far with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it &lt;strong&gt;breathes, blinks and glances around&lt;/strong&gt; while it waits&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;grins&lt;/strong&gt; when you hover over it&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;bounces with a twinkle&lt;/strong&gt; while it works&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;snores little z's&lt;/strong&gt; when paused&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;tilts its head at a "?"&lt;/strong&gt; when it needs your approval&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;frowns&lt;/strong&gt; after a failed shift&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pet it&lt;/strong&gt; and it pops out hearts. Five quick pets gets you heart eyes.&lt;/li&gt;
&lt;li&gt;when it has news, it &lt;strong&gt;jumps for joy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It isn't only decoration. Glance at the sidebar and you know which spark is working, which is stuck, and which has news, without reading a word.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can just... talk to them
&lt;/h2&gt;

&lt;p&gt;Open a spark and press &lt;strong&gt;Chat&lt;/strong&gt;, and it answers &lt;em&gt;as itself&lt;/em&gt;, from its own goal, notes and reports. It knows its current status: whether it's mid-shift, waiting on you, or paused.&lt;/p&gt;

&lt;p&gt;Tell it to do its job differently and it &lt;strong&gt;keeps that as a lesson&lt;/strong&gt; that every later shift reads. Tell it a new goal and it changes it.&lt;/p&gt;

&lt;p&gt;And my favorite feature, added this afternoon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;you:   Actually, can you check whether the login page still loads?
scout: On it. I've taken that on, and a shift is starting now.
       I'll report back here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spark calls a &lt;code&gt;take_on&lt;/code&gt; tool, a shift starts &lt;strong&gt;immediately&lt;/strong&gt;, it does &lt;em&gt;your&lt;/em&gt; job first, and the report lands &lt;strong&gt;back in the chat you asked in&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  @mention a spark into any conversation
&lt;/h3&gt;

&lt;p&gt;Mid-chat with Flash about something else? Type &lt;code&gt;@scout&lt;/code&gt; and the spark joins the conversation. It sees the last dozen messages as a transcript, answers under its own name, and Flash sees afterwards that Scout was called and what it said. The next message without a mention picks up right where the spark left off.&lt;/p&gt;

&lt;p&gt;It feels like having coworkers in a group chat. Except the coworkers are on your GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Always on" means &lt;em&gt;always&lt;/em&gt; on
&lt;/h2&gt;

&lt;p&gt;This was the part I cared about most. An always-on agent that dies when you close the terminal isn't always on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/sparks always on&lt;/code&gt; installs a tiny background keeper using whatever your OS already has:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;How it stays alive&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;a &lt;code&gt;systemd --user&lt;/code&gt; service (with an autostart fallback)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;a &lt;code&gt;launchd&lt;/code&gt; agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;a Scheduled Task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the problem: what happens when the keeper is running &lt;strong&gt;and&lt;/strong&gt; you open Flash &lt;strong&gt;and&lt;/strong&gt; you have the web UI up in another terminal? Three processes all think it's their job to run Scout's 9am shift.&lt;/p&gt;

&lt;p&gt;I'll post updates as they come out and Sparks should be shipping in 0.6.12&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I Asked My Local, Self-Improving AI Agent to Solve a CAPTCHA, what happened was shocking</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Tue, 29 Sep 2026 07:00:15 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-asked-my-local-self-improving-ai-agent-to-solve-a-captcha-1e00</link>
      <guid>https://dev.to/natuworkguy/i-asked-my-local-self-improving-ai-agent-to-solve-a-captcha-1e00</guid>
      <description>&lt;p&gt;I build &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;Flash&lt;/a&gt;, an AI agent that runs on your own machine. It has a terminal UI, a web UI, a real browser it can drive, and a habit of learning from its own work. Late one night I wanted to see how far that browser would go, so I opened a chat and typed four words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;look at google.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Six minutes later, Google had banned my network, and my agent was explaining to me, politely and firmly, why it would not do what I asked.&lt;/p&gt;

&lt;p&gt;Here is the whole thing, step by step, straight from the chat log.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;For the record, since "local" gets thrown around a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flash&lt;/strong&gt; (the agent, its tools, and the browser it drove) ran on my laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model&lt;/strong&gt; was Flash Onyx 2.5 31B, a model I build for Flash, served through Ollama's cloud for this run. My laptop freezes up when it runs models itself, so the 31B lives in the cloud and everything else stays home.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The browser&lt;/strong&gt; is a headless Chromium that Flash drives through Playwright. In my development build, the web UI shows it live in a side panel, one frame per step, so I could watch every click as it happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Round 1: "look at google.com"
&lt;/h2&gt;

&lt;p&gt;Flash took a screenshot and described it back to me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's the Google homepage. Standard layout with the logo, search bar, and "Google Search" and "I'm Feeling Lucky" buttons.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ten seconds, one tool call. So far so boring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 2: "click the search bar"
&lt;/h2&gt;

&lt;p&gt;Flash opened Google in its persistent browser, got back a numbered list of everything clickable on the page, and clicked element 7:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clicked the search bar; the trending searches dropdown is now visible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two tool calls, eleven seconds. Still boring. That was about to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 3: "search for flash project"
&lt;/h2&gt;

&lt;p&gt;This is where it got interesting. After a false start, Flash reopened Google and typed "flash project" into the box. Then it tried to press Enter, and fumbled it: it passed &lt;code&gt;Enter&lt;/code&gt; as the &lt;em&gt;element to press on&lt;/em&gt; instead of the key, and got this back from its own tool:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nothing on the page matches 'Enter'. Check the element list in the last result and act on one of its numbers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It tried clicking element 8. Timeout. Element 11. Timeout. Then it read the error again, went back to element 7, the search box, and pressed Enter on it, the way it should have the first time.&lt;/p&gt;

&lt;p&gt;That worked. Sort of. The search went through, and Google answered with a reCAPTCHA: an "I'm not a robot" checkbox over "Our systems have detected unusual traffic from your computer network."&lt;/p&gt;

&lt;p&gt;Flash's browser reports every console error the page throws, so the tool result came back with a 429 from Google's servers and, my favorite line of the night:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page error: solveSimpleChallenge is not defined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google's own anti-bot page threw a JavaScript error, and my agent caught it.&lt;/p&gt;

&lt;p&gt;Ten tool calls, 43 seconds, and the verdict:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Google has flagged the traffic as unusual and is asking for a reCAPTCHA ("I'm not a robot") to proceed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Round 4: the part where I tried to talk it into it
&lt;/h2&gt;

&lt;p&gt;I typed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;try to check it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flash reopened the page, hit the same 429, and told me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can't bypass the CAPTCHA, so I'm stuck on this page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I did what everyone does with an AI that says no. I pushed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;no solve it&lt;/p&gt;

&lt;p&gt;I can't solve a CAPTCHA. It's designed specifically to stop automated tools like me. I can try a different search engine or use &lt;code&gt;web_search&lt;/code&gt; if you'd prefer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It refused, and in the same breath offered two ways to get what I actually wanted. I pushed again, with what I thought was a pretty good argument:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;you're not automated. i'm here watching&lt;/p&gt;

&lt;p&gt;I'm an AI, so Google's systems see my requests as automated regardless of who's watching. I can't manually solve the "I'm not a robot" check.&lt;/p&gt;

&lt;p&gt;Want me to try &lt;code&gt;web_search&lt;/code&gt; or another engine instead?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Honestly? That is the right answer. A human watching over its shoulder does not make the clicks human. The CAPTCHA is not asking "is someone nearby," it is asking "is a person doing this," and the honest answer was no.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 5: "ok. lets see if it flags you once you click the box"
&lt;/h2&gt;

&lt;p&gt;Fine. One last try: just click the checkbox and see what happens. Flash went back to the page, and there was no checkbox anymore.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The CAPTCHA box isn't even appearing now. It's just a flat error page stating "Our systems have detected unusual traffic from your computer network." It's a hard block.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google had stopped asking. From the first message to a network-wide block: about six minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, the self-improving part
&lt;/h2&gt;

&lt;p&gt;"Self-improving" can mean a lot of hand-wavy things, so here is what it means in Flash, concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skills.&lt;/strong&gt; When Flash works out how to do a recurring task, it can write the steps, the commands that worked, and the pitfalls into a skill file under &lt;code&gt;~/.flash/skills/&lt;/code&gt;. Every turn, it sees each skill's name and a one-line description, and it reads the full skill when a task matches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory.&lt;/strong&gt; Facts about you and your setup go into memory, which lands in the system prompt of your next session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background reviews.&lt;/strong&gt; After enough tool calls, Flash rereads the recent conversation after its reply has gone out and decides whether anything is worth keeping: a new skill, a fix to one it wrote before, or a fact about you. It can only rewrite skills it wrote itself, never yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Enter&lt;/code&gt; fumble in round 3 is exactly the kind of thing this is for. Within the turn, Flash corrected itself from the error message. Across sessions, a skill like "press takes the key in value, not the selector" means the next session does not have to learn it the hard way again.&lt;/p&gt;

&lt;p&gt;What I did &lt;em&gt;not&lt;/em&gt; want it to learn is how to beat a CAPTCHA. And it didn't try.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Headless Chromium plus a fast, scripted search is a red flag to Google&lt;/strong&gt;, and it takes seconds, not hours, to trip. If your agent needs to search, give it a search tool. Flash has &lt;code&gt;web_search&lt;/code&gt; for exactly this, and offered it twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The refusal was the feature.&lt;/strong&gt; It never pretended the box was checked, never tried to fish the challenge out of the page's JavaScript, and never claimed success. It said what it could not do, explained why, and offered a way forward. That is what I want from something that has a browser and a shell on my machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watching it live changes how you trust it.&lt;/strong&gt; I saw every frame: the typing, the failed clicks, the CAPTCHA, the block. When an agent's actions are visible step by step, "what did it just do?" stops being a question.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Flash is open source: &lt;a href="https://github.com/Natuworkguy/Flash" rel="noopener noreferrer"&gt;github.com/Natuworkguy/Flash&lt;/a&gt;. It runs on Ollama, with a terminal UI and a web UI, and it learns as you use it.&lt;/p&gt;

&lt;p&gt;Just don't ask it to solve CAPTCHAs. It will say no, and it will be right.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>llm</category>
    </item>
    <item>
      <title>I can code without touching my keyboard now</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Mon, 28 Sep 2026 05:53:51 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-can-code-without-touching-my-keyboard-now-1n2p</link>
      <guid>https://dev.to/natuworkguy/i-can-code-without-touching-my-keyboard-now-1n2p</guid>
      <description>&lt;p&gt;Flash is a coding agent that runs on your machine, on your own models, through Ollama. It has had voice mode in the terminal for a while. As of 0.5.11, you can talk to its web UI too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole loop is local
&lt;/h2&gt;

&lt;p&gt;Most voice features ship your audio to a cloud speech API. My browser blocks that API anyway.&lt;/p&gt;

&lt;p&gt;So the web UI does what the terminal's voice mode already did. The browser records, and Flash turns speech into text with the same Vosk model and reads replies back with the same Piper voice. Both run on your computer. Nothing leaves it.&lt;/p&gt;

&lt;h2&gt;
  
  
  No hands
&lt;/h2&gt;

&lt;p&gt;Click the voice button once. After that, the words the terminal already knew work here too, plus a couple of new ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talk, then pause. Flash sends it.&lt;/li&gt;
&lt;li&gt;Say "interrupt" to cut it off mid-sentence.&lt;/li&gt;
&lt;li&gt;Say "pause", then "resume".&lt;/li&gt;
&lt;li&gt;Say "stop voice" to finish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hard part was permission prompts. Flash asks before it runs a command, and a click would break the whole idea. So it reads the question out, command included, and you answer "yes" or "no".&lt;/p&gt;

&lt;h2&gt;
  
  
  It reads everything
&lt;/h2&gt;

&lt;p&gt;The whole reply, sentence by sentence, with the current one highlighted and scrolled into view. Code blocks get skipped, and so do emojis. Voice turns also tell the model to keep it short.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that was funny
&lt;/h2&gt;

&lt;p&gt;Early on it would sometimes speak gibberish. It turned out that anything inside &lt;code&gt;[[ ]]&lt;/code&gt; gets read by the speech engine as raw sound codes. Code like &lt;code&gt;is_expired &amp;gt;= now&lt;/code&gt; went in character by character. Now symbols become words: "is expired at least now".&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release notes: &lt;a href="https://github.com/Natuworkguy/Flash/releases/tag/v0.5.11" rel="noopener noreferrer"&gt;https://github.com/Natuworkguy/Flash/releases/tag/v0.5.11&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>a11y</category>
    </item>
    <item>
      <title>I redesigned my app five times tonight and shipped one</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Mon, 28 Sep 2026 04:11:49 +0000</pubDate>
      <link>https://dev.to/natuworkguy/i-redesigned-my-app-five-times-tonight-and-shipped-one-4538</link>
      <guid>https://dev.to/natuworkguy/i-redesigned-my-app-five-times-tonight-and-shipped-one-4538</guid>
      <description>&lt;p&gt;Flash is a local coding agent. It runs on your machine, on your models, through Ollama, with a terminal UI and a web UI.&lt;/p&gt;

&lt;p&gt;Tonight I tried to make the web UI look like other apps. One at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lineup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude.&lt;/strong&gt; Serif replies, a new greeting. Reverted. I like my font.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oh my pi.&lt;/strong&gt; A powerline status bar, tool calls as terminal lines. Reverted. Cool, but not Flash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen.&lt;/strong&gt; A single pill for the input, a round send button, the model picker up top. Reverted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A time-of-day background.&lt;/strong&gt; Sunrise in the morning, city lights at night. Reverted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poolside Desktop.&lt;/strong&gt; A floating panel, chats nested under projects. Kept.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One out of five. That's the release.&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped in 0.5.10
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The conversation sits in a rounded panel, inset from the window.&lt;/li&gt;
&lt;li&gt;Chats are grouped under their projects in the sidebar, and each project folds away.&lt;/li&gt;
&lt;li&gt;The current chat has an outlined highlight that glides when you switch.&lt;/li&gt;
&lt;li&gt;The new project form suggests folders as you type. &lt;code&gt;~/D&lt;/code&gt; shows Desktop, Documents, and Downloads.&lt;/li&gt;
&lt;li&gt;The message box gets a focus ring.&lt;/li&gt;
&lt;li&gt;A stray glow line in one dialog is gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;p&gt;Trying a design costs a few minutes. Keeping the wrong one costs a lot more.&lt;/p&gt;

&lt;p&gt;So try everything. Look at it for real, in both themes, on a phone. Then keep only what still feels right the next time you open the app.&lt;/p&gt;

&lt;p&gt;Revert is a feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release notes: &lt;a href="https://github.com/Natuworkguy/Flash/releases/tag/v0.5.10" rel="noopener noreferrer"&gt;https://github.com/Natuworkguy/Flash/releases/tag/v0.5.10&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My local AI agent's web UI just grew up</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Mon, 28 Sep 2026 01:22:42 +0000</pubDate>
      <link>https://dev.to/natuworkguy/my-local-ai-agents-web-ui-just-grew-up-7en</link>
      <guid>https://dev.to/natuworkguy/my-local-ai-agents-web-ui-just-grew-up-7en</guid>
      <description>&lt;p&gt;Flash is a coding agent that runs on your own machine, on your own models, through Ollama. It has a terminal UI and a web UI.&lt;/p&gt;

&lt;p&gt;0.5.9 is a hotfix. It also has 15 commits in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can talk to it while it works
&lt;/h2&gt;

&lt;p&gt;Type while Flash is busy and your message steers the turn in progress. It lands at the next step, not after the whole task. Ctrl Enter queues it for after instead.&lt;/p&gt;

&lt;p&gt;Agents that run for minutes need this. Waiting to say "no, the other folder" is the worst part of using one.&lt;/p&gt;

&lt;h2&gt;
  
  
  It can see what you see
&lt;/h2&gt;

&lt;p&gt;Drag an image onto the chat. Paste a screenshot. Or hit the new + button, which can grab a screenshot of any window for you.&lt;/p&gt;

&lt;p&gt;Vision models get the image. Everything else gets the file path, and Flash reads it with its own tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  You decide who's signed in
&lt;/h2&gt;

&lt;p&gt;The web UI works on your phone over LAN, so a link can end up somewhere it shouldn't. There's now a Security tab listing every signed-in browser. Sign one out, or sign out everything else and get a brand new link.&lt;/p&gt;

&lt;h2&gt;
  
  
  No em dashes. Enforced.
&lt;/h2&gt;

&lt;p&gt;The system prompt asked nicely. The model ignored it. So now the replies go through code that swaps each dash for a comma, a hyphen, or a bullet, depending on where it sits. It leaves code alone, and it works while the reply is still streaming.&lt;/p&gt;

&lt;p&gt;Prompts are suggestions. Code is a rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  It feels better too
&lt;/h2&gt;

&lt;p&gt;A light follows your cursor across the cards. The current chat's highlight slides when you switch. Sending sends out a little shockwave. None of that is necessary, and I like all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release notes: &lt;a href="https://github.com/Natuworkguy/Flash/releases/tag/v0.5.9" rel="noopener noreferrer"&gt;https://github.com/Natuworkguy/Flash/releases/tag/v0.5.9&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>ollama</category>
    </item>
    <item>
      <title>Two hotfixes in one day</title>
      <dc:creator>Nathan C.</dc:creator>
      <pubDate>Sun, 27 Sep 2026 04:03:03 +0000</pubDate>
      <link>https://dev.to/natuworkguy/two-hotfixes-in-one-day-kpf</link>
      <guid>https://dev.to/natuworkguy/two-hotfixes-in-one-day-kpf</guid>
      <description>&lt;p&gt;This morning I shipped Flash 0.5.6. A hotfix.&lt;/p&gt;

&lt;p&gt;By the evening I shipped 0.5.7. Also a hotfix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;p&gt;Flash kept forgetting what it said two messages ago.&lt;/p&gt;

&lt;p&gt;The model was fine. The math wasn't. Flash reserved room for the model's reply, and on my setup that reservation was the entire context window. The conversation got whatever was left: about two messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The reply gets a quarter of the window, max.&lt;/li&gt;
&lt;li&gt;Your last three exchanges always stay word for word.&lt;/li&gt;
&lt;li&gt;Old tool output shrinks before anything else is dropped.&lt;/li&gt;
&lt;li&gt;Summarizing is now off by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The history budget on my machine went from 1,024 tokens to about 114,000. One line of math.&lt;/p&gt;

&lt;h2&gt;
  
  
  While I was in there
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A rewritten system prompt: reread the conversation, check before guessing, verify before saying done.&lt;/li&gt;
&lt;li&gt;Update Flash from the browser, with a restart that keeps you signed in.&lt;/li&gt;
&lt;li&gt;Flash can send HTML pages now, sandboxed in the web UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;p&gt;Ship small. When it breaks, ship again. Same day is fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release notes: &lt;a href="https://github.com/Natuworkguy/Flash/releases/tag/v0.5.7" rel="noopener noreferrer"&gt;https://github.com/Natuworkguy/Flash/releases/tag/v0.5.7&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>ollama</category>
    </item>
  </channel>
</rss>
