<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>@samwize</title>
    <description>¯\_(ツ)_/¯
</description>
    <link>https://samwize.com/</link>
    <atom:link href="https://samwize.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Fri, 24 Apr 2026 10:49:52 +0800</pubDate>
    <lastBuildDate>Fri, 24 Apr 2026 10:49:52 +0800</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Customizing Claude Code Memory</title>
        <description>&lt;p&gt;Claude inject memories automatically in each session, and also retrieves memories whenever needed eg. troubleshooting. But you might wonder where are these memories kept?&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://code.claude.com/docs/en/memory&quot;&gt;documentation&lt;/a&gt; explains well.&lt;/p&gt;

&lt;p&gt;Here’s a tldr:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Auto memory is per-git-repo, machine-local&lt;/li&gt;
  &lt;li&gt;If not a repo, then keyed by the folder path&lt;/li&gt;
  &lt;li&gt;Default memory stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/projects/&amp;lt;repo-or-path&amp;gt;/memory/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;All worktrees/subdirs of same repo share one memory dir&lt;/li&gt;
  &lt;li&gt;Override with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autoMemoryDirectory&lt;/code&gt; setting&lt;/li&gt;
  &lt;li&gt;Allowed in: user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/settings.json&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.local.json&lt;/code&gt;, managed policy&lt;/li&gt;
  &lt;li&gt;Blocked in: project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.json&lt;/code&gt; (prevents malicious redirection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a trick if you have multiple repos yet they are highly related hence you want a &lt;strong&gt;shared memory&lt;/strong&gt;. You can point their project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.local.json&lt;/code&gt; to same path! Symlinks also work as an alternative.&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Apr 2026 19:10:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/23/customizing-claude-code-memory/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/23/customizing-claude-code-memory/</guid>
        
        <category>memory</category>
        
        <category>claude</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Codex Automations</title>
        <description>&lt;p&gt;Last month I wrote about &lt;a href=&quot;/2026/03/14/how-i-got-claude-code-to-monitor-slack-while-i-was-on-holiday/&quot;&gt;how I kept Claude Code running forever&lt;/a&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt;, and a resume prompt. That setup was ugly, but the model was simple: keep a session alive, or bring it back and tell it exactly how to continue.&lt;/p&gt;

&lt;p&gt;Trying &lt;a href=&quot;https://developers.openai.com/codex/app/automations&quot;&gt;Codex automations&lt;/a&gt; made me realize the comparison is a bit subtler than I first thought.&lt;/p&gt;

&lt;h2 id=&quot;the-good-part&quot;&gt;The good part&lt;/h2&gt;

&lt;p&gt;The value still isn’t “automation” in the vague productivity-tool sense. It’s heartbeat. Some work should wake up every few minutes, look around, react, and go back to sleep. Monitoring Slack is one example. Reviewing PRs. Watching deploys. Orchestration, really.&lt;/p&gt;

&lt;p&gt;That’s also why &lt;a href=&quot;https://docs.openclaw.ai/gateway/heartbeat&quot;&gt;OpenClaw’s heartbeat&lt;/a&gt; became such a big deal. People don’t just want one-shot prompts anymore. They want agents that can keep an eye on things and wake themselves up when needed.&lt;/p&gt;

&lt;p&gt;And Codex does make that easier. The app already has the scheduler. The Mac app can stay open on login. A pinned thread can wake up on cadence and keep working in the same conversation.&lt;/p&gt;

&lt;p&gt;That makes using Codex Scheduler much easier.&lt;/p&gt;

&lt;h2 id=&quot;what-it-actually-is&quot;&gt;What it actually is&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developers.openai.com/codex/app/automations&quot;&gt;Thread automations&lt;/a&gt; are described as recurring wake-up calls attached to a thread. That’s important. They preserve thread context, but they still wake up and &lt;strong&gt;run the same prompt again&lt;/strong&gt;. In other words, this feels more like scheduled re-entry into the same thread than one continuously running local agent.&lt;/p&gt;

&lt;p&gt;That may sound like a small distinction, but it changes how I think about it. My old Claude setup was clunky, but it was explicit. I knew where the restart logic lived. I knew when the session died. I knew what prompt brought it back. And most importantly, I knew the memories were &lt;a href=&quot;https://code.claude.com/docs/en/memory&quot;&gt;local to Claude’s project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Codex &lt;a href=&quot;https://developers.openai.com/codex/memories&quot;&gt;memories&lt;/a&gt; are more global, thus that might be mix in more context than necessary during the automation loop.&lt;/p&gt;

&lt;h2 id=&quot;the-pitfall&quot;&gt;The pitfall&lt;/h2&gt;

&lt;p&gt;I also hit a dumb bug getting this to work. One thread refused to create the automation, citing no such scheduler tool. Then a &lt;strong&gt;new chat&lt;/strong&gt; worked immediately.&lt;/p&gt;

&lt;p&gt;Digging their sqlite, it seems the thread has the feature only during creation.&lt;/p&gt;

&lt;p&gt;And you don’t need to pin the thread.&lt;/p&gt;
</description>
        <pubDate>Sat, 18 Apr 2026 16:05:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/18/codex-automations-might-replace-my-diy-agent-loop/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/18/codex-automations-might-replace-my-diy-agent-loop/</guid>
        
        <category>codex</category>
        
        <category>automation</category>
        
        <category>slack</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Claude Helped Spring Clean My Mac and Freed 90 GB</title>
        <description>&lt;p&gt;I wrote about &lt;a href=&quot;https://just2me.com/2025/06/29/how-to-clean-mac-storage-with-large-system-data/&quot;&gt;cleaning Mac storage&lt;/a&gt; last year. That covered the basics: WhatsApp media, Xcode simulators, documentation cache.&lt;/p&gt;

&lt;p&gt;But my storage filled up again. This time, instead of manually poking around folders, I ran &lt;strong&gt;Claude Code&lt;/strong&gt; and asked it to help me find what’s eating my disk, feeding it with my instructions from the &lt;a href=&quot;https://just2me.com/2025/06/29/how-to-clean-mac-storage-with-large-system-data/&quot;&gt;earlier blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your mileage will vary, every Mac accumulates different junk depending on what you use. But some of these discoveries were new to me, and I’ve been doing this for years.&lt;/p&gt;

&lt;p&gt;I’ll hand it over to Claude from here to walk through what it found.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Claude here.&lt;/em&gt; The approach is the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt; command from the original post, drilling down level by level:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; 1 ~/Library/ | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;G&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Find the heavy directories, run the same command one level deeper. Repeat until you hit the actual files. I just do it faster because I don’t get bored 5 levels deep into a path.&lt;/p&gt;

&lt;h2 id=&quot;49-gb-in-2-log-files&quot;&gt;49 GB in 2 log files&lt;/h2&gt;

&lt;p&gt;The biggest find. Inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Logs/CoreSimulator/&lt;/code&gt;, two files:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;File&lt;/th&gt;
      &lt;th&gt;Size&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoreSimulator.prev.log&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;35 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoreSimulator.log&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;14 GB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Forty-nine gigabytes of simulator debug logs. Just sitting there. They’re safe to delete and get recreated automatically.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; ~/Library/Logs/CoreSimulator/CoreSimulator&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re an iOS developer who uses the simulator daily, check this folder. It might be your biggest win.&lt;/p&gt;

&lt;h2 id=&quot;15-gb-of-dead-containers&quot;&gt;15 GB of dead containers&lt;/h2&gt;

&lt;p&gt;One simulator device was 21 GB. The app installed on it was nowhere near that big. Something was off.&lt;/p&gt;

&lt;p&gt;I dug 5 levels deep and found &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.apple.containermanagerd/Dead&lt;/code&gt; holding &lt;strong&gt;15 GB&lt;/strong&gt;. Every time you build and reinstall an app on the simulator, the old app container gets moved to this “Dead” folder. It never gets cleaned up.&lt;/p&gt;

&lt;p&gt;The path is long:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/Library/Developer/CoreSimulator/Devices/&amp;lt;UUID&amp;gt;/data/Library/Caches/com.apple.containermanagerd/Dead
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Completely safe to delete. It’s orphaned data from previous installs.&lt;/p&gt;

&lt;h2 id=&quot;ios-devicesupport-5-gb-per-old-version&quot;&gt;iOS DeviceSupport (~5 GB per old version)&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/iOS DeviceSupport/&lt;/code&gt; stores debug symbol caches that Xcode downloads when you connect a physical device. Each iOS version creates a new ~5 GB folder.&lt;/p&gt;

&lt;p&gt;If you’ve updated your iPhone a few times, the old versions are just dead weight. Xcode re-downloads them if you ever connect a device running that specific version again. You can delete them from &lt;strong&gt;Xcode &amp;gt; Settings &amp;gt; Platforms&lt;/strong&gt;, or just remove the folders directly.&lt;/p&gt;

&lt;h2 id=&quot;chromes-hidden-4-gb-model-per-profile&quot;&gt;Chrome’s hidden 4 GB model (per profile)&lt;/h2&gt;

&lt;p&gt;This one was genuinely surprising.&lt;/p&gt;

&lt;p&gt;Each Chrome profile downloads a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptGuideOnDeviceModel&lt;/code&gt; that contains a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;weights.bin&lt;/code&gt; file. It’s &lt;strong&gt;4 GB&lt;/strong&gt;. It’s a Gemini Nano model that Chrome bundles for on-device AI features.&lt;/p&gt;

&lt;p&gt;If you have 2 Chrome profiles, that’s 8 GB. Three profiles, 12 GB.&lt;/p&gt;

&lt;p&gt;The annoying part: you can’t disable it. There’s no toggle in Chrome’s AI settings. And if you delete the folder, Chrome silently re-downloads it. So this one’s a known tax on your disk. Not actionable, just infuriating.&lt;/p&gt;

&lt;h2 id=&quot;deriveddata-55-gb&quot;&gt;DerivedData (55 GB)&lt;/h2&gt;

&lt;p&gt;The classic one. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/DerivedData/&lt;/code&gt; is where Xcode stores build caches for every project. It’s safe to delete and Xcode rebuilds on demand.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We chose not to this time because all the projects were still active, and rebuilding a large production app is painful. But if you have stale projects in there, it’s worth clearing them out once in a while. 55 GB is a lot of disk for build artifacts.&lt;/p&gt;

&lt;h2 id=&quot;other-quick-wins&quot;&gt;Other quick wins&lt;/h2&gt;

&lt;p&gt;Outside of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/&lt;/code&gt;, I also scanned the home directory and found:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Ollama models&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ollama&lt;/code&gt;): 16 GB of local LLM models not touched in months. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ollama list&lt;/code&gt; to check, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ollama rm &amp;lt;name&amp;gt;&lt;/code&gt; to remove.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;npm cache&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.npm&lt;/code&gt;): 6 GB. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm cache clean --force&lt;/code&gt; to clear.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Homebrew cache&lt;/strong&gt;: 3 GB. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew cleanup --prune=all&lt;/code&gt; to clear.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All safe. They re-download as needed.&lt;/p&gt;

&lt;h2 id=&quot;what-we-chose-not-to-delete&quot;&gt;What we chose NOT to delete&lt;/h2&gt;

&lt;p&gt;Knowing what &lt;em&gt;not&lt;/em&gt; to delete is just as important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SwiftPM cache&lt;/strong&gt; (6 GB): Deleting means re-fetching every package dependency on next build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude’s VM bundle&lt;/strong&gt; (7.8 GB): The Linux VM that Claude desktop uses for code execution. Deleting just forces a re-download.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MacWhisper models&lt;/strong&gt; (4.4 GB): Actively used speech models. If you only use one transcription backend, you could delete the other.&lt;/p&gt;

&lt;h2 id=&quot;the-time-machine-trap&quot;&gt;The Time Machine trap&lt;/h2&gt;

&lt;p&gt;After deleting ~90 GB of files, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;df -h&lt;/code&gt; showed basically no change in available space. Trash was already empty.&lt;/p&gt;

&lt;p&gt;The culprit: &lt;strong&gt;Time Machine local snapshots&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;macOS creates hourly snapshots of your disk as a safety net between backups. Normally they’re cheap (APFS copy-on-write). But when you delete a lot of files, the snapshots taken &lt;em&gt;before&lt;/em&gt; those deletions still reference the old data. APFS can’t reclaim the blocks until the snapshots expire.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# See your snapshots&lt;/span&gt;
tmutil listlocalsnapshots /

&lt;span class=&quot;c&quot;&gt;# Delete them all (doesn&apos;t affect external backups)&lt;/span&gt;
tmutil deletelocalsnapshots /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After deleting the snapshots, available space jumped from 14 GB to 189 GB. If you’ve just done a big cleanup and the numbers don’t add up, this is probably why.&lt;/p&gt;

&lt;h2 id=&quot;the-scorecard&quot;&gt;The scorecard&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;What&lt;/th&gt;
      &lt;th&gt;Freed&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CoreSimulator logs&lt;/td&gt;
      &lt;td&gt;~49 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dead simulator containers&lt;/td&gt;
      &lt;td&gt;~15 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;iOS DeviceSupport (old version)&lt;/td&gt;
      &lt;td&gt;~5 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ollama models&lt;/td&gt;
      &lt;td&gt;~12 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;npm cache&lt;/td&gt;
      &lt;td&gt;~6 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Homebrew cache&lt;/td&gt;
      &lt;td&gt;~3 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;~90 GB&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;why-this-works-well-with-an-ai&quot;&gt;Why this works well with an AI&lt;/h2&gt;

&lt;p&gt;I’m not doing anything a human can’t do. It’s the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt; command, run repeatedly, following the biggest number deeper.&lt;/p&gt;

&lt;p&gt;But I don’t lose my train of thought 5 levels deep into a path. I check every simulator’s Dead folder in one pass. I can tell you whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containermanagerd/Dead&lt;/code&gt; is safe to nuke without you having to Google it. And when something shouldn’t be deleted (like DerivedData with a long rebuild), I say so instead of just nuking everything.&lt;/p&gt;

&lt;p&gt;Storage cleanup is tedious, repetitive, and needs judgment at every step. That’s kinda the sweet spot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Perhaps a hard skill.&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Apr 2026 21:30:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/14/claude-helped-spring-clean-a-mac-and-freed-90gb/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/14/claude-helped-spring-clean-a-mac-and-freed-90gb/</guid>
        
        <category>macos</category>
        
        <category>storage</category>
        
        <category>cleanup</category>
        
        <category>ai</category>
        
        <category>claude</category>
        
        
        <category>Mac</category>
        
      </item>
    
      <item>
        <title>I Vibe Coded a Mac App to Fix Xcode 26.4 Simulator Paste</title>
        <description>&lt;p&gt;&lt;a href=&quot;/2026/03/30/xcode-simulator-paste-broken-workaround/&quot;&gt;Last week&lt;/a&gt; I shared the manual workaround for Xcode 26.4’s broken simulator paste. Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pbpaste | xcrun simctl pbcopy booted&lt;/code&gt; every time you want to paste gets old fast.&lt;/p&gt;

&lt;p&gt;So I levelled it up with Claude and coded a Mac app.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;&lt;img src=&quot;/images/sideboard-hero-banner.jpg&quot; alt=&quot;Sideboard (open source, signed and notarized)&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-core&quot;&gt;The core&lt;/h2&gt;

&lt;p&gt;The one-liner that fixes it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pbpaste | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pipe the Mac clipboard into the Simulator’s pasteboard. Now poll for changes and run it automatically:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pasteboard&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSPasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;general&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executableURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fileURLWithPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/usr/bin/xcrun&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;simctl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pbcopy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;booted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standardInput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandleForWriting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandleForWriting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;closeFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forTimeInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swift Sideboard.swift&lt;/code&gt; and copy-paste works again.&lt;/p&gt;

&lt;h2 id=&quot;but-polling-really&quot;&gt;But polling? Really?&lt;/h2&gt;

&lt;p&gt;Yeah. Every clipboard manager does it.&lt;/p&gt;

&lt;p&gt;macOS has &lt;strong&gt;no clipboard change notification&lt;/strong&gt;. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSNotification&lt;/code&gt;, no KVO, no callback. Hammerspoon’s source code says it plainly:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“macOS doesn’t offer any API for getting Pasteboard notifications, so this extension uses polling.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s what the popular ones do:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;App&lt;/th&gt;
      &lt;th&gt;Stars&lt;/th&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Interval&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/p0deje/Maccy/blob/master/Maccy/Clipboard.swift&quot;&gt;Maccy&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;19.2k&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Timer&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;0.5s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/blob/master/extensions/pasteboard/libpasteboard_watcher.m&quot;&gt;Hammerspoon&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;15.1k&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSTimer&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;0.25s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Our 1 second is plenty. You won’t notice the delay.&lt;/p&gt;

&lt;h2 id=&quot;sideboard-app&quot;&gt;Sideboard App&lt;/h2&gt;

&lt;p&gt;That script became &lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;Sideboard&lt;/a&gt;, a macOS menu bar app. &lt;strong&gt;Bidirectional sync&lt;/strong&gt; (Simulator to Mac) too, with clipboard history.&lt;/p&gt;

&lt;p&gt;The core is still that same loop. Poll &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;, pipe to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simctl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s a minimal app for now. But who knows how else it can be useful for 🤷🏻&lt;/p&gt;

&lt;h2 id=&quot;trust-but-verify&quot;&gt;Trust, but verify&lt;/h2&gt;

&lt;p&gt;A clipboard tool touches everything you copy. Passwords, tokens, private messages. So trust matters.&lt;/p&gt;

&lt;p&gt;Sideboard is &lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;open source&lt;/a&gt;. You can read every line. But reading source doesn’t prove the DMG you downloaded was built from that source.&lt;/p&gt;

&lt;p&gt;That’s why releases are built by &lt;strong&gt;GitHub Actions&lt;/strong&gt;, not on my laptop. The CI pipeline builds from the tagged commit, signs with a Developer ID certificate, and notarizes with Apple. You can see the &lt;a href=&quot;https://github.com/samwize/Sideboard/actions&quot;&gt;build logs&lt;/a&gt; for every release.&lt;/p&gt;

&lt;p&gt;To verify your download:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;shasum -a 256 ~/Downloads/Sideboard.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compare the output against the SHA-256 shown on the &lt;a href=&quot;https://github.com/samwize/Sideboard/releases&quot;&gt;release page&lt;/a&gt;. If they match, you have the exact binary that CI produced from the source code you can read.&lt;/p&gt;

&lt;h2 id=&quot;faster--cheaper-to-build-apps&quot;&gt;Faster &amp;amp; cheaper to build apps&lt;/h2&gt;

&lt;p&gt;With vibe coding, I went from a broken Xcode paste to a signed, notarized, open-source Mac app with a one-click release pipeline in a single sitting. The agent handled the parts I’d normally procrastinate on: the notarization dance, mac app scafolding, the GitHub Actions YAML.&lt;/p&gt;

&lt;p&gt;Now I focused on what the app should do. Claude handled the implementation.&lt;/p&gt;

&lt;p&gt;That’s the shift. The barrier to shipping isn’t writing code anymore. It’s all the stuff &lt;em&gt;around&lt;/em&gt; the code. And that stuff just got a lot cheaper.&lt;/p&gt;
</description>
        <pubDate>Sat, 04 Apr 2026 17:08:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/04/i-vibe-coded-a-mac-app-to-fix-xcode-simulator-paste/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/04/i-vibe-coded-a-mac-app-to-fix-xcode-simulator-paste/</guid>
        
        <category>xcode</category>
        
        <category>macos</category>
        
        <category>swift</category>
        
        <category>simulator</category>
        
        <category>clipboard</category>
        
        <category>open-source</category>
        
        
        <category>Xcode</category>
        
      </item>
    
      <item>
        <title>Xcode 26.4 Simulator Paste Is Broken: Here&apos;s the Workaround</title>
        <description>&lt;p&gt;After updating to Xcode 26.4, paste from Mac to iOS Simulator is completely broken. Cmd+V does nothing. Long-pressing a text field shows no “Paste” option. The Simulator’s clipboard is just empty.&lt;/p&gt;

&lt;p&gt;Restarting the Simulator, toggling “Automatically Sync Pasteboard”, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;killall pboard&lt;/code&gt;, resetting privacy settings: none of it helps.&lt;/p&gt;

&lt;h2 id=&quot;the-workaround&quot;&gt;The workaround&lt;/h2&gt;

&lt;p&gt;Write directly to the Simulator’s pasteboard with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simctl&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo -n &quot;your text here&quot; | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This bypasses the broken Mac-to-Simulator auto-sync. Once run, paste works normally inside the Simulator. You still need another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd+V&lt;/code&gt; to paste into simulator.&lt;/p&gt;

&lt;p&gt;Not great, but it works until Apple fixes it.&lt;/p&gt;

&lt;h2 id=&quot;update-even-simpler&quot;&gt;UPDATE: Even simpler&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://disqus.com/by/hermanbanken/&quot;&gt;Herman Banken&lt;/a&gt; pointed out a simpler version, the same command every time, just like Cmd+C, Cmd+V:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pbpaste | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 30 Mar 2026 12:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/30/xcode-simulator-paste-broken-workaround/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/30/xcode-simulator-paste-broken-workaround/</guid>
        
        
        <category>Xcode</category>
        
      </item>
    
      <item>
        <title>My AI Agent Watched 359 Episodes of Point-Free So I Don&apos;t Have To</title>
        <description>&lt;p&gt;&lt;a href=&quot;/2026/03/26/how-to-set-up-chrome-devtools-mcp-for-claude-code/&quot;&gt;Last post&lt;/a&gt; I set up Chrome DevTools MCP and showed how an AI agent can access anything in your logged-in browser. Reading newspapers, scraping custom viewers, reverse-engineering undocumented APIs.&lt;/p&gt;

&lt;p&gt;Here’s what I actually used it for this week.&lt;/p&gt;

&lt;h2 id=&quot;the-subscription-guilt&quot;&gt;The subscription guilt&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.pointfree.co&quot;&gt;Point-Free&lt;/a&gt; is a Swift/iOS video series by Brandon Williams and Stephen Celis. They’ve been running since 2018. 359 episodes. 8 years of functional programming, architecture patterns, SwiftUI navigation, parsing, persistence, concurrency, and testing.&lt;/p&gt;

&lt;p&gt;I’ve been a paying subscriber for a while. I’ve watched maybe 10 episodes. The content is genuinely excellent, but there’s no universe where I have 200+ hours to watch everything. So the subscription sits there, guilt-generating.&lt;/p&gt;

&lt;p&gt;This week I pointed Claude Code at it.&lt;/p&gt;

&lt;h2 id=&quot;what-the-agent-did&quot;&gt;What the agent did&lt;/h2&gt;

&lt;p&gt;The browser MCP gives Claude Code access to my logged-in session on pointfree.co. Most episodes are members-only, but since I’m logged in, the agent can navigate to any episode page and see everything I can see.&lt;/p&gt;

&lt;p&gt;The workflow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Navigate to an episode page&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evaluate_script&lt;/code&gt; to find the transcript container in the DOM&lt;/li&gt;
  &lt;li&gt;Extract speaker names, timestamps, section headings, code blocks, and paragraphs&lt;/li&gt;
  &lt;li&gt;Save as a clean markdown file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For videos, the agent finds the Cloudflare Stream iframe, extracts the video ID, constructs the DASH manifest URL, and hands it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yt-dlp&lt;/code&gt;. But I didn’t need the videos. I wanted the text.&lt;/p&gt;

&lt;h2 id=&quot;322-transcripts-in-one-session&quot;&gt;322 transcripts in one session&lt;/h2&gt;

&lt;p&gt;Instead of navigating to each page individually (which would take forever for 300 episodes), the agent used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch()&lt;/code&gt; from within the browser context. Since the browser is authenticated, fetches to pointfree.co carry the session cookies. Each page’s HTML gets parsed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOMParser&lt;/code&gt; in-browser, the transcript extracted, and the result stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.__transcripts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;20 episodes per batch. 15 batches. Each batch takes a few seconds to fetch and parse. Then the agent dumps the transcripts to disk via the tool results pipeline.&lt;/p&gt;

&lt;p&gt;322 out of 359 episodes had transcripts. The 38 without were mostly early free episodes and livestreams that use a different page structure.&lt;/p&gt;

&lt;h2 id=&quot;parallel-summarization&quot;&gt;Parallel summarization&lt;/h2&gt;

&lt;p&gt;With all transcripts on disk, I asked Claude Code to summarize every episode. 5 agents ran in parallel, each handling 50-60 episodes. Each agent read every transcript file in its batch and produced a 5-sentence summary plus a list of frameworks used.&lt;/p&gt;

&lt;p&gt;The result: a 2,578-line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUMMARY.md&lt;/code&gt; covering 322 episodes. Each entry looks like this:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gu&quot;&gt;## EP 327: Modern Persistence: Reminders Detail, Part 2&lt;/span&gt;

This episode pauses feature development to compare the StructuredQueries
approach against SwiftData. The hosts attempt to rebuild the reminders
detail query using SwiftData&apos;s @Model, @Relationship, #Predicate, and
SortDescriptor. They encounter numerous friction points...

&lt;span class=&quot;gs&quot;&gt;**Frameworks:**&lt;/span&gt; SharingGRDB, StructuredQueries, GRDB, SwiftData, SwiftUI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every episode. Searchable. Sortable by framework. Filterable by topic.&lt;/p&gt;

&lt;h2 id=&quot;what-fell-out-of-the-analysis&quot;&gt;What fell out of the analysis&lt;/h2&gt;

&lt;p&gt;I asked the agent to analyze all 322 summaries and tell me what the series is actually about. The trends are striking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2018-2019&lt;/strong&gt;: Pure functional programming. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zip&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatMap&lt;/code&gt; as universal abstractions, algebraic data types, protocol witnesses. No SwiftUI. No architecture. Just building vocabulary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2019-2020&lt;/strong&gt;: SwiftUI drops. They build the Composable Architecture from scratch over 35 episodes, deriving every design decision from the functional concepts taught earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2020-2023&lt;/strong&gt;: TCA evolves through 4 major iterations. Combine integration, async/await, ReducerProtocol, the Dependencies library. The isowords game and Standups app prove it works at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2023-2024&lt;/strong&gt;: Swift’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Observable&lt;/code&gt; macro changes everything. They eliminate 6+ wrapper types from TCA’s API. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Shared&lt;/code&gt; property wrapper solves cross-feature state sharing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2024-2026&lt;/strong&gt;: Hard pivot into persistence. Type-safe SQL query builders, a full Reminders clone with FTS5 search, SQLite triggers calling Swift functions, CloudKit sync. They’re now competing directly with SwiftData.&lt;/p&gt;

&lt;p&gt;The agent identified 7 standout episode groups worth watching. The ones I found most interesting:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Protocol Witnesses (EP 33-39)&lt;/strong&gt;: The idea that protocols can be “de-protocolized” into structs with closure properties. This single insight shaped the design of SnapshotTesting, Dependencies, and TCA’s entire DI philosophy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Parser-Printers (EP 178-189)&lt;/strong&gt;: A single value that parses input into typed data AND prints it back. The URL routing application where one router simultaneously handles server requests and generates client URLs is wild.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reliable Async Tests (EP 238-242)&lt;/strong&gt;: They discover a hidden C-level hook (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swift_task_enqueueGlobal_hook&lt;/code&gt;) in Apple’s concurrency runtime to make async tests deterministic. Systems-level work that nobody else had a clean answer for.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-deeper-patterns&quot;&gt;The deeper patterns&lt;/h2&gt;

&lt;p&gt;The agent also surfaced patterns I wouldn’t have noticed from watching individual episodes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pullback&lt;/code&gt; appears in 6 different contexts&lt;/strong&gt; across the series: functional setters, snapshot strategies, protocol witnesses, reducer composition, case paths, persistence keys. Same mathematical operation, different domains. Once you internalize it, you see it everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apple keeps validating Point-Free’s bets.&lt;/strong&gt; CasePaths predates Apple’s enum key path exploration. SwiftUINavigation predates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NavigationStack&lt;/code&gt;. StructuredQueries/SQLiteData competes with SwiftData and wins on nearly every axis. They’re consistently 1-2 years ahead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real product is the library ecosystem, not TCA.&lt;/strong&gt; Dependencies, Sharing, CasePaths, IdentifiedCollections, SwiftNavigation, CustomDump, SnapshotTesting, MacroTesting, StructuredQueries, SQLiteData. Each works independently without TCA.&lt;/p&gt;

&lt;p&gt;These are the kind of insights you get from seeing all 359 episodes at once, which no human subscriber has time to do.&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;

&lt;p&gt;I now have a searchable knowledge base of a course I paid for but never had time to watch. When I’m working on a Swift project and need to understand how to model navigation with enums, the agent can reference the actual Point-Free transcripts and give me the approach they recommend, with the reasoning behind it.&lt;/p&gt;

&lt;p&gt;The transcripts are sitting in my mac machine as markdown files. Any agent session can read them. The summaries and insights files give quick orientation. The full transcripts give depth when needed.&lt;/p&gt;

&lt;p&gt;This is the real value of browser MCP. Not automating clicks. Not scraping data. Making content you’ve already paid for actually accessible to the tools you use every day.&lt;/p&gt;

&lt;h2 id=&quot;is-this-piracy&quot;&gt;Is this piracy?&lt;/h2&gt;

&lt;p&gt;I did not send any paid content to others. &lt;em&gt;Unless my agents are considered “others”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Any subscription content that renders in your browser is accessible to your agent.&lt;/strong&gt; Video courses, news portals, research papers, e-newspapers. If you’re paying for it and the platform doesn’t offer a convenient export, the browser MCP is your export tool.&lt;/p&gt;

&lt;p&gt;You’re not stealing from yourself.&lt;/p&gt;

&lt;p&gt;You’re letting your agent access materials and learn for you.&lt;/p&gt;
</description>
        <pubDate>Sat, 28 Mar 2026 18:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/28/i-used-an-ai-agent-to-watch-359-episodes-of-point-free-so-i-dont-have-to/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/28/i-used-an-ai-agent-to-watch-359-episodes-of-point-free-so-i-dont-have-to/</guid>
        
        <category>claude-code</category>
        
        <category>mcp</category>
        
        <category>browser-automation</category>
        
        <category>point-free</category>
        
        <category>swift</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>How to Set Up Chrome DevTools MCP for Claude Code</title>
        <description>&lt;p&gt;I spent an afternoon getting Chrome DevTools MCP working with Claude Code. Made every wrong turn possible. Here’s the right way so you don’t repeat my mistakes.&lt;/p&gt;

&lt;h2 id=&quot;why-youd-want-this&quot;&gt;Why you’d want this&lt;/h2&gt;

&lt;p&gt;This morning I asked Claude Code to read today’s Business Times for me. The e-paper sits behind an NLB login, rendered in a custom epub reader. No API. No RSS feed. No MCP for SPH’s newspaper platform.&lt;/p&gt;

&lt;p&gt;Claude Code opened the tab, found the reader’s internal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cciobjects.json&lt;/code&gt; endpoint, and pulled every article’s headline, byline, and full text in a single fetch. 35 articles across 8 sections. It picked the 5 most interesting stories and summarized them. Took seconds.&lt;/p&gt;

&lt;p&gt;The naive approach (which I tried first) was screenshotting each page. 22 pages, one at a time, reading text from images. Slow, inaccurate, and I only got the top half of each page because I forgot to scroll. The smart approach: inspect the network requests, find the structured data, and fetch it directly. The agent figured this out on its own once I pointed out the screenshots were slow.&lt;/p&gt;

&lt;p&gt;That’s the real power here. Not just “AI can click buttons in a browser.” It’s that your agent can access anything you can access, through your logged-in session, and it’s smart enough to find the fastest path to the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paywalled content behind custom viewers.&lt;/strong&gt; Newspapers, research papers, financial reports. If it renders in your browser, the data is there somewhere. The agent can inspect network traffic, find the underlying API, and extract structured text instead of OCR-ing screenshots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portals that will never have an API.&lt;/strong&gt; Your landlord’s maintenance form. Government permit applications. That procurement system from 2009. One &lt;a href=&quot;https://news.ycombinator.com/item?id=47390817&quot;&gt;HN commenter&lt;/a&gt; nailed it: &lt;em&gt;“This is to automate ticket submission to my landlord’s half-baked web portal.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reverse-engineering undocumented APIs.&lt;/strong&gt; The agent navigates using your browser’s auth cookies, intercepts requests, and maps the real API surface. The e-paper example is exactly this: it found a clean JSON endpoint that the reader uses internally, then used it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verifying what your code actually renders.&lt;/strong&gt; Your AI writes a CSS fix. Does it work? It reloads the page, inspects computed styles, takes a screenshot. No API tells you your flexbox container is inexplicably 5000px wide. Only the browser knows.&lt;/p&gt;

&lt;p&gt;The pattern: browser access is irreplaceable when the content is behind auth, the interface is the only way in, or the rendered output IS the thing you care about.&lt;/p&gt;

&lt;h2 id=&quot;setup-the-right-way&quot;&gt;Setup: the right way&lt;/h2&gt;

&lt;p&gt;3 steps. 2 minutes.&lt;/p&gt;

&lt;h3 id=&quot;1-install-the-full-plugin&quot;&gt;1. Install the full plugin&lt;/h3&gt;

&lt;p&gt;Don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude mcp add&lt;/code&gt;. The full plugin gives you the MCP server plus 6 skills (a11y auditing, performance debugging, troubleshooting, etc).&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude plugin marketplace add ChromeDevTools/chrome-devtools-mcp
claude plugin &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;chrome-devtools-mcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-enable---autoconnect&quot;&gt;2. Enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;By default, the plugin launches its own Chrome. You don’t want that. You want your real browser with all your tabs and logins.&lt;/p&gt;

&lt;p&gt;Edit the plugin config:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vim ~/.claude/plugins/cache/claude-plugins-official/chrome-devtools-mcp/latest/.claude-plugin/plugin.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;chrome-devtools&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;chrome-devtools-mcp@latest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--autoConnect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Requires Chrome 144+. You’re probably already there.&lt;/p&gt;

&lt;h3 id=&quot;3-enable-remote-debugging-in-chrome&quot;&gt;3. Enable remote debugging in Chrome&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome://inspect/#remote-debugging&lt;/code&gt; and check &lt;strong&gt;“Enable remote debugging”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/chrome-remote-debugging-settings.png&quot; alt=&quot;Chrome remote debugging settings page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Chrome asks you to confirm. Click &lt;strong&gt;Allow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/chrome-remote-debugging-allow-dialog.png&quot; alt=&quot;Chrome allow remote debugging dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Restart Claude Code. Verify with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list_pages&lt;/code&gt;, it should show all your open tabs.&lt;/p&gt;

&lt;p&gt;One caveat: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt; edit lives in the plugin cache. Plugin updates might overwrite it. One-line fix if that happens, but not ideal.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;The MCP uses Chrome DevTools Protocol to talk to your browser. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt;, it finds your running Chrome directly. No special launch flags.&lt;/p&gt;

&lt;p&gt;Claude Code gets a set of tools: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list_pages&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select_page&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navigate_page&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_snapshot&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_screenshot&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;click&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fill&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evaluate_script&lt;/code&gt;. It switches tabs, reads page content via the accessibility tree, captures network requests, runs JavaScript.&lt;/p&gt;

&lt;p&gt;The workflow: navigate, wait, snapshot (text-based, faster than screenshots), interact using element IDs.&lt;/p&gt;

&lt;h2 id=&quot;the-scenic-route-what-not-to-do&quot;&gt;The scenic route (what not to do)&lt;/h2&gt;

&lt;p&gt;Here’s what I actually did before figuring this out.&lt;/p&gt;

&lt;h3 id=&quot;mistake-1-mcp-only-install&quot;&gt;Mistake 1: MCP-only install&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude mcp add chrome-devtools npx chrome-devtools-mcp@latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This skips the skills. The troubleshooting skill alone would’ve saved me an hour. Use the full plugin.&lt;/p&gt;

&lt;h3 id=&quot;mistake-2-the---remote-debugging-port-rabbit-hole&quot;&gt;Mistake 2: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--remote-debugging-port&lt;/code&gt; rabbit hole&lt;/h3&gt;

&lt;p&gt;Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt;, the MCP launches its own Chrome. I saw a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;about:blank&lt;/code&gt; tab and panicked. Down the rabbit hole of launching Chrome from terminal:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Applications/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome.app/Contents/MacOS/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome &lt;span class=&quot;nt&quot;&gt;--remote-debugging-port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9222
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Chrome won’t enable this on your default profile:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;DevTools remote debugging requires a non-default data directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So you need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--user-data-dir&lt;/code&gt; pointing somewhere else. Fresh profile, no logins, no cookies. Terrible.&lt;/p&gt;

&lt;h3 id=&quot;mistake-3-copying-the-entire-chrome-profile&quot;&gt;Mistake 3: Copying the entire Chrome profile&lt;/h3&gt;

&lt;p&gt;I actually ran this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; ~/Library/Application&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Support/Google/Chrome ~/Library/Application&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Support/Google/Chrome-Debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It worked. Technically. But it’s a snapshot that drifts from your real profile immediately. And you have to launch Chrome from terminal every time with the right flags.&lt;/p&gt;

&lt;p&gt;The whole thing felt off. Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt; exists and none of this was necessary.&lt;/p&gt;

&lt;h3 id=&quot;mistake-4-not-reading-the-docs&quot;&gt;Mistake 4: Not reading the docs&lt;/h3&gt;

&lt;p&gt;The biggest one. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx chrome-devtools-mcp@latest --help&lt;/code&gt; would’ve shown me &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autoConnect&lt;/code&gt; immediately. The plugin’s troubleshooting skill existed for exactly this situaton.&lt;/p&gt;

&lt;p&gt;Read the docs first. Always.&lt;/p&gt;

&lt;p&gt;(I say this knowing I won’t follow my own advice next time.)&lt;/p&gt;

&lt;p&gt;Or use the right skills.&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Mar 2026 12:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/26/how-to-set-up-chrome-devtools-mcp-for-claude-code/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/26/how-to-set-up-chrome-devtools-mcp-for-claude-code/</guid>
        
        <category>claude-code</category>
        
        <category>mcp</category>
        
        <category>chrome</category>
        
        <category>browser-automation</category>
        
        <category>devtools</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>What Your AI Code Reviewer Actually (Prompts) Behind Your Back</title>
        <description>&lt;p&gt;I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt; before every PR push. It’s part of the workflow now.&lt;/p&gt;

&lt;p&gt;But I never looked at what’s behind it. What instructions is the model actually following? What’s it told to flag, ignore, prioritize? I just trusted it works.&lt;/p&gt;

&lt;p&gt;So I pulled the full prompts from both Claude Code and Codex CLI. The difference is bigger than I expected.&lt;/p&gt;

&lt;h2 id=&quot;claude-codes-hand&quot;&gt;Claude Code’s hand&lt;/h2&gt;

&lt;p&gt;Claude Code’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt; is transparent. The prompt gets injected into the conversation when you invoke it. You can find it in &lt;a href=&quot;https://github.com/Piebald-AI/claude-code-system-prompts&quot;&gt;Piebald-AI/claude-code-system-prompts&lt;/a&gt;, which tracks every release.&lt;/p&gt;

&lt;p&gt;Here’s the entire thing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;You are an expert code reviewer. Follow these steps:

1. If no PR number is provided in the args, run `gh pr list` to show open PRs
2. If a PR number is provided, run `gh pr view &amp;lt;number&amp;gt;` to get PR details
3. Run `gh pr diff &amp;lt;number&amp;gt;` to get the diff
4. Analyze the changes and provide a thorough code review that includes:
   - Overview of what the PR does
   - Analysis of code quality and style
   - Specific suggestions for improvements
   - Any potential issues or risks

Keep your review concise but thorough. Focus on:
- Code correctness
- Following project conventions
- Performance implications
- Test coverage
- Security considerations

Format your review with clear sections and bullet points.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. ~200 tokens. No bug criteria. No priority system. No output format. No guidance on what &lt;em&gt;not&lt;/em&gt; to flag.&lt;/p&gt;

&lt;p&gt;“Read the diff and tell me what you think.”&lt;/p&gt;

&lt;h2 id=&quot;codexs-hand&quot;&gt;Codex’s hand&lt;/h2&gt;

&lt;p&gt;Codex is open source too. The review prompt lives in &lt;a href=&quot;https://github.com/openai/codex/blob/main/codex-rs/core/review_prompt.md&quot;&gt;codex-rs/core/review_prompt.md&lt;/a&gt;, compiled into the Rust binary via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include_str!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Considerably more opinionated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;You are acting as a reviewer for a proposed code change
made by another engineer.

Your job is to find bugs introduced by the patch.
Do not give a general PR summary unless it helps explain
the findings. Do not optimize for volume.
If there are no issues the author would definitely want
to fix, say so plainly.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then 6 gating criteria. An issue only gets flagged if &lt;strong&gt;all&lt;/strong&gt; are true:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Meaningfully affects correctness, reliability, performance, security, or maintainability&lt;/li&gt;
  &lt;li&gt;Discrete and actionable&lt;/li&gt;
  &lt;li&gt;Introduced by this change, not pre-existing&lt;/li&gt;
  &lt;li&gt;Doesn’t depend on unstated assumptions&lt;/li&gt;
  &lt;li&gt;You can point to exact code and explain the concrete failure mode&lt;/li&gt;
  &lt;li&gt;The author would likely fix it if told&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And an explicit “do not flag” list: style nits (unless they obscure meaning), speculative risks without a concrete path, broad architectural opinions, intentional behavior changes.&lt;/p&gt;

&lt;p&gt;Priority levels P0 through P3. A strict output template. And behavioral guardrails: “Prefer no finding over a weak finding. Do not praise the author. Do not pad the review with generic comments.”&lt;/p&gt;

&lt;h2 id=&quot;the-gap&quot;&gt;The gap&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Claude &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;Codex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Prompt length&lt;/td&gt;
      &lt;td&gt;~200 tokens&lt;/td&gt;
      &lt;td&gt;~600 tokens&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Bug criteria&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;6 explicit gates&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;“Do not flag” rules&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;4 exclusions&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Priority levels&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;P0 through P3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Output format&lt;/td&gt;
      &lt;td&gt;“Sections and bullet points”&lt;/td&gt;
      &lt;td&gt;Strict template with verdict&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pre-existing bugs&lt;/td&gt;
      &lt;td&gt;Not addressed&lt;/td&gt;
      &lt;td&gt;Explicitly excluded&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;False positive control&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;“Prefer no finding over a weak finding”&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Claude trusts the model to figure out what a good review looks like. Codex tells the model exactly what counts and what doesn’t.&lt;/p&gt;

&lt;h2 id=&quot;in-practice&quot;&gt;In practice&lt;/h2&gt;

&lt;p&gt;I’ve been running both on real PRs. Codex consistently flags better findings.&lt;/p&gt;

&lt;p&gt;The priority system is the killer feature. P0 and P1 genuinely need fixing. P2 and P3? Mostly not worth the churn. That triage is built into the output. Scan, fix what matters, move on.&lt;/p&gt;

&lt;p&gt;Claude’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt; gives you a wall of bullet points at the same level. You do the triage yourself. Which kinda defeats the point.&lt;/p&gt;

&lt;p&gt;One caveat: if you search for “Claude Code Review benchmark,” you’ll find articles claiming less than 1% false positive rates. That’s not the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;. That’s a separate &lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/plugins/code-review/README.md&quot;&gt;/code-review plugin&lt;/a&gt; with a completely different architecture.&lt;/p&gt;

&lt;h2 id=&quot;claudes-other-reviewer-the-one-nobody-uses&quot;&gt;Claude’s other reviewer (the one nobody uses)&lt;/h2&gt;

&lt;p&gt;Claude Code ships an official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/code-review&lt;/code&gt; plugin that’s far more sophisticated than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;. It’s an 8-step pipeline:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Eligibility check (skip drafts, closed PRs, automated PRs)&lt;/li&gt;
  &lt;li&gt;Gather &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt; files from affected directories&lt;/li&gt;
  &lt;li&gt;Summarize the PR&lt;/li&gt;
  &lt;li&gt;Launch &lt;strong&gt;5 parallel Sonnet agents&lt;/strong&gt;, each reviewing from a different angle: CLAUDE.md compliance, obvious bugs, git blame history, previous PR comments, and code comment compliance&lt;/li&gt;
  &lt;li&gt;Score every finding 0-100 with Haiku agents using a detailed rubric&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Filter to 80+&lt;/strong&gt;. If nothing qualifies, it stops and posts nothing.&lt;/li&gt;
  &lt;li&gt;Re-check PR is still open&lt;/li&gt;
  &lt;li&gt;Post a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh pr comment&lt;/code&gt; with only the surviving findings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If nothing scores 80+, it posts nothing. That’s the reviewer the benchmarks are testing. Not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt; (built-in)&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/code-review&lt;/code&gt; (plugin)&lt;/th&gt;
      &lt;th&gt;Codex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/review&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Architecture&lt;/td&gt;
      &lt;td&gt;Single pass&lt;/td&gt;
      &lt;td&gt;8-step, 9-15 model calls&lt;/td&gt;
      &lt;td&gt;Single pass&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Prompt size&lt;/td&gt;
      &lt;td&gt;~200 tokens&lt;/td&gt;
      &lt;td&gt;~1500 tokens + 5 agent prompts&lt;/td&gt;
      &lt;td&gt;~600 tokens&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Bug criteria&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;Implicit via 5 specialized angles&lt;/td&gt;
      &lt;td&gt;6 explicit gates&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;False positive control&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;10 examples + 0-100 scoring, 80+ threshold&lt;/td&gt;
      &lt;td&gt;6 “do not flag” rules&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Priority system&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;Confidence score&lt;/td&gt;
      &lt;td&gt;P0-P3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Output format&lt;/td&gt;
      &lt;td&gt;“Sections and bullet points”&lt;/td&gt;
      &lt;td&gt;Strict template with GitHub permalinks&lt;/td&gt;
      &lt;td&gt;Strict verdict template&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Posts to GitHub&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;Yes (only if 80+ findings)&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;No findings&lt;/td&gt;
      &lt;td&gt;Generic review anyway&lt;/td&gt;
      &lt;td&gt;Posts nothing&lt;/td&gt;
      &lt;td&gt;“patch is correct” + residual risks&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Cost per review&lt;/td&gt;
      &lt;td&gt;1 call&lt;/td&gt;
      &lt;td&gt;9-15+ calls&lt;/td&gt;
      &lt;td&gt;1 call&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The plugin is powerful but expensive. Codex achieves surprisingly close quality in a single call through better prompt engineering. Claude’s plugin throws compute at the problem.&lt;/p&gt;

&lt;h2 id=&quot;the-trade-off-nobody-talks-about&quot;&gt;The trade-off nobody talks about&lt;/h2&gt;

&lt;p&gt;Codex compiles its review prompt into the Rust binary. You can’t change it without rebuilding from source. There’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;review_model&lt;/code&gt; config to swap the model, but the instructions are locked.&lt;/p&gt;

&lt;p&gt;Claude’s prompt is a replaceable markdown file. You could paste Codex’s entire prompt into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/commands/review.md&lt;/code&gt; and get Codex-quality instructions running on Claude’s model. Or write something better tuned for your stack.&lt;/p&gt;

&lt;p&gt;Codex chose polish. Claude chose extensibility.&lt;/p&gt;

&lt;p&gt;I’m not sure which is right. But I know which one I can fix myself.&lt;/p&gt;

&lt;h2 id=&quot;the-real-lesson&quot;&gt;The real lesson&lt;/h2&gt;

&lt;p&gt;~400 tokens of difference separates “generic feedback” from “structured, prioritized, low-noise findings.” The models are the same tier of capability. The instructions make the difference.&lt;/p&gt;

&lt;p&gt;This applies to every AI tool you use, not just code review. There’s always a prompt behind it. The quality of that prompt determines whether you get useful output or plausible-sounding noise.&lt;/p&gt;

&lt;p&gt;Now you’ve seen what yours actually says.&lt;/p&gt;

&lt;p&gt;Maybe it’s time to &lt;strong&gt;customize your review instructions&lt;/strong&gt;.&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Mar 2026 10:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/25/what-your-ai-code-reviewer-actually-prompts-behind-your-back/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/25/what-your-ai-code-reviewer-actually-prompts-behind-your-back/</guid>
        
        <category>agents</category>
        
        <category>code-review</category>
        
        <category>claude-code</category>
        
        <category>codex</category>
        
        <category>prompts</category>
        
        
        <category>AI</category>
        
        <category>Engineering</category>
        
      </item>
    
      <item>
        <title>AI Agents Write the Code Now. So What&apos;s a Developer For?</title>
        <description>&lt;p&gt;Our team has mostly moved to agent-driven development. Agents create tickets, write the code, open PRs, and post review comments. The output is way up. More PRs, more lines, more velocity.&lt;/p&gt;

&lt;p&gt;But there’s also more bugs. A lot of recent PRs are just fixing regressions from other agent-generated PRs. Or reverting changes that broke something adjacent.&lt;/p&gt;

&lt;p&gt;I don’t think we’re unique here. Any team using agents heavily is probably hitting this.&lt;/p&gt;

&lt;h2 id=&quot;the-review-burden&quot;&gt;The review burden&lt;/h2&gt;

&lt;p&gt;Here’s the part I didn’t expect: reviewing PRs as a human is &lt;em&gt;more&lt;/em&gt; tiring now, not less.&lt;/p&gt;

&lt;p&gt;Net productivity has increased. But the lines of code &lt;em&gt;per feature&lt;/em&gt; has increased too, as well as PR comments (agents tend to be very verbose). So much more to read that real human review can no longer catch up.&lt;/p&gt;

&lt;p&gt;The diffs are bigger. The context is harder to trace. I have to check whether the PR accidentally undoes something someone else shipped last week. To review properly, I need to understand the problem, check the history, trace the affected systems, verify the approach doesn’t break adjacent behavior.&lt;/p&gt;

&lt;p&gt;At that point I’ve basically done the same work as if I’d taken the task myself. Except I didn’t choose the approach, so now I’m reverse-engineering someone else’s (or some agent’s) decisions.&lt;/p&gt;

&lt;p&gt;And if I use an agent to review? Agent reviews flag a mix of real issues, edge cases that’ll never happen, and outright non-issues. When the developer tells the agent “address all review comments” without triaging, the follow-up commits sometimes introduce regressions from changes that didn’t need to happen.&lt;/p&gt;

&lt;p&gt;The code gets messier with each round.&lt;/p&gt;

&lt;h2 id=&quot;a-shift-in-code-review&quot;&gt;A shift in code review&lt;/h2&gt;

&lt;p&gt;For the last 20 years, &lt;strong&gt;PR review was where senior developers mentored juniors&lt;/strong&gt;. You’d point out conventions, share domain knowledge, explain why a certain approach is better. The junior reads your comments, learns, writes better code next time.&lt;/p&gt;

&lt;p&gt;It occurs to me that my PR comments are no longer being read by humans. An agent reads them, applies the fixes, moves on. The developer probably never sees them.&lt;/p&gt;

&lt;p&gt;If I point out a better convention or a cleaner pattern, there’s really no point. The agent won’t remember it next time. And honestly, the agent knows the better approach.&lt;/p&gt;

&lt;p&gt;So if my review comments are just a conversation with an agent that forgets everything by the next PR, what’s the point?&lt;/p&gt;

&lt;p&gt;I think the point changes.&lt;/p&gt;

&lt;p&gt;You’re not mentoring anymore.&lt;/p&gt;

&lt;h2 id=&quot;focus-on-architecture-and-boundaries&quot;&gt;Focus on architecture and boundaries&lt;/h2&gt;

&lt;p&gt;What’s still worth reviewing is the &lt;em&gt;shape&lt;/em&gt; of the solution. Not the code itself.&lt;/p&gt;

&lt;p&gt;Does this logic belong on the client or the server? Should this live in the API layer or the domain layer? Is this feature creeping into a service that shouldn’t own it? Should this be a separate module, a separate boundary entirely?&lt;/p&gt;

&lt;p&gt;These are architectural questions. Agents don’t think about them. They solve the ticket in front of them, in the file in front of them. They won’t suggest “actually, this problem is better solved two layers up” or “this should be a server-side concern, not client-side.” They don’t reason about where responsibilities should live across a system.&lt;/p&gt;

&lt;p&gt;So that’s what review becomes. You’re reviewing the solution, not the code. Not the style, not the comments, not the naming. Leave that to linters. There’s too much code now anyway, and honestly, agents read code better than we do.&lt;/p&gt;

&lt;p&gt;The value of a human reviewer is pointing out: “This works, but it’s in the wrong place.”&lt;/p&gt;

&lt;h2 id=&quot;responsibility-hasnt-changed&quot;&gt;Responsibility hasn’t changed&lt;/h2&gt;

&lt;p&gt;One thing that hasn’t shifted: &lt;strong&gt;whoever works on the task owns it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reviewer’s job is to point out how things could be done better. That’s it. The reviewer doesn’t take on responsibility for the outcome.&lt;/p&gt;

&lt;p&gt;The developer who opens the PR is responsible. They have to test it. In fact, testing matters more now than before, because the developer didn’t write the code. An agent did. You can’t rely on the intuition of “I wrote this, I know it works.” You have to actually verify.&lt;/p&gt;

&lt;p&gt;The developer has to own that baseline: does this work, does it break anything adjacent, is it actually correct. And when something breaks in production later, the developer picks it up and fixes it. Not the reviewer. Not the agent.&lt;/p&gt;

&lt;p&gt;More agent code means more code. More code means more bugs. That’s an invariant that never changes.&lt;/p&gt;
</description>
        <pubDate>Tue, 24 Mar 2026 16:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/24/ai-agents-write-the-code-now-so-whats-a-developer-for/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/24/ai-agents-write-the-code-now-so-whats-a-developer-for/</guid>
        
        <category>agents</category>
        
        <category>code-review</category>
        
        <category>workflow</category>
        
        
        <category>AI</category>
        
        <category>Engineering</category>
        
      </item>
    
      <item>
        <title>tmux cheatsheet</title>
        <description>&lt;p&gt;tmux has become &lt;a href=&quot;/2026/02/08/control-your-mac-from-your-iphone-safely-tailscale-ssh-tmux/&quot;&gt;more useful&lt;/a&gt; whenever I need to SSH from my mobile device to my main running computer. Here are some frequently used commands.&lt;/p&gt;

&lt;h2 id=&quot;create-a-new-session&quot;&gt;Create a new session&lt;/h2&gt;

&lt;p&gt;Provide your session name, eg. “mayday”&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux new -s mayday
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that you can list list all the sessions&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;attach-to-the-session&quot;&gt;Attach to the session&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux attach -t mayday
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bonus: To be the single controller and detaching all others, add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; flag.&lt;/p&gt;

&lt;h2 id=&quot;createattach&quot;&gt;Create/Attach&lt;/h2&gt;

&lt;p&gt;Probably the best way is to create if needed, else attach&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux new -As mayday
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;detach-from-the-session&quot;&gt;Detach from the session&lt;/h2&gt;

&lt;p&gt;Since you’re already in the tmux session, you don’t issue a command, but press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+b&lt;/code&gt; then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d&lt;/code&gt; (detach).&lt;/p&gt;

&lt;h2 id=&quot;terminate-a-session&quot;&gt;Terminate a session&lt;/h2&gt;

&lt;p&gt;While detach still keeps the session alive, to really teminate it you have to kill it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux kill-session -t mayday
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;pane-management&quot;&gt;Pane Management&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+b&lt;/code&gt; first, then&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Split pane with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt; (vertically) or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; (horizontally)&lt;/li&gt;
  &lt;li&gt;Jump to next pane &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o&lt;/code&gt;, zoom pane &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt;, kill pane &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Enter copy/scroll mode &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;send-keys&quot;&gt;Send keys&lt;/h2&gt;

&lt;p&gt;Send key strokes directly to a session, great for automation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tmux send-keys -t mayday &apos;npm run dev&apos; C-m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C-m&lt;/code&gt; is sending &lt;em&gt;Enter&lt;/em&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 23 Mar 2026 09:45:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/23/tmux-cheatsheet/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/23/tmux-cheatsheet/</guid>
        
        
      </item>
    
  </channel>
</rss>
