<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hayato Wada</title>
    <description>The latest articles on DEV Community by Hayato Wada (@hatoya).</description>
    <link>https://dev.to/hatoya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4031666%2F52b2ceb6-76f1-4858-944c-1f88f8c20ac0.png</url>
      <title>DEV Community: Hayato Wada</title>
      <link>https://dev.to/hatoya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hatoya"/>
    <language>en</language>
    <item>
      <title>I got tired of alt-tabbing between Claude Code sessions, so I built a floating status panel for macOS</title>
      <dc:creator>Hayato Wada</dc:creator>
      <pubDate>Thu, 16 Jul 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/hatoya/i-got-tired-of-alt-tabbing-between-claude-code-sessions-so-i-built-a-floating-status-panel-for-3ko4</link>
      <guid>https://dev.to/hatoya/i-got-tired-of-alt-tabbing-between-claude-code-sessions-so-i-built-a-floating-status-panel-for-3ko4</guid>
      <description>&lt;p&gt;If you run more than one Claude Code session at a time, you probably know this failure mode: a session hits a permission prompt and just... sits there. Silently. While you're heads-down in another window, one of your agents has been waiting ten minutes for you to answer "can I run this command?"&lt;/p&gt;

&lt;p&gt;I kept doing this to myself. I'd kick off three sessions across different projects, get absorbed in reviewing one of them, and come back to find another had stalled on a prompt almost immediately after I looked away. The whole point of running sessions in parallel is throughput, and a stalled session is zero throughput that &lt;em&gt;looks&lt;/em&gt; like progress until you check.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/hatoya/ccglance" rel="noopener noreferrer"&gt;ccglance&lt;/a&gt; — a small always-on-top floating panel for macOS that shows every running Claude Code session at a glance. This post is about what it does, how it works under the hood (the architecture is almost embarrassingly simple), and the slightly meta experience of building it almost entirely with Claude Code while it watched its own development sessions.&lt;/p&gt;

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

&lt;p&gt;ccglance is a translucent HUD-style panel that floats above everything else. You park it in a corner — a secondary display works great — and it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every session, grouped by project.&lt;/strong&gt; No more remembering which terminal tab belongs to which repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working sessions&lt;/strong&gt; show what they're currently doing, with elapsed time. If a session has been "running a build" for eight minutes, you can tell at a glance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sessions waiting for your permission pulse yellow.&lt;/strong&gt; This is the whole reason the app exists. The pulse is impossible to miss in your peripheral vision, which is exactly where a status panel should live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents show up indented under their parent session&lt;/strong&gt;, so a session that fans out into parallel workers reads as a little tree instead of a mystery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idle sessions show the PR's CI/review status&lt;/strong&gt; for the session's branch — open, draft, merged, closed — so a "done" session tells you what to do next instead of just going quiet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmcqui0bs5u2h01rhisc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmcqui0bs5u2h01rhisc.gif" alt="ccglance demo" width="759" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. It's not a dashboard, it doesn't have settings pages, it doesn't want your attention. It's a glanceable answer to one question: &lt;em&gt;do any of my sessions need me right now?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;This is my favorite part, because the architecture is three arrows and no boxes worth drawing.&lt;/p&gt;

&lt;p&gt;Claude Code has &lt;a href="https://docs.claude.com/en/docs/claude-code/hooks" rel="noopener noreferrer"&gt;lifecycle hooks&lt;/a&gt; — &lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;UserPromptSubmit&lt;/code&gt;, &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;Notification&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;, &lt;code&gt;SessionEnd&lt;/code&gt;. You register a command for each event, and Claude Code invokes it with a JSON payload on stdin whenever the event fires.&lt;/p&gt;

&lt;p&gt;ccglance registers one tiny, dependency-free Node script for those events. The script reads the event from stdin and writes one JSON file per session to &lt;code&gt;~/.claude/ccglance/sessions/&amp;lt;session_id&amp;gt;.json&lt;/code&gt; — current state, tool being used, working directory, timestamps.&lt;/p&gt;

&lt;p&gt;The app side is a native Swift/AppKit program that polls that directory every 0.5 seconds and renders whatever it finds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Code hooks → node script (stdin → JSON file) → AppKit app (polls every 0.5s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No server. No sockets. No IPC. The two halves don't know about each other — they just share a directory. If the app crashes, hooks keep writing files and nothing breaks. If a hook fails, the app just renders slightly stale state. And when something looks wrong, the entire debugging story is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.claude/ccglance/sessions/&lt;span class="k"&gt;*&lt;/span&gt;.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I went back and forth on whether polling was too crude — file-system events (FSEvents) would be the "proper" way. But at a 0.5s interval on a handful of tiny JSON files, the polling cost is unmeasurable, and the code is dramatically simpler. Stale files from crashed sessions get cleaned up after 12 hours of no updates. Boring technology, chosen on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design constraints I refused to compromise on
&lt;/h2&gt;

&lt;p&gt;A status panel is an always-running utility, which puts it in a category of software I hold to a different standard than apps I open on demand. Three constraints shaped everything:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Electron.&lt;/strong&gt; Nothing against Electron for the right job, but I couldn't justify a Chromium instance for a panel that renders a list of session rows. ccglance is plain Swift/AppKit. There isn't even an Xcode project — the build is a shell script that calls &lt;code&gt;swiftc&lt;/code&gt; directly, with zero external packages. The whole app compiles in seconds and the binary is tiny. If you've never built a Mac app without Xcode, I recommend trying it once; it's refreshing how little ceremony you actually need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never steal focus.&lt;/strong&gt; This one is non-negotiable for a panel that floats over your work. The panel is a &lt;em&gt;non-activating&lt;/em&gt; &lt;code&gt;NSPanel&lt;/code&gt; — you can click it, drag it around, use its right-click menu, and the app you were typing in keeps keyboard focus the entire time. A status utility that yanks focus when you interact with it is worse than no utility at all, because it interrupts exactly the flow it was supposed to protect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero dependencies, everywhere.&lt;/strong&gt; The hook script is dependency-free CommonJS — no &lt;code&gt;npm install&lt;/code&gt;, no &lt;code&gt;node_modules&lt;/code&gt;, nothing to break when Node versions drift. The Swift side has no package dependencies. The fewer moving parts an always-on tool has, the fewer ways it can rot on someone's machine two years from now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meta part: it was built by the thing it monitors
&lt;/h2&gt;

&lt;p&gt;Here's the bit I find genuinely funny: ccglance was built almost entirely &lt;em&gt;with&lt;/em&gt; Claude Code. The Swift app, the hook scripts, the release CI (Developer ID signing and notarization on GitHub Actions), the Homebrew tap automation, the README, even the demo GIF pipeline — all of it came out of Claude Code sessions.&lt;/p&gt;

&lt;p&gt;Which means that for most of its development, ccglance was sitting in the corner of my screen monitoring the very sessions that were building it. When a session paused on a permission prompt while implementing the yellow permission-pulse animation, the panel pulsed yellow to tell me about it. The tool debugged its own reason for existing in real time.&lt;/p&gt;

&lt;p&gt;Beyond the novelty, one practical lesson from this project stuck with me: &lt;strong&gt;put your invariants in CLAUDE.md, not in your prompts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the project-level instruction file Claude Code reads at the start of every session. Early on I added this line (translated from Japanese):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The panel must never steal focus from the app you're working in — don't break this property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single sentence did a surprising amount of work. Across dozens of sessions — many of them touching window code with no prompt from me mentioning focus at all — Claude consistently reached for non-activating &lt;code&gt;NSPanel&lt;/code&gt; APIs and avoided anything that would activate the app. I never had to re-explain the constraint, and I never caught a regression against it.&lt;/p&gt;

&lt;p&gt;The general pattern: things you'd normally enforce through code review ("we don't do X in this codebase") work better as standing instructions than as per-task reminders. Prompts are ephemeral; the instruction file is institutional memory. My &lt;code&gt;CLAUDE.md&lt;/code&gt; also encodes the build verification step, hook-testing procedure, and release rules, so every session starts already knowing how this project operates.&lt;/p&gt;

&lt;p&gt;Did I write any code by hand? Barely. My job was closer to a picky reviewer with strong opinions about focus behavior and CPU usage. For a small, well-scoped utility like this, that division of labor worked better than I expected going in — the constraints were mine, the keystrokes mostly weren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;If you're on an Apple Silicon Mac running macOS 12+:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; hatoya/tap/ccglance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or grab the zip from the &lt;a href="https://github.com/hatoya/ccglance/releases/latest" rel="noopener noreferrer"&gt;releases page&lt;/a&gt;. Releases are Developer ID signed and notarized, so Gatekeeper won't complain. Intel Macs aren't covered by the prebuilt binaries, but building from source is a &lt;code&gt;git clone&lt;/code&gt; and one &lt;code&gt;./build.sh&lt;/code&gt; away — Xcode Command Line Tools are the only requirement.&lt;/p&gt;

&lt;p&gt;On first launch the app wires up the Claude Code hooks automatically (it appends to &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, leaves your existing hooks untouched, and saves a backup). Start a new Claude Code session and the panel picks it up. Uninstalling removes only ccglance's hooks — nothing else in your settings is touched.&lt;/p&gt;

&lt;p&gt;It's free and MIT licensed: &lt;strong&gt;&lt;a href="https://github.com/hatoya/ccglance" rel="noopener noreferrer"&gt;https://github.com/hatoya/ccglance&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Current version is v1.6.0. It's an unofficial side project, not affiliated with Anthropic in any way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;There's a growing category of tooling problems created by agentic coding itself — not "how do I make the agent smarter" but "how do I, the human, supervise several of them without losing my mind." ccglance is my answer to one narrow slice of that: knowing when an agent needs me, without polling terminal tabs by hand.&lt;/p&gt;

&lt;p&gt;If you run parallel Claude Code sessions, give it a try. And if you run &lt;em&gt;more&lt;/em&gt; parallel sessions than I do, I'd especially love to hear how it holds up — issues and PRs welcome on &lt;a href="https://github.com/hatoya/ccglance" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
