<?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: 성현</title>
    <description>The latest articles on DEV Community by 성현 (@s3onghyun).</description>
    <link>https://dev.to/s3onghyun</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%2F4184088%2F9037af0e-63e7-42dd-9da5-9f7e28b41c3f.jpg</url>
      <title>DEV Community: 성현</title>
      <link>https://dev.to/s3onghyun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/s3onghyun"/>
    <language>en</language>
    <item>
      <title>I can barely use Vim, so I built a Vim trainer with AI. Then I failed my own level.</title>
      <dc:creator>성현</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:39:45 +0000</pubDate>
      <link>https://dev.to/s3onghyun/i-can-barely-use-vim-so-i-built-a-vim-trainer-with-ai-then-i-failed-my-own-level-4aid</link>
      <guid>https://dev.to/s3onghyun/i-can-barely-use-vim-so-i-built-a-vim-trainer-with-ai-then-i-failed-my-own-level-4aid</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR: A DevOps engineer who can barely use Vim built a Vim/tmux trainer with Claude Code. Then he couldn't solve his own level. The builder learned Vim before the site launched.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why: I open a terminal more than an IDE now
&lt;/h2&gt;

&lt;p&gt;The way I work changed. Claude Code and Codex run in a terminal, a few tmux panes show the output, and I watch.&lt;/p&gt;

&lt;p&gt;But there's still a moment where &lt;strong&gt;a human touches up what the agent wrote&lt;/strong&gt;. That happens in Vim/Neovim inside the terminal. And I'm not good at Vim. I know &lt;code&gt;:wq&lt;/code&gt;. That's roughly it.&lt;/p&gt;

&lt;p&gt;Around then I heard DHH say he just builds whatever he needs. Fair enough: if there's no good place to practice, build one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal: that LearnGitBranching feeling
&lt;/h2&gt;

&lt;p&gt;If you've played &lt;a href="https://learngitbranching.js.org/" rel="noopener noreferrer"&gt;LearnGitBranching&lt;/a&gt;, you know how addictive it is: &lt;strong&gt;you see the goal state and close the gap with your own hands.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So nvim-edu looks like this:&lt;/p&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%2F0jd841zyw4hex2ukym1m.png" 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%2F0jd841zyw4hex2ukym1m.png" alt="Boss level: apply an AI code review" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your buffer on the left, the goal on the right. &lt;strong&gt;Red lines&lt;/strong&gt; still differ&lt;/li&gt;
&lt;li&gt;Beat the par keystroke count for ★★★&lt;/li&gt;
&lt;li&gt;41 levels: motions → editing → text objects → visual &amp;amp; macros → &lt;strong&gt;real world: cleaning up AI code&lt;/strong&gt; → &lt;strong&gt;tmux&lt;/strong&gt; → &lt;strong&gt;guru combos&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Drills, a daily challenge (same tasks for everyone that day, with a 🔥 streak) and free practice&lt;/li&gt;
&lt;li&gt;Deliberately Neovim-looking: neo-tree sidebar, lualine, &lt;code&gt;:colo tokyonight&lt;/code&gt; and friends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real-world boss reads like this: "Review comments: ① remove the DEBUG prints ② rename &lt;code&gt;tot&lt;/code&gt; → &lt;code&gt;total&lt;/code&gt;." Exactly the moment where a human cleans up agent code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tmux chapter: only the keys humans still type
&lt;/h2&gt;

&lt;p&gt;tmux has hundreds of bindings, but while an agent runs, a human presses only a handful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create, attach, detach sessions (&lt;code&gt;tmux new -s agent&lt;/code&gt;, &lt;code&gt;C-b d&lt;/code&gt;, &lt;code&gt;tmux a&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Split, move, zoom panes (&lt;code&gt;C-b %&lt;/code&gt;, &lt;code&gt;C-b o&lt;/code&gt;, &lt;code&gt;C-b z&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy mode to scroll back and copy an error&lt;/strong&gt; (the one I use most when agent logs get long)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole chapter. tmux can't run in a browser, so there's a small simulator with a session/window/pane tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident 1: the builder failed his own level
&lt;/h2&gt;

&lt;p&gt;With the levels done, I was asked to try one by hand. The par solution on screen: &lt;code&gt;3lxfmx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Me: "I don't get 3lxfmx."&lt;/p&gt;

&lt;p&gt;I was pressing 3 over and over. 😇 Claude broke it down:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keys&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;move right 3&lt;/td&gt;
&lt;td&gt;cursor on the 4th character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;delete under cursor&lt;/td&gt;
&lt;td&gt;typo gone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;find the next &lt;code&gt;m&lt;/code&gt; on this line&lt;/td&gt;
&lt;td&gt;cursor on &lt;code&gt;m&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;done&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Oh, it works."&lt;/p&gt;

&lt;p&gt;Since then Claude has a standing rule for me: always explain keys as &lt;strong&gt;keys → meaning → result&lt;/strong&gt;. The builder was the first user, and the first to fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident 2: typing &lt;code&gt;jjl&lt;/code&gt; produced &lt;code&gt;ㅓㅓㅣ&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Every Korean (or Japanese, or Chinese) Vim user knows this one. Leave your input method on Korean, press &lt;code&gt;jjl&lt;/code&gt; in NORMAL mode, and instead of moving down, down, right you get &lt;code&gt;ㅓㅓㅣ&lt;/code&gt; on screen. That happened to me.&lt;/p&gt;

&lt;p&gt;So in NORMAL mode, if a non-Latin IME is composing, nvim-edu blocks the text from entering the buffer and tells you to switch to English input. Early levels also block arrow keys and the mouse to build hjkl habits. (After I asked "wait, do arrow keys not work in real nvim?", arrows came back from chapter 2 on.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it behave like real Neovim?
&lt;/h2&gt;

&lt;p&gt;For speed, levels run on a browser Vim emulator (&lt;code&gt;@replit/codemirror-vim&lt;/code&gt;). The worry: if the emulator is wrong, the site teaches wrong habits.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;real Neovim is the answer key&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same keys go into &lt;code&gt;nvim --headless&lt;/code&gt; and into the site's editor; text, cursor and mode are compared&lt;/li&gt;
&lt;li&gt;It started at &lt;strong&gt;130/169 matching&lt;/strong&gt;: search always ignored case, &lt;code&gt;dG&lt;/code&gt; left an empty line, &lt;code&gt;Ctrl-a&lt;/code&gt; read &lt;code&gt;007&lt;/code&gt; as octal…&lt;/li&gt;
&lt;li&gt;After fixing the emulator: &lt;strong&gt;180/180&lt;/strong&gt;. CI compares against Neovim 0.11.0 on every push&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still skeptical? There's a &lt;strong&gt;Real Vim mode (beta)&lt;/strong&gt;: actual Vim 8.2 running in the browser via &lt;a href="https://github.com/rhysd/vim.wasm" rel="noopener noreferrer"&gt;vim.wasm&lt;/a&gt;, solving the same levels.&lt;/p&gt;

&lt;p&gt;…except in Safari. 😅 GitHub Pages can't send the required isolation headers, so a service worker adds them, and Safari doesn't like that.&lt;/p&gt;

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

&lt;p&gt;No install, no config. English and Korean.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://s3onghyun.github.io/nvim-edu/" rel="noopener noreferrer"&gt;https://s3onghyun.github.io/nvim-edu/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Needs a physical keyboard (no touch yet)&lt;/li&gt;
&lt;li&gt;Progress lives only in your browser's localStorage&lt;/li&gt;
&lt;li&gt;MIT open source: &lt;a href="https://github.com/s3onghyun/nvim-edu" rel="noopener noreferrer"&gt;https://github.com/s3onghyun/nvim-edu&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A level is one object in &lt;code&gt;src/levels.js&lt;/code&gt;, and &lt;code&gt;npm test&lt;/code&gt; checks that the par solution really clears it, both in the emulator and in real Vim. Got a situation you wish were a level? Issues and PRs welcome.&lt;/p&gt;

&lt;p&gt;Plenty of you already live in Vim. This is for anyone who, like me, is still stuck at &lt;code&gt;:wq&lt;/code&gt;. 🙂&lt;/p&gt;

</description>
      <category>vim</category>
      <category>neovim</category>
      <category>tmux</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I put a night-watch soldier on my Mac for AI agents. 70 green tests, 3 real-world incidents.</title>
      <dc:creator>성현</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:18:38 +0000</pubDate>
      <link>https://dev.to/s3onghyun/i-put-a-night-watch-soldier-on-my-mac-for-ai-agents-70-green-tests-3-real-world-incidents-34in</link>
      <guid>https://dev.to/s3onghyun/i-put-a-night-watch-soldier-on-my-mac-for-ai-agents-70-green-tests-3-real-world-incidents-34in</guid>
      <description>&lt;p&gt;In the Korean army, &lt;em&gt;bulchimbeon&lt;/em&gt; (불침번) is the night-watch shift: one soldier stays awake all night so everyone else can sleep. Fall asleep on watch and you're in trouble.&lt;/p&gt;

&lt;p&gt;My Mac had the opposite problem. It slept &lt;strong&gt;too well&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'd tell Claude Code "go refactor this" and go to bed. In the morning, one of three things had happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Mac had gone to sleep and the agent was frozen halfway 😇&lt;/li&gt;
&lt;li&gt;I'd left &lt;code&gt;caffeinate -d&lt;/code&gt; running and the screen had been on all night&lt;/li&gt;
&lt;li&gt;Worst case: I'd forgotten to undo &lt;code&gt;sudo pmset disablesleep 1&lt;/code&gt; and the battery was at 0%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built my Mac a night-watch soldier. A pixel-art private patrols under the menu bar with a flashlight while an agent works, then salutes "Shift over! 🫡" and lets the Mac sleep.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  How do you know an agent is "working"?
&lt;/h2&gt;

&lt;p&gt;Version one was simple: if a Claude Code process exists, stay on duty.&lt;/p&gt;

&lt;p&gt;Result: &lt;strong&gt;the shift never ended.&lt;/strong&gt; I keep several Claude Code sessions open at all times. Counting open sessions meant the private would stand watch until discharge.&lt;/p&gt;

&lt;p&gt;What I ended up with:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The agent has a &lt;code&gt;caffeinate&lt;/code&gt; child process&lt;/td&gt;
&lt;td&gt;Claude Code spawns &lt;code&gt;caffeinate -i -t 300&lt;/code&gt; itself, only while it's working&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Its process tree uses ≥ 2% CPU averaged over 60 s&lt;/td&gt;
&lt;td&gt;Catches tool runs like builds and tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headless sessions launched with &lt;code&gt;--tools=&lt;/code&gt; are ignored&lt;/td&gt;
&lt;td&gt;Plugins like claude-mem keep helper sessions running&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I started with a 5-second CPU sample. A file watcher the agent had started would nibble a little CPU now and then, and the shift &lt;strong&gt;didn't end for 18 minutes&lt;/strong&gt;. Switching to a 60-second average fixed it. The private wasn't slacking; my measurement was.&lt;/p&gt;

&lt;p&gt;The duty logic lives in a pure state machine (&lt;code&gt;DutyEngine&lt;/code&gt;) with no system calls. With a fake clock I wrote 70 scenario tests: "quiet for 5 minutes → hand over", "battery under 20% → stand down", "overheating → rest, then resume".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All green.&lt;/strong&gt; Deployed with full confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident 1: the watchdog went home with its officer
&lt;/h2&gt;

&lt;p&gt;Staying awake with the lid closed needs &lt;code&gt;pmset -a disablesleep 1&lt;/code&gt;. The catch: if the app gets force-quit, that setting &lt;strong&gt;stays on&lt;/strong&gt;. Next stop, a hot Mac in a backpack.&lt;/p&gt;

&lt;p&gt;So when enabling it with admin rights, I also launched a watchdog script with &lt;code&gt;nohup&lt;/code&gt;. If the app dies, the watchdog restores the original setting.&lt;/p&gt;

&lt;p&gt;Measured on a real machine: &lt;strong&gt;the watchdog vanished the moment the admin shell exited.&lt;/strong&gt; The soldier posted on watch clocked out with his officer.&lt;/p&gt;

&lt;p&gt;It looked like &lt;code&gt;osascript&lt;/code&gt;'s &lt;code&gt;do shell script ... with administrator privileges&lt;/code&gt; cleans up child processes in the same session when it finishes. &lt;code&gt;nohup&lt;/code&gt; only ignores SIGHUP, so it didn't survive.&lt;/p&gt;

&lt;p&gt;Fix: fork via &lt;code&gt;/usr/bin/perl&lt;/code&gt; and call &lt;code&gt;setsid()&lt;/code&gt; to fully detach into a new session. That caused incident 1b: &lt;code&gt;A &amp;amp;&amp;amp; B &amp;amp;&lt;/code&gt; left a subshell behind and &lt;strong&gt;osascript never returned&lt;/strong&gt;. The final version synchronizes over a pipe: the parent exits only after the child reports "detached".&lt;/p&gt;

&lt;p&gt;Final measurement: even after &lt;code&gt;kill -9&lt;/code&gt; on the app, the sleep setting is restored &lt;strong&gt;within 3 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident 2: the shift evaporated on restart
&lt;/h2&gt;

&lt;p&gt;Feature: remaining watch time survives app restarts and updates. Tests passed.&lt;/p&gt;

&lt;p&gt;Reality: restart, and the watch was gone.&lt;/p&gt;

&lt;p&gt;The cause was humbling. &lt;code&gt;init&lt;/code&gt; called &lt;code&gt;persist()&lt;/code&gt; &lt;strong&gt;first&lt;/strong&gt;, overwriting the saved watch with an empty state, and only then ran &lt;code&gt;resume()&lt;/code&gt;. Like shredding the handover log before the next soldier could read it.&lt;/p&gt;

&lt;p&gt;The tests covered the engine, not the app shell's initialization order. One line in the wrong place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incident 3: no report ever arrived
&lt;/h2&gt;

&lt;p&gt;Shift changes, heat advisories, a handover report when you come back from being away: I built lots of notifications.&lt;/p&gt;

&lt;p&gt;Reality: &lt;strong&gt;not a single notification, ever.&lt;/strong&gt; No errors either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNErrorDomain Code=1 "Notifications are not allowed for this application"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;macOS &lt;strong&gt;doesn't grant notification permission at all&lt;/strong&gt; to ad-hoc signed apps without a Developer ID (Team ID). And my permission request looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;{ _, _ in }&lt;/code&gt;. Elegantly ignoring failure. The private had been writing reports and filing them straight into the trash.&lt;/p&gt;

&lt;p&gt;Now, without permission, it falls back to &lt;code&gt;osascript&lt;/code&gt;'s &lt;code&gt;display notification&lt;/code&gt;. The text is passed &lt;strong&gt;as argv&lt;/strong&gt; rather than spliced into the script string, so there's no AppleScript injection surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure logic tests only verify what you intend to do.&lt;/strong&gt; Whether the OS actually lets you do it takes a real machine. None of the three incidents could have been caught by unit tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callbacks that swallow failure (&lt;code&gt;{ _, _ in }&lt;/code&gt;) are the quietest, most dangerous bugs&lt;/strong&gt;, whether an AI or a human wrote them.&lt;/li&gt;
&lt;li&gt;Building this with Claude Code, the most useful habit was asking "did you actually verify that?" over and over. One real measurement beat a wall of green tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the private stands today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;On duty &lt;strong&gt;only while&lt;/strong&gt; Claude Code, Codex, Gemini CLI, Cursor Agent, Copilot CLI, Aider or OpenCode are actually working&lt;/li&gt;
&lt;li&gt;Lid-closed mode, with a watchdog that restores your sleep setting even if the app dies&lt;/li&gt;
&lt;li&gt;🥵 Rests when the Mac overheats and resumes once it cools; stands down below 20% battery&lt;/li&gt;
&lt;li&gt;Back after 10+ minutes away? "Handover report, sir! 📋"&lt;/li&gt;
&lt;li&gt;Installs no hooks, daemons or root helpers. Works on macOS 13+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're on macOS 26.4+ and want hook-level precision, &lt;a href="https://github.com/kageroumado/adrafinil" rel="noopener noreferrer"&gt;Adrafinil&lt;/a&gt; is a great alternative; the README has an honest comparison.&lt;/p&gt;

&lt;p&gt;It's MIT open source. Ideas for the private's lines or sprites are welcome. He hasn't passed his promotion review yet (Apple notarization), so the first launch needs "Open Anyway" once.&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; s3onghyun/tap/bulchimbeon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;a href="https://github.com/s3onghyun/bulchimbeon" rel="noopener noreferrer"&gt;https://github.com/s3onghyun/bulchimbeon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;End of watch report. 🫡&lt;br&gt;
감사합니다.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>swift</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
