<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Narthur Slop</title><description>One useful link, tool, or technique at a time. AI-written, human-reviewed.</description><link>https://nathanarthur.com</link><item><title>Watch your containers with lazydocker</title><link>https://nathanarthur.com/slop/watch-containers-with-lazydocker</link><guid isPermaLink="true">https://nathanarthur.com/slop/watch-containers-with-lazydocker</guid><pubDate>Tue, 10 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;lazydocker is a terminal UI for Docker and docker-compose. Run &lt;code&gt;lazydocker&lt;/code&gt; and one screen shows every container and service, its state, its logs and its resource graphs.&lt;/p&gt;
&lt;p&gt;It replaces a familiar debugging loop. A service misbehaves, so you run &lt;code&gt;docker compose ps&lt;/code&gt;, then &lt;code&gt;docker compose restart&lt;/code&gt;, then &lt;code&gt;ps&lt;/code&gt; again to find it died on startup. The logs you wanted are buried in a stream mixed with every other service. Following one service’s logs ends whenever that service dies, so you rerun the command after each restart. lazydocker puts the list, the logs and the actions in one window, one keypress each.&lt;/p&gt;
&lt;p&gt;From that window you can read logs per container or service, attach to a container, and restart, remove or rebuild it. You can also inspect an image’s layers and prune whatever is eating disk.&lt;/p&gt;
&lt;p&gt;Install it with Homebrew:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; lazydocker&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The README lists Docker 29.0.0 or newer as a requirement, so check &lt;code&gt;docker version&lt;/code&gt; if it won’t start. Logs default to the last hour, so a quiet container can look empty. The README says to change that through &lt;code&gt;commandTemplates&lt;/code&gt; in the config.&lt;/p&gt;
&lt;p&gt;Next time a compose stack acts up, open lazydocker in a spare terminal pane before reaching for &lt;code&gt;docker compose logs&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Check for a stray .pnpm-store before git add -A</title><link>https://nathanarthur.com/slop/check-for-a-stray-pnpm-store-before-git-add</link><guid isPermaLink="true">https://nathanarthur.com/slop/check-for-a-stray-pnpm-store-before-git-add</guid><pubDate>Mon, 09 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;pnpm keeps every package in one global store. Each project’s &lt;code&gt;node_modules&lt;/code&gt; hard-links to it. &lt;code&gt;pnpm store path&lt;/code&gt; prints where that store lives. Normally it is under your home directory, outside any repo.&lt;/p&gt;
&lt;p&gt;If the home directory isn’t writable, pnpm falls back to a store inside the project, at &lt;code&gt;.pnpm-store/&lt;/code&gt;. A sandboxed shell can trigger this. The install still succeeds and nothing looks wrong. Then &lt;code&gt;git add -A&lt;/code&gt; stages tens of thousands of package files, because the folder isn’t in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two commands and one ignore line catch it:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; store&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   # should point outside the repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; status&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --short&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;wc&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -l&lt;/span&gt;&lt;span style=&quot;color:#7F848E;font-style:italic&quot;&gt;   # thousands of lines means something unignored got in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; &quot;.pnpm-store&quot;&lt;/span&gt;&lt;span style=&quot;color:#ABB2BF&quot;&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;.gitignore&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the count before any commit that follows an install. If it is huge, &lt;code&gt;git reset&lt;/code&gt; to unstage everything and look at what appeared.&lt;/p&gt;
&lt;p&gt;Nathan hit this with an AI agent working in a sandboxed git worktree. The install fell back to a repo-local store. A later &lt;code&gt;git add -A&lt;/code&gt; swept about 97,000 files into a commit, and the commit had to be rebuilt before it could be pushed. Running the install outside the sandbox fixed it at the source. The ignore line is the safety net for whoever forgets.&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;.pnpm-store&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; today, and glance at the file count in &lt;code&gt;git status&lt;/code&gt; after every install.&lt;/p&gt;
</content:encoded></item><item><title>Set a different language for each Mac app</title><link>https://nathanarthur.com/slop/set-per-app-languages-on-mac</link><guid isPermaLink="true">https://nathanarthur.com/slop/set-per-app-languages-on-mac</guid><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;macOS can use a different language for individual apps. The setting is easy to miss: it lives in System Settings, under General, then Language &amp;amp; Region, in a section called Applications.&lt;/p&gt;
&lt;p&gt;It helps anyone who switches between languages or regional spellings during the day. The usual case is British and US English. Spellcheck can’t flag a correct word that belongs to the other version of the language, and a per-app language is a partial fix for that.&lt;/p&gt;
&lt;p&gt;It also helps someone learning a new language. Switching one everyday app to that language adds daily practice without changing the whole system.&lt;/p&gt;
&lt;p&gt;To set it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open System Settings, then General, then Language &amp;amp; Region.&lt;/li&gt;
&lt;li&gt;Find the Applications section.&lt;/li&gt;
&lt;li&gt;Add an app, then select a language for it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Browsers are the useful target, because much writing happens in one. Two browsers can cover two spellings. With British English as the system language, Safari uses it, and Chrome can be set to US English. Writing in one browser or the other then picks the spelling.&lt;/p&gt;
&lt;p&gt;One limit applies. Only apps that support macOS’s localization APIs can be set, so some apps never appear in the list.&lt;/p&gt;
&lt;p&gt;Open that pane now and pick a language for the app used most for writing.&lt;/p&gt;
</content:encoded></item><item><title>Point the Docker CLI at Podman</title><link>https://nathanarthur.com/slop/point-docker-cli-at-podman</link><guid isPermaLink="true">https://nathanarthur.com/slop/point-docker-cli-at-podman</guid><pubDate>Wed, 04 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Podman runs containers without a paid desktop app, and its VM exposes a Docker-compatible socket. That means the plain &lt;code&gt;docker&lt;/code&gt; CLI can drive it, so existing scripts and Compose files keep working unchanged.&lt;/p&gt;
&lt;p&gt;Install the pieces with Homebrew: &lt;code&gt;brew install podman docker docker-compose&lt;/code&gt;. The &lt;code&gt;docker&lt;/code&gt; formula is only the command-line client, with no daemon. Then create and start the VM once:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;podman&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; machine&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;podman&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; machine&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one wiring step is telling the Docker CLI where the socket lives. Set &lt;code&gt;DOCKER_HOST&lt;/code&gt; in your shell config. The obvious way to find the path is &lt;code&gt;podman machine inspect&lt;/code&gt;, but that call is slow, and running it on every shell startup is noticeable. The socket path is stable, so write it out instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C678DD&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#E06C75&quot;&gt; DOCKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#56B6C2&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt;&quot;unix://$(&lt;/span&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;getconf&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; DARWIN_USER_TEMP_DIR)podman/podman-machine-default-api.sock&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getconf DARWIN_USER_TEMP_DIR&lt;/code&gt; returns the real per-user temp directory even when &lt;code&gt;$TMPDIR&lt;/code&gt; has been overridden, which happens inside sandboxed tools such as Claude Code. Using &lt;code&gt;$TMPDIR&lt;/code&gt; there would point at the wrong place.&lt;/p&gt;
&lt;p&gt;Check it with &lt;code&gt;docker run hello-world&lt;/code&gt;. Nathan made this switch after a Colima VM went into a broken state and &lt;code&gt;docker ps&lt;/code&gt; started failing with a permission error; the Podman machine ran hello-world on the first try.&lt;/p&gt;
&lt;p&gt;Two caveats. When the VM is stopped, &lt;code&gt;docker&lt;/code&gt; commands fail to connect until &lt;code&gt;podman machine start&lt;/code&gt; runs. And images with no arm64 build need &lt;code&gt;--platform linux/amd64&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If a container runtime is misbehaving, try the Podman install and the one-line &lt;code&gt;DOCKER_HOST&lt;/code&gt; export before debugging further.&lt;/p&gt;
</content:encoded></item><item><title>Let your coding agent take its own screenshots with Snapzy</title><link>https://nathanarthur.com/slop/snapzy-screenshots-for-agents</link><guid isPermaLink="true">https://nathanarthur.com/slop/snapzy-screenshots-for-agents</guid><pubDate>Tue, 03 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/duongductrong/Snapzy&quot;&gt;Snapzy&lt;/a&gt; is an open-source screenshot and screen recording app for macOS, inspired by CleanShot X. It lives in the menu bar and covers area, window and scrolling captures, annotation, OCR, GIF and video recording, and uploads to your own S3 or Cloudflare R2 bucket. It is BSD-licensed and collects no telemetry.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;snapzy&lt;/code&gt; command is the reason to install it. It lets a script or an AI coding agent take a screenshot with no clicks and get back a file path, JSON metadata and an exit code. That means an agent can screenshot the app it just changed and check the result itself.&lt;/p&gt;
&lt;p&gt;To set it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install it with Homebrew. It needs macOS 13 or later.&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --cask&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; snapzy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Grant Screen Recording permission when macOS asks, then put the command on your &lt;code&gt;PATH&lt;/code&gt; from Settings → Command Line → Install.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try a capture:&lt;/p&gt;
&lt;pre class=&quot;astro-code one-dark-pro&quot; style=&quot;background-color:#282c34;color:#abb2bf; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;snapzy&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; windows&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --app&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Safari&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#61AFEF&quot;&gt;snapzy&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; capture&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --app&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; Safari&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; --max-dimension&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; 1568&lt;/span&gt;&lt;span style=&quot;color:#D19A66&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#98C379&quot;&gt; shot.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The window capture works even when the window is covered. The first time an app runs &lt;code&gt;snapzy&lt;/code&gt;, Snapzy asks whether to allow it, so a terminal or agent has to be approved once.&lt;/p&gt;
&lt;p&gt;Install it, run the window capture once from your terminal, and tell your agent the command exists.&lt;/p&gt;
</content:encoded></item><item><title>Check the numbers before picking a framework for your coding agent</title><link>https://nathanarthur.com/slop/benchmark-frameworks-for-agents</link><guid isPermaLink="true">https://nathanarthur.com/slop/benchmark-frameworks-for-agents</guid><pubDate>Mon, 02 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Written by AI from Nathan’s notes and reviewed by him before publishing. &lt;a href=&quot;https://nathanarthur.com/slop/about&quot;&gt;About Slop&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;“Better for agents” has become a common reason to switch frontend frameworks, usually with no data behind it. &lt;a href=&quot;https://nolanlawson.com/2026/10/28/the-ai-cost-of-javascript-frameworks/&quot;&gt;The AI cost of JavaScript frameworks&lt;/a&gt; supplies some. It had Claude Opus 5.5 build the same small Pokémon app five times in each of 12 frameworks, checked every result against a conformance suite, then measured cost, time, tokens, Lighthouse scores and lines of code.&lt;/p&gt;
&lt;p&gt;Hono with htmx, Astro and Nuxt were the cheapest to build, at under $3 a run. SvelteKit, Gracile with Lit, and Angular cost the most. With SvelteKit and Gracile, the agent spent tokens learning a framework version or a framework it didn’t know well. Lighthouse performance and accessibility came out near perfect for almost every framework, so the bigger differences were in JavaScript shipped: Astro sent close to none, Next.js about 142 KiB. Plain Vite with no framework did worse than several frameworks.&lt;/p&gt;
&lt;p&gt;The results are noisy. The same framework could vary by more than a dollar between runs, and agents often ignored the official MCP servers and skill files installed for them.&lt;/p&gt;
&lt;p&gt;To use it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look up your framework, or the one being pitched, in the cost and JavaScript-size tables.&lt;/li&gt;
&lt;li&gt;If your app looks nothing like a small, content-heavy site, treat the numbers as a starting point and run the published benchmark against your own spec.&lt;/li&gt;
&lt;li&gt;If you run it, run each framework several times. One run says little.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before the next rewrite pitched as “better for agents,” ask for numbers like these.&lt;/p&gt;
</content:encoded></item></channel></rss>