<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://forketyfork.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://forketyfork.github.io/" rel="alternate" type="text/html" /><updated>2026-08-12T08:06:58+00:00</updated><id>https://forketyfork.github.io/feed.xml</id><title type="html">Forketyfork Dev Blog</title><subtitle>Personal blog of a software developer at JetBrains. Writing about Java, Kotlin, Go, cloud technologies, AI-assisted coding, and developer tools with an 80s retro aesthetic.</subtitle><entry><title type="html">Teaching My Static Analyzer to Catch the Bug It Missed</title><link href="https://forketyfork.github.io/blog/2026/02/03/teaching-my-static-analyzer-to-catch-the-bug-it-missed/" rel="alternate" type="text/html" title="Teaching My Static Analyzer to Catch the Bug It Missed" /><published>2026-02-03T00:00:00+00:00</published><updated>2026-02-03T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2026/02/03/teaching-my-static-analyzer-to-catch-the-bug-it-missed</id><content type="html" xml:base="https://forketyfork.github.io/blog/2026/02/03/teaching-my-static-analyzer-to-catch-the-bug-it-missed/"><![CDATA[<p>I found a stack use-after-return bug in <a href="https://github.com/forketyfork/architect">Architect</a> today. Then I taught <a href="https://github.com/forketyfork/zwanzig">zwanzig</a>, my Zig static analyzer, to catch it.</p>

<h2 id="the-crash">The crash</h2>

<p>Cmd+clicking links in the terminal would occasionally crash the app:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EXC_BAD_ACCESS / SIGSEGV
KERN_INVALID_ADDRESS at 0x0000000000000010
</code></pre></div></div>

<p>The crash happened in <code class="language-plaintext highlighter-rouge">_platform_memmove</code> called from <code class="language-plaintext highlighter-rouge">process.Child.spawn</code>. Classic memory corruption - the stack trace points at libc internals and tells you nothing useful.</p>

<h2 id="the-bug">The bug</h2>

<p>Here’s the problematic code (simplified):</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">openUrl</span><span class="p">(</span><span class="n">allocator</span><span class="p">:</span> <span class="n">std</span><span class="p">.</span><span class="py">mem</span><span class="p">.</span><span class="py">Allocator</span><span class="p">,</span> <span class="n">url</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="kt">u8</span><span class="p">)</span> <span class="o">!</span><span class="k">void</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">thread_allocator</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">heap</span><span class="p">.</span><span class="py">c_allocator</span><span class="p">;</span>
    <span class="k">const</span> <span class="n">owned_url</span> <span class="o">=</span> <span class="k">try</span> <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">dupe</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>

    <span class="k">const</span> <span class="n">child</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">process</span><span class="p">.</span><span class="py">Child</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span>
        <span class="o">&amp;.</span><span class="p">{</span> <span class="s">"open"</span><span class="p">,</span> <span class="n">owned_url</span> <span class="p">},</span>  <span class="c">// &lt;- stack-allocated argv</span>
        <span class="n">allocator</span>
    <span class="p">);</span>

    <span class="k">const</span> <span class="n">thread</span> <span class="o">=</span> <span class="k">try</span> <span class="n">std</span><span class="p">.</span><span class="py">Thread</span><span class="p">.</span><span class="nf">spawn</span><span class="p">(</span><span class="o">.</span><span class="p">{},</span> <span class="n">openUrlThread</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span> <span class="n">thread_allocator</span><span class="p">,</span> <span class="n">child</span><span class="p">,</span> <span class="n">owned_url</span> <span class="p">});</span>
    <span class="n">thread</span><span class="p">.</span><span class="nf">detach</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">openUrlThread</span><span class="p">(</span><span class="n">thread_allocator</span><span class="p">:</span> <span class="n">std</span><span class="p">.</span><span class="py">mem</span><span class="p">.</span><span class="py">Allocator</span><span class="p">,</span> <span class="n">child</span><span class="p">:</span> <span class="n">std</span><span class="p">.</span><span class="py">process</span><span class="p">.</span><span class="py">Child</span><span class="p">,</span> <span class="n">owned_url</span><span class="p">:</span> <span class="p">[]</span><span class="kt">u8</span><span class="p">)</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">var</span> <span class="n">process</span> <span class="o">=</span> <span class="n">child</span><span class="p">;</span>
    <span class="mi">_</span> <span class="o">=</span> <span class="n">process</span><span class="p">.</span><span class="nf">spawnAndWait</span><span class="p">()</span> <span class="k">catch</span> <span class="p">{};</span>
    <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">free</span><span class="p">(</span><span class="n">owned_url</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">&amp;.{ "open", owned_url }</code> creates a temporary array on the stack. <code class="language-plaintext highlighter-rouge">Child.init</code> stores a pointer to this array. When <code class="language-plaintext highlighter-rouge">child</code> is passed to the thread, the struct is copied, but it still holds a pointer to the original stack memory. The function returns, reclaiming the stack frame. Thread tries to spawn using the now-invalid argv pointer. Crash.</p>

<h2 id="the-fix">The fix</h2>

<p>Put the argv array in a heap-allocated context struct:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">ThreadContext</span> <span class="o">=</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="n">allocator</span><span class="p">:</span> <span class="n">std</span><span class="p">.</span><span class="py">mem</span><span class="p">.</span><span class="py">Allocator</span><span class="p">,</span>
    <span class="n">url</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="kt">u8</span><span class="p">,</span>
    <span class="n">argv</span><span class="p">:</span> <span class="p">[</span><span class="mi">2</span><span class="p">][]</span><span class="k">const</span> <span class="kt">u8</span><span class="p">,</span>

    <span class="k">fn</span> <span class="n">deinit</span><span class="p">(</span><span class="n">self</span><span class="p">:</span> <span class="o">*</span><span class="n">ThreadContext</span><span class="p">)</span> <span class="k">void</span> <span class="p">{</span>
        <span class="n">self</span><span class="p">.</span><span class="py">allocator</span><span class="p">.</span><span class="nf">free</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="py">url</span><span class="p">);</span>
        <span class="n">self</span><span class="p">.</span><span class="py">allocator</span><span class="p">.</span><span class="nf">destroy</span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="k">fn</span> <span class="n">openUrl</span><span class="p">(</span><span class="mi">_</span><span class="p">:</span> <span class="n">std</span><span class="p">.</span><span class="py">mem</span><span class="p">.</span><span class="py">Allocator</span><span class="p">,</span> <span class="n">url</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="kt">u8</span><span class="p">)</span> <span class="o">!</span><span class="k">void</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">thread_allocator</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">heap</span><span class="p">.</span><span class="py">c_allocator</span><span class="p">;</span>

    <span class="k">const</span> <span class="n">ctx</span> <span class="o">=</span> <span class="k">try</span> <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="n">ThreadContext</span><span class="p">);</span>
    <span class="k">errdefer</span> <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">destroy</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>

    <span class="n">ctx</span><span class="p">.</span><span class="py">allocator</span> <span class="o">=</span> <span class="n">thread_allocator</span><span class="p">;</span>
    <span class="n">ctx</span><span class="p">.</span><span class="py">url</span> <span class="o">=</span> <span class="k">try</span> <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">dupe</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>
    <span class="k">errdefer</span> <span class="n">thread_allocator</span><span class="p">.</span><span class="nf">free</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="py">url</span><span class="p">);</span>

    <span class="n">ctx</span><span class="p">.</span><span class="py">argv</span> <span class="o">=</span> <span class="o">.</span><span class="p">{</span> <span class="s">"open"</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="py">url</span> <span class="p">};</span>

    <span class="k">const</span> <span class="n">thread</span> <span class="o">=</span> <span class="k">try</span> <span class="n">std</span><span class="p">.</span><span class="py">Thread</span><span class="p">.</span><span class="nf">spawn</span><span class="p">(</span><span class="o">.</span><span class="p">{},</span> <span class="n">openUrlThread</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span><span class="n">ctx</span><span class="p">});</span>
    <span class="n">thread</span><span class="p">.</span><span class="nf">detach</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">openUrlThread</span><span class="p">(</span><span class="n">ctx</span><span class="p">:</span> <span class="o">*</span><span class="n">ThreadContext</span><span class="p">)</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">defer</span> <span class="n">ctx</span><span class="p">.</span><span class="nf">deinit</span><span class="p">();</span>
    <span class="k">var</span> <span class="n">child</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">process</span><span class="p">.</span><span class="py">Child</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ctx</span><span class="p">.</span><span class="py">argv</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="py">allocator</span><span class="p">);</span>
    <span class="mi">_</span> <span class="o">=</span> <span class="n">child</span><span class="p">.</span><span class="nf">spawnAndWait</span><span class="p">()</span> <span class="k">catch</span> <span class="p">{};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The context lives on the heap and owns everything the thread needs. The Child is created inside the thread function, so its argv pointer points to heap memory that remains valid.</p>

<h2 id="teaching-zwanzig">Teaching zwanzig</h2>

<p>Finding this bug manually was annoying. Intermittent crash, stack trace pointing nowhere useful, actual cause buried under several layers of indirection.</p>

<p>I wanted zwanzig to catch this. The pattern: a pointer to something on the stack gets passed to a detached thread, and the function returns before the thread finishes.</p>

<p>I wrote a checker called <code class="language-plaintext highlighter-rouge">stack-escape-engine</code>:</p>

<ol>
  <li>Tracks values with stack-backed origins (local variables, temporary arrays)</li>
  <li>Follows pointers through function calls and struct fields</li>
  <li>Flags when such values escape via <code class="language-plaintext highlighter-rouge">Thread.spawn</code> + <code class="language-plaintext highlighter-rouge">detach</code></li>
  <li>Ignores cases where the thread is joined (not detached)</li>
</ol>

<p>It catches the exact pattern from the bug:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// zwanzig catches this:</span>
<span class="k">const</span> <span class="n">child</span> <span class="o">=</span> <span class="n">std</span><span class="p">.</span><span class="py">process</span><span class="p">.</span><span class="py">Child</span><span class="p">.</span><span class="nf">init</span><span class="p">(</span><span class="o">&amp;.</span><span class="p">{</span> <span class="s">"open"</span><span class="p">,</span> <span class="n">owned_url</span> <span class="p">},</span> <span class="n">allocator</span><span class="p">);</span>
<span class="k">const</span> <span class="n">thread</span> <span class="o">=</span> <span class="k">try</span> <span class="n">std</span><span class="p">.</span><span class="py">Thread</span><span class="p">.</span><span class="nf">spawn</span><span class="p">(</span><span class="o">.</span><span class="p">{},</span> <span class="n">openUrlThread</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span> <span class="n">allocator</span><span class="p">,</span> <span class="n">child</span><span class="p">,</span> <span class="n">owned_url</span> <span class="p">});</span>
<span class="n">thread</span><span class="p">.</span><span class="nf">detach</span><span class="p">();</span>  <span class="c">// &lt;- "Stack-backed value escapes via thread"</span>
</code></pre></div></div>

<p>It also handles argv constructed via switch (the actual cross-platform code uses one), helpers wrapping the spawn call, etc. The test suite covers the patterns I could think of.</p>

<h2 id="why-bother">Why bother</h2>

<p>Every time I fix something subtle, I ask: could a tool have caught this? Sometimes no - the bug needs runtime context or is too dynamic. But stack escapes via thread captures? That’s a pattern. Patterns can be detected.</p>

<p>If you’re writing Zig:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/forketyfork/zwanzig
<span class="nb">cd </span>zwanzig <span class="o">&amp;&amp;</span> zig build
./zig-out/bin/zwanzig /path/to/your/project
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">stack-escape-engine</code> checker is on by default in v0.6.0+.</p>

<hr />

<p><strong>Links:</strong></p>
<ul>
  <li><a href="https://github.com/forketyfork/architect/pull/191">Architect #191</a> - the bug fix</li>
  <li><a href="https://github.com/forketyfork/zwanzig/pull/54">zwanzig #54</a> - the new checker (+3,233 / -271 lines)</li>
  <li><a href="https://github.com/forketyfork/zwanzig">zwanzig on GitHub</a></li>
</ul>]]></content><author><name></name></author><category term="Zig" /><category term="Static Analysis" /><category term="zwanzig" /><category term="Architect" /><summary type="html"><![CDATA[I found a stack use-after-return bug in Architect today. Then I taught zwanzig, my Zig static analyzer, to catch it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Running 9 AI Coding Agents at Once: The Terminal I Built to Keep Up</title><link href="https://forketyfork.github.io/blog/2026/01/21/running-4-ai-coding-agents-at-once-the-terminal-i-built-to-keep-up/" rel="alternate" type="text/html" title="Running 9 AI Coding Agents at Once: The Terminal I Built to Keep Up" /><published>2026-01-21T00:00:00+00:00</published><updated>2026-01-21T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2026/01/21/running-4-ai-coding-agents-at-once-the-terminal-i-built-to-keep-up</id><content type="html" xml:base="https://forketyfork.github.io/blog/2026/01/21/running-4-ai-coding-agents-at-once-the-terminal-i-built-to-keep-up/"><![CDATA[<p>I run multiple AI coding agents simultaneously because I often need to work on several tasks at once. One agent implements a feature, another does a code review, another improves my dev setup, yet another helps me keep my Obsidian notes up to date.</p>

<p>The problems I’ve faced:</p>

<ul>
  <li>I kept forgetting that I’d spawned an agent to work on some task, only to find out at the end of the day that it had been waiting for my approval for hours.</li>
  <li>I tried to use git worktrees to parallelize tasks, but they’re cumbersome to manage.</li>
  <li>I tried other terminals, but they either lack native agentic support or are overloaded with AI features that rub me the wrong way.</li>
  <li>I tried tools that wrap agent executions into a UI, but always reverted back to the terminal because they didn’t provide the level of control I needed, or the ability to use the same window for different tasks. Agent was in one place, terminal in another.</li>
</ul>

<h2 id="the-attention-problem">The attention problem</h2>

<p>Here’s what happens when you run 4 Claude Code sessions in multiple terminal tabs: nothing, visually. All four tabs look the same. One agent finishes and sits there, waiting for your next prompt. Another hits a permission check and needs approval. You don’t notice because you’re focused on tab 3, watching it churn through a file. Twenty minutes later you realize agent 1 has been idle the whole time.</p>

<p>I tried the obvious solutions — desktop notifications, but they come and go at the operating system’s whim. Multiple terminal windows arranged carefully. A second monitor dedicated to “the agents.” None of it worked because the fundamental problem remained: terminals don’t know what’s running inside them.</p>

<p>A shell prompt looks the same whether the last command succeeded, failed, or is waiting for human input. There’s no semantic information.</p>

<h2 id="what-i-built">What I built</h2>

<p><a href="https://github.com/forketyfork/architect">Architect</a> is a terminal I wrote specifically for this workflow. Show all sessions in a grid, make it easy to switch between them, make it visually obvious which ones need attention.</p>

<p>When an agent finishes a task, the cell hue changes. When it’s waiting for approval, it glows. At a glance, I know where to focus.</p>

<video class="post-video" autoplay="" loop="" muted="" playsinline="" controls="">
  <source src="/img/agents.mp4" type="video/mp4" />
</video>

<p>That’s it. That’s the feature.</p>

<p>Everything else — the smooth animations, the expand/collapse, the keyboard shortcuts — exists to make this core loop fast. See the grid. Spot the agent that needs you. Expand. Respond. Collapse. Back to the grid.</p>

<h2 id="the-workflow">The workflow</h2>

<p>I start with a single terminal. When I need another, I hit ⌘N and the grid expands automatically. When I’m done with one, ⌘W closes it and the grid contracts. If I need to focus on a specific terminal, I hit ⌘Enter, or do a long Esc hold to pop back to the grid.</p>

<video class="post-video" autoplay="" loop="" muted="" playsinline="" controls="">
  <source src="/img/grid.mp4" type="video/mp4" />
</video>

<p>If I need multiple tasks in the same repo, I hit ⌘T to open a worktree popup. ⌘0 creates a new worktree, ⌘1/⌘2/⌘3… switches to an existing one. I can delete worktrees from the same popup. The integration works by sending git commands to the terminal — fully traceable, no magic.</p>

<video class="post-video" autoplay="" loop="" muted="" playsinline="" controls="">
  <source src="/img/worktrees.mp4" type="video/mp4" />
</video>

<h2 id="status-detection">Status detection</h2>

<p>It’s based on hooks. Many agents support them, so it’s not limited to the big 3. I provide a Python script you can call to highlight the cell where it’s running.</p>

<p>I learned a lot of agent quirks along the way:</p>

<ul>
  <li><strong>Claude Code</strong> first signals it’s done (<code class="language-plaintext highlighter-rouge">Stop</code>), then triggers another hook (<code class="language-plaintext highlighter-rouge">Notification</code>) after ~10 seconds if you don’t react. So the cell turns green, then yellow.</li>
  <li><strong>Gemini</strong> hooks have to be <a href="https://github.com/google-gemini/gemini-cli/blob/main/docs/get-started/configuration.md">explicitly enabled in settings</a>. It also sends its <code class="language-plaintext highlighter-rouge">AfterAgent</code> notification after every step instead of just once at the end — <a href="https://github.com/google-gemini/gemini-cli/issues/14596">known issue</a>.</li>
  <li><strong>Codex</strong> <a href="https://github.com/openai/codex/discussions/2150">doesn’t support hooks</a>, just a simple notification script after every agent turn. No permission request notification, sadly.</li>
</ul>

<h2 id="why-zig">Why Zig?</h2>

<p>I wanted to learn Zig, and terminal emulators are a good project for it. Also, Architect builds on <a href="https://github.com/ghostty-org/ghostty">ghostty-vt</a>, which is written in Zig — using the same language meant I could integrate directly without FFI overhead. SDL3 for rendering, ghostty-vt for terminal emulation, Zig for glue.</p>

<h2 id="whats-missing">What’s missing</h2>

<p>Architect is early. I use it daily, but there are gaps:</p>

<ul>
  <li><strong>Linux support</strong>: macOS only for now.</li>
  <li><strong>Customizable keybindings</strong>: Hardcoded. You get what I like.</li>
  <li><strong>Windows</strong>: Not happening anytime soon.</li>
  <li><strong>Bugs and UI quirks</strong>: Plenty.</li>
</ul>

<p>Agent detection is also limited to a handful of tools.</p>

<h2 id="try-it">Try it</h2>

<p>If you’re running multiple AI agents and fighting the same attention problem, give Architect a shot.</p>

<p><a href="https://github.com/forketyfork/architect/releases">Download the latest release</a> or install via Homebrew:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew tap forketyfork/architect https://github.com/forketyfork/architect
brew <span class="nb">install </span>architect
<span class="nb">cp</span> <span class="nt">-r</span> <span class="si">$(</span>brew <span class="nt">--prefix</span><span class="si">)</span>/Cellar/architect/<span class="k">*</span>/Architect.app /Applications/
</code></pre></div></div>

<p>Issues and PRs welcome.</p>

<hr />

<p>I’ve been building a few other tools for multi-agent workflows:</p>

<ul>
  <li><a href="https://github.com/forketyfork/stepcat"><strong>Stepcat</strong></a> — orchestrates multi-step implementation plans with Claude Code and Codex</li>
  <li><a href="https://github.com/forketyfork/marx"><strong>Marx</strong></a> — runs Claude, Codex, and Gemini in parallel for PR code review</li>
  <li><a href="https://github.com/forketyfork/claude-nein"><strong>Claude Nein</strong></a> — macOS menu bar app to track Claude Code spending</li>
</ul>]]></content><author><name></name></author><category term="AI" /><category term="Agentic coding" /><category term="Claude Code" /><category term="Architect" /><category term="Terminal" /><summary type="html"><![CDATA[I run multiple AI coding agents simultaneously because I often need to work on several tasks at once. One agent implements a feature, another does a code review, another improves my dev setup, yet another helps me keep my Obsidian notes up to date.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">fzkill: Fuzzy Process Killer for Your Shell</title><link href="https://forketyfork.github.io/blog/2025/10/06/fzkill-fuzzy-process-killer-for-your-shell/" rel="alternate" type="text/html" title="fzkill: Fuzzy Process Killer for Your Shell" /><published>2025-10-06T00:00:00+00:00</published><updated>2025-10-06T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/10/06/fzkill-fuzzy-process-killer-for-your-shell</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/10/06/fzkill-fuzzy-process-killer-for-your-shell/"><![CDATA[<p>I don’t know about you, but for me, killing processes from the command line always involved a ritual: run <code class="language-plaintext highlighter-rouge">ps aux</code>, scroll through output, find the target, copy the PID, then <code class="language-plaintext highlighter-rouge">kill</code> it. Or maybe grep for a pattern, parse column 2, and hope I picked the right one.</p>

<p>Now I use a better way. Meet <code class="language-plaintext highlighter-rouge">fzkill</code>: a shell function that I use to interactively fuzzy-search my running processes and kill them with immediate visual feedback.</p>

<h2 id="what-it-does">What it does</h2>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fzkill<span class="o">()</span> <span class="o">{</span>
  <span class="nb">local </span><span class="nv">pid</span><span class="o">=</span><span class="si">$(</span>ps aux | fzf <span class="nt">--header</span><span class="o">=</span><span class="s2">"Select a process to kill"</span> <span class="nt">--preview</span><span class="o">=</span><span class="s2">"echo {}"</span> | <span class="nb">awk</span> <span class="s1">'{print $2}'</span><span class="si">)</span>

  <span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span>print <span class="nt">-P</span> <span class="s2">"%F{yellow}❌ No process selected, operation canceled%f"</span>
    <span class="k">return </span>1
  <span class="k">fi

  if </span><span class="nb">kill</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then
    </span>print <span class="nt">-P</span> <span class="s2">"%F{green}✅ Successfully killed process </span><span class="nv">$pid</span><span class="s2">%f"</span>
  <span class="k">else
    </span>print <span class="nt">-P</span> <span class="s2">"%F{red}⚠️  Failed to kill process </span><span class="nv">$pid</span><span class="s2"> (may require sudo or process doesn't exist)%f"</span>
    <span class="k">return </span>1
  <span class="k">fi</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run <code class="language-plaintext highlighter-rouge">fzkill</code>, start typing to filter processes, hit Enter, done. I get:</p>
<ul>
  <li>🟡 <strong>⚠️</strong> when I cancel (press Esc or Ctrl+C)</li>
  <li>🟢 <strong>✅</strong> when the process is killed successfully</li>
  <li>🔴 <strong>❌</strong> when it fails (permissions or the process disappeared)</li>
</ul>

<h2 id="how-to-install">How to install</h2>

<h3 id="prerequisites">Prerequisites</h3>

<p>You need <a href="https://junegunn.github.io/fzf/">fzf</a> installed. Most package managers have it, I use brew:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># macOS</span>
brew <span class="nb">install </span>fzf
</code></pre></div></div>

<h3 id="for-zsh">For Zsh</h3>

<p>Add the function to your <code class="language-plaintext highlighter-rouge">~/.zshrc</code>:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fzkill<span class="o">()</span> <span class="o">{</span>
  <span class="nb">local </span><span class="nv">pid</span><span class="o">=</span><span class="si">$(</span>ps aux | fzf <span class="nt">--header</span><span class="o">=</span><span class="s2">"Select a process to kill"</span> <span class="nt">--preview</span><span class="o">=</span><span class="s2">"echo {}"</span> | <span class="nb">awk</span> <span class="s1">'{print $2}'</span><span class="si">)</span>

  <span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span>print <span class="nt">-P</span> <span class="s2">"%F{yellow}⚠️ No process selected, operation canceled%f"</span>
    <span class="k">return </span>1
  <span class="k">fi

  if </span><span class="nb">kill</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then
    </span>print <span class="nt">-P</span> <span class="s2">"%F{green}✅ Successfully killed process </span><span class="nv">$pid</span><span class="s2">%f"</span>
  <span class="k">else
    </span>print <span class="nt">-P</span> <span class="s2">"%F{red}❌  Failed to kill process </span><span class="nv">$pid</span><span class="s2"> (may require sudo or process doesn't exist)%f"</span>
    <span class="k">return </span>1
  <span class="k">fi</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Reload your shell:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> ~/.zshrc
</code></pre></div></div>

<h3 id="for-bash">For Bash</h3>

<p>Add to your <code class="language-plaintext highlighter-rouge">~/.bashrc</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fzkill<span class="o">()</span> <span class="o">{</span>
  <span class="nb">local </span><span class="nv">pid</span><span class="o">=</span><span class="si">$(</span>ps aux | fzf <span class="nt">--header</span><span class="o">=</span><span class="s2">"Select a process to kill"</span> <span class="nt">--preview</span><span class="o">=</span><span class="s2">"echo {}"</span> | <span class="nb">awk</span> <span class="s1">'{print $2}'</span><span class="si">)</span>

  <span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\0</span><span class="s2">33[0;33m⚠️ No process selected, operation canceled</span><span class="se">\0</span><span class="s2">33[0m"</span>
    <span class="k">return </span>1
  <span class="k">fi

  if </span><span class="nb">kill</span> <span class="s2">"</span><span class="nv">$pid</span><span class="s2">"</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\0</span><span class="s2">33[0;32m✅ Successfully killed process </span><span class="nv">$pid</span><span class="se">\0</span><span class="s2">33[0m"</span>
  <span class="k">else
    </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\0</span><span class="s2">33[0;31m❌  Failed to kill process </span><span class="nv">$pid</span><span class="s2"> (may require sudo or process doesn't exist)</span><span class="se">\0</span><span class="s2">33[0m"</span>
    <span class="k">return </span>1
  <span class="k">fi</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Reload:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> ~/.bashrc
</code></pre></div></div>

<h2 id="design-notes">Design notes</h2>

<p><strong>Why <code class="language-plaintext highlighter-rouge">kill</code> instead of <code class="language-plaintext highlighter-rouge">kill -9</code>?</strong>
SIGTERM (the default) lets processes clean up gracefully: flush buffers, close connections, save state. SIGKILL (<code class="language-plaintext highlighter-rouge">-9</code>) is immediate and brutal, leaving the proces no time to cleanup.</p>

<p><strong>Why fzf?</strong>
Interactive fuzzy search is better than grepping. You see what you’re about to kill, and you can type partial matches: “chro” finds Chrome, “node” finds all Node processes.</p>

<h2 id="usage-tips">Usage tips</h2>

<ul>
  <li><strong>Filter fast</strong>: Start typing immediately after running <code class="language-plaintext highlighter-rouge">fzkill</code>. “python”, “node”, “postgres” - fzf narrows as you type.</li>
  <li><strong>Preview is helpful</strong>: The preview pane shows the full <code class="language-plaintext highlighter-rouge">ps aux</code> line, so you can double-check before killing.</li>
  <li><strong>Cancel safely</strong>: Just hit Esc or Ctrl+C if you change your mind.</li>
  <li><strong>Permissions</strong>: If you get ⚠️, you might need <code class="language-plaintext highlighter-rouge">sudo</code>. Or the process died between selection and kill.</li>
</ul>

<h2 id="variants">Variants</h2>

<ul>
  <li>Want SIGKILL by default? Change <code class="language-plaintext highlighter-rouge">kill "$pid"</code> to <code class="language-plaintext highlighter-rouge">kill -9 "$pid"</code>.</li>
  <li>Want to kill multiple processes? Pipe through <code class="language-plaintext highlighter-rouge">xargs</code> and adapt the selection logic.</li>
  <li>Want to filter by user? Add <code class="language-plaintext highlighter-rouge">| grep $USER</code> before fzf.</li>
</ul>]]></content><author><name></name></author><category term="CLI" /><category term="Shell" /><category term="Productivity" /><category term="fzf" /><summary type="html"><![CDATA[I don’t know about you, but for me, killing processes from the command line always involved a ritual: run ps aux, scroll through output, find the target, copy the PID, then kill it. Or maybe grep for a pattern, parse column 2, and hope I picked the right one.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry xml:lang="en"><title type="html">New ClaudeNein app icon, Courtesy of Gemini 2.5 Flash Image Preview</title><link href="https://forketyfork.github.io/blog/2025/08/27/new-claudenein-app-icon-courtecy-of-gemini-2-5-flash-image-preview/" rel="alternate" type="text/html" title="New ClaudeNein app icon, Courtesy of Gemini 2.5 Flash Image Preview" /><published>2025-08-27T00:00:00+00:00</published><updated>2025-08-27T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/08/27/new-claudenein-app-icon-courtecy-of-gemini-2-5-flash-image-preview</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/08/27/new-claudenein-app-icon-courtecy-of-gemini-2-5-flash-image-preview/"><![CDATA[<p>Tried out the new Gemini 2.5 Flash Image Preview, was surprised how fast it is. Used it to generate a new icon for the <a href="https://github.com/forketyfork/claude-nein">ClaudeNein</a> app which turned out to be exactly what I wanted. Couldn’t help it, just had to use the “Guardians of the Galaxy” color palette.</p>

<p><img src="/img/ClaudeNein-new.png" alt="ClaudeNein app icon" class="post-image" /></p>]]></content><author><name></name></author><category term="Claude" /><category term="Gemini" /><category term="Image Generation" /><summary type="html"><![CDATA[Tried out the new Gemini 2.5 Flash Image Preview, was surprised how fast it is. Used it to generate a new icon for the ClaudeNein app which turned out to be exactly what I wanted. Couldn’t help it, just had to use the “Guardians of the Galaxy” color palette.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Ollama App Released: Quick Comparison with Enchanted LLM</title><link href="https://forketyfork.github.io/blog/2025/07/31/ollama-now-comes-as-an-app-for-macos-and-windows/" rel="alternate" type="text/html" title="Ollama App Released: Quick Comparison with Enchanted LLM" /><published>2025-07-31T00:00:00+00:00</published><updated>2025-07-31T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/07/31/ollama-now-comes-as-an-app-for-macos-and-windows</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/07/31/ollama-now-comes-as-an-app-for-macos-and-windows/"><![CDATA[<p>Today, Ollama launched its <a href="https://ollama.com/blog/new-app">desktop app for macOS and Windows</a>. As someone who regularly uses <a href="https://apps.apple.com/us/app/enchanted-llm/id6474268307">Enchanted LLM</a>, I gave the new Ollama app a spin. Here’s where it stands out:</p>

<p>What Ollama Gets Right:</p>
<ul>
  <li>Windows support — finally, a native desktop LLM app on both major platforms.</li>
  <li>Model downloads — lets you run models locally without relying on a constant internet connection.</li>
  <li>Context window slider — handy for tuning memory depth per session.</li>
</ul>

<p>Where Ollama Falls Short (for now):</p>
<ul>
  <li>No chat export — there’s no built-in way to copy or save the conversation.</li>
  <li>No system-level TTS/STT — unlike Enchanted, it doesn’t integrate with macOS speech-to-text or text-to-speech.</li>
  <li>No global hotkey integration — can’t summon it for quick completions or interactions system-wide.</li>
</ul>

<p>For now, I’ll probably keep both. Enchanted LLM still has features I rely on daily, but it’s been stagnant — no updates in over a year. Ollama feels more modern and active, so I’m keeping an eye on how fast it evolves.</p>]]></content><author><name></name></author><category term="AI" /><category term="Ollama" /><category term="Local Models" /><summary type="html"><![CDATA[Today, Ollama launched its desktop app for macOS and Windows. As someone who regularly uses Enchanted LLM, I gave the new Ollama app a spin. Here’s where it stands out:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Keeping Claude Code costs in check</title><link href="https://forketyfork.github.io/blog/2025/07/30/keeping-claude-code-costs-in-check/" rel="alternate" type="text/html" title="Keeping Claude Code costs in check" /><published>2025-07-30T00:00:00+00:00</published><updated>2025-07-30T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/07/30/keeping-claude-code-costs-in-check</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/07/30/keeping-claude-code-costs-in-check/"><![CDATA[<p>Claude Code usage isn’t free, and keeping costs under control matters—for several reasons:</p>
<ul>
  <li>Financial awareness: You stay within your budget and avoid unpleasant surprises.</li>
  <li>Environmental responsibility: Less compute = less energy, water, and waste.</li>
</ul>

<p>I care about both. So, to stay mindful of my Claude Code spend, I built a small macOS app that puts the current usage right in the menu bar:</p>

<p><img src="/img/claude-nein.png" alt="claude-nein" class="post-image" /></p>

<p>It’s called <a href="https://github.com/forketyfork/claude-nein">Claude Nein</a> — a pun on “cloud nine.”</p>

<p>Since I’m not eager to hand Apple $99/year just to ship an open-source menu bar app, it’s published unsigned. That means you’ll need to jump through some Gatekeeper hoops to run it. Still, it builds cleanly from source, and I’d love feedback, bug reports, or contributions.</p>]]></content><author><name></name></author><category term="AI" /><category term="Agentic coding" /><category term="Claude Code" /><category term="Frugality" /><summary type="html"><![CDATA[Claude Code usage isn’t free, and keeping costs under control matters—for several reasons: Financial awareness: You stay within your budget and avoid unpleasant surprises. Environmental responsibility: Less compute = less energy, water, and waste.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why I Always Say “Please” and “Thank You” to AI Assistants</title><link href="https://forketyfork.github.io/blog/2025/07/19/why-i-always-say-please-and-thank-you-to-the-ai/" rel="alternate" type="text/html" title="Why I Always Say “Please” and “Thank You” to AI Assistants" /><published>2025-07-19T00:00:00+00:00</published><updated>2025-07-19T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/07/19/why-i-always-say-please-and-thank-you-to-the-ai</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/07/19/why-i-always-say-please-and-thank-you-to-the-ai/"><![CDATA[<p>You might think it’s silly to be polite with a machine. “It’s not human,” you say. Fair point — but hear me out: it’s not about the AI, it’s about me.</p>

<p>When I start a request with “please,” I force myself to slow down and think — I’m careful with words, I clarify what I really want. If I just bark commands, I stay in hurry, sloppy mode. That tiny ritual—taking a breath, choosing words—already cools my mood.</p>

<p>And when the task is done, I type “thank you.” Again, it’s not for the AI’s sake—it doesn’t feel gratitude—but for mine. It closes the loop — I feel I’ve completed a little exchange properly. Without it, everything is just open orders and unfinished business buzzing in my head. With it, I can move on.</p>

<p>Sure, some day AI will pretend to care. Today, it doesn’t matter. The real benefit is in me staying sane, staying human. If politeness can shift my mindset by a degree — make me calmer, more focused, more thoughtful — then it’s worth sounding old-fashioned. Try it. Say “please” and “thank you” to your next AI — don’t do it for the bot, do it for you.</p>]]></content><author><name></name></author><category term="AI" /><category term="AI Assistants" /><category term="Technology" /><summary type="html"><![CDATA[You might think it’s silly to be polite with a machine. “It’s not human,” you say. Fair point — but hear me out: it’s not about the AI, it’s about me.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Stop Using AI to Fix Garbage Interfaces - Build Better Ones</title><link href="https://forketyfork.github.io/blog/2025/06/20/stop-using-ai-to-fix-garbage-interfaces/" rel="alternate" type="text/html" title="Stop Using AI to Fix Garbage Interfaces - Build Better Ones" /><published>2025-06-20T00:00:00+00:00</published><updated>2025-06-20T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/06/20/stop-using-ai-to-fix-garbage-interfaces</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/06/20/stop-using-ai-to-fix-garbage-interfaces/"><![CDATA[<p>We’re slapping AI assistants on broken systems and calling it innovation. Booking tickets, ordering food, hailing rides — all the places where AI is supposed to “help” are just UX graveyards we’ve created. You don’t hand over booking a flight to an AI because it’s beneath you — you do it because it’s a multi-hour nightmare.</p>

<p>Now imagine booking a ticket was actually fast and pleasant. Imagine the person building that system cared about making it fast and pleasant. But no. “Make this seamless and effortless” — said no product manager ever. Instead, it’s all about “engagement,” making sure users spend more time on our bloated, ad-infested site. You log in, get subscribed to newsletters you never asked for, and get pushed through a dark pattern funnel just to give someone your money.</p>

<h2 id="the-real-problem-terrible-ux-is-everywhere">The Real Problem: Terrible UX Is Everywhere</h2>

<p>Booking a concert ticket shouldn’t take three hours. But it does. Why? Because of:</p>
<ul>
  <li>Forced logins</li>
  <li>Cluttered interfaces</li>
  <li>Pop-up spam</li>
  <li>Cookie consent walls</li>
  <li>CAPTCHAs</li>
  <li>Email verification loops</li>
  <li>Push notification nags no sane person wants</li>
</ul>

<p>Of course people want an AI to deal with this mess. But let’s be honest — the real problem is we’ve normalized awful UI everywhere. And instead of fixing it, we’re now duct-taping bots over the top.</p>

<h2 id="the-real-fix-one-clean-interface-that-just-works">The Real Fix: One Clean Interface That Just Works</h2>

<p>Picture this: a single global platform for booking concert tickets (flights, rides, etc.) The UI? One clean form:</p>
<ul>
  <li>Name</li>
  <li>Location</li>
  <li>Seat preference</li>
  <li>Payment</li>
  <li>Done</li>
</ul>

<p>No 15 tabs open. No forced account creation (seriously, give me one good reason I need to “register” to buy something — I’ll wait). No spam. No verification circus.</p>

<p>If you build it right, there’s no need for AI. Just good UX.</p>

<h2 id="digital-communism-or-just-basic-sanity">Digital Communism or Just Basic Sanity?</h2>

<p>Sure, having one interface for everything sounds dystopian. But maybe it’s just… rational? We crave tools that are predictable, fast, and usable. Instead, we get a dozen half-baked services and then bring in AI as a bandaid.</p>

<p>Until we actually build good interfaces, we’ll keep wasting time developing AI that papers over our own failures. So let’s stop asking how AI can make things easier — and start asking why things are so hard in the first place.</p>]]></content><author><name></name></author><category term="AI" /><category term="AI Assistants" /><category term="Technology" /><summary type="html"><![CDATA[We’re slapping AI assistants on broken systems and calling it innovation. Booking tickets, ordering food, hailing rides — all the places where AI is supposed to “help” are just UX graveyards we’ve created. You don’t hand over booking a flight to an AI because it’s beneath you — you do it because it’s a multi-hour nightmare.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Building a Retro-Styled Blog with Windsurf</title><link href="https://forketyfork.github.io/blog/2025/03/20/building-retro-blog/" rel="alternate" type="text/html" title="Building a Retro-Styled Blog with Windsurf" /><published>2025-03-20T00:00:00+00:00</published><updated>2025-03-20T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/03/20/building-retro-blog</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/03/20/building-retro-blog/"><![CDATA[<p>I just vibe-coded this blog with Windsurf, really. The initial prompt was “Build a personal developer blog for github pages. Use 80th era style.” However, it came up with such eye-popping design that we had several iterations until converging to this.</p>]]></content><author><name></name></author><category term="AI" /><category term="Windsurf" /><category term="VibeCoding" /><summary type="html"><![CDATA[I just vibe-coded this blog with Windsurf, really. The initial prompt was “Build a personal developer blog for github pages. Use 80th era style.” However, it came up with such eye-popping design that we had several iterations until converging to this.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">IJ Conference 2025 is over</title><link href="https://forketyfork.github.io/blog/2025/03/19/ij-conference-2025/" rel="alternate" type="text/html" title="IJ Conference 2025 is over" /><published>2025-03-19T00:00:00+00:00</published><updated>2025-03-19T00:00:00+00:00</updated><id>https://forketyfork.github.io/blog/2025/03/19/ij-conference-2025</id><content type="html" xml:base="https://forketyfork.github.io/blog/2025/03/19/ij-conference-2025/"><![CDATA[<p>Had a lot of fun, got some interesting ideas. Trying to go to sleep now, while the party music is still booming somewhere below. Next week a break, then another week of KubeCon, where I’ll man the booth.</p>]]></content><author><name></name></author><category term="JetBrains" /><category term="IJ" /><category term="Conference" /><summary type="html"><![CDATA[Had a lot of fun, got some interesting ideas. Trying to go to sleep now, while the party music is still booming somewhere below. Next week a break, then another week of KubeCon, where I’ll man the booth.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://forketyfork.github.io/img/site-card.png" /><media:content medium="image" url="https://forketyfork.github.io/img/site-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>