<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mike Olson - Blog Posts</title><description>Blog posts from Mike Olson</description><link>https://mwolson.org/</link><atom:link href="https://mwolson.org/blog/rss.xml" rel="self" type="application/rss+xml" xmlns:atom="http://www.w3.org/2005/Atom"/><language>en-us</language><ttl>60</ttl><lastBuildDate>Tue, 17 Feb 2026 00:00:00 GMT</lastBuildDate><image><url>https://mwolson.org/apple-touch-icon.png</url><title>Mike Olson - Blog Posts</title><link>https://mwolson.org/</link></image><item><title>Fixing HDMI Audio After Suspend on Linux</title><link>https://mwolson.org/blog/2026-02-17-fixing-hdmi-audio-after-suspend-on-linux/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-02-17-fixing-hdmi-audio-after-suspend-on-linux/</guid><description>A small systemd service that restores HDMI audio after suspend/resume on PipeWire systems.</description><pubDate>Tue, 17 Feb 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-problem&quot;&gt;The Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-kernel-fixes-havent-stuck&quot;&gt;Why Kernel Fixes Haven’t Stuck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-other-power-problem-idle-suspend&quot;&gt;The Other Power Problem: Idle Suspend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-workaround-profile-cycling&quot;&gt;The Workaround: Profile Cycling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#automating-it&quot;&gt;Automating It&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#installation&quot;&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#options&quot;&gt;Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#links&quot;&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;If you use HDMI or DisplayPort audio on a Linux desktop, you’ve probably hit
this: you suspend your machine, wake it up, and your audio is gone. The HDMI
output shows up as disconnected or silent. The most reliable fix is to open your
audio settings and manually toggle the card profile off and back on.&lt;/p&gt;
&lt;p&gt;This has been a persistent issue for years. It affects AMD and Intel GPUs across
multiple kernel versions and desktop environments. The root cause is that when
the system resumes, the kernel re-initializes the GPU but the HDMI audio codec
doesn’t always come back cleanly. WirePlumber ends up holding references to
stale audio nodes that no longer work.&lt;/p&gt;
&lt;h2&gt;Why Kernel Fixes Haven’t Stuck&lt;/h2&gt;
&lt;p&gt;There have been multiple attempts to fix this at the kernel level. In Linux
6.11, a patch was merged that
&lt;a href=&quot;https://www.phoronix.com/news/AMD-HDMI-Audio-Fix-Linux-6.11&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;conditionally disabled ALSA snooping for AMD HDMI&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
hardware when IOMMU wasn’t in play. The idea was to work around DMA coherency
issues that caused crackling, dropouts, and post-suspend silence.&lt;/p&gt;
&lt;p&gt;That patch was
&lt;a href=&quot;https://www.mail-archive.com/amd-gfx@lists.freedesktop.org/msg128128.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;reverted&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
because it broke audio on other hardware configurations. The same pattern has
repeated several times: a fix goes in, it helps some systems, it causes
regressions on others, and it gets pulled. The Arch Linux forums have extensive
threads documenting this cycle, including
&lt;a href=&quot;https://bbs.archlinux.org/viewtopic.php?id=306343&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this one on ELD info being empty after resume&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The fundamental issue is that HDMI audio depends on a coordination chain between
the GPU driver, the HDA controller, the ALSA layer, and the user-space audio
server. Suspend/resume interrupts that chain, and rebuilding it reliably across
all hardware combinations has proven difficult.&lt;/p&gt;
&lt;h2&gt;The Other Power Problem: Idle Suspend&lt;/h2&gt;
&lt;p&gt;System suspend isn’t the only power-related audio issue on Linux. PipeWire’s
WirePlumber also suspends audio nodes after a few seconds of silence to save
power. On paper this saves about 100mW on mobile devices, but on desktops it
causes two problems: an audible pop when audio starts, and a constant hum on
connected amplifiers because the unpowered output leaves the line floating,
picking up electrical noise.&lt;/p&gt;
&lt;p&gt;A
&lt;a href=&quot;https://www.reddit.com/r/linux/comments/1pn41yb/quick_tip_how_to_disable_audio_suspend_in_pipewire/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;popular r/linux thread&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
in December 2025 highlighted how frustrating this is. The configuration has
changed across PulseAudio, WirePlumber 0.4 (Lua), and WirePlumber 0.5+ (SPA
JSON), so most solutions you find online are outdated. The current fix is a
WirePlumber config drop-in:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# ~/.config/wireplumber/wireplumber.conf.d/50-no-suspend.conf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;monitor.alsa.rules&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;    matches&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;        node.name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;~alsa_output.*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;      update-props&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#1E66F5&quot;&gt;        session.suspend-timeout-seconds&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; 86400&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One interesting detail from that thread: setting the timeout to 0 disables
suspend entirely, but this breaks audio device sharing on multi-user systems.
Setting it to a high value like 86400 (one day) avoids both the pop/hum and the
multi-user issue.&lt;/p&gt;
&lt;p&gt;For HDMI specifically, I hit both problems: the idle suspend pop between tracks,
and complete audio loss after system suspend. The WirePlumber config above
handles the idle suspend pop; audio-profile-manager handles the post-suspend
audio loss.&lt;/p&gt;
&lt;h2&gt;The Workaround: Profile Cycling&lt;/h2&gt;
&lt;p&gt;The manual fix that always works is cycling the PipeWire/PulseAudio card profile
off and back on. This forces a full teardown of the audio nodes and rebuilds
them from scratch against the current hardware state:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;pactl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; set-card-profile&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; alsa_card.pci-0000_01_00.1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; off&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;sleep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;pactl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; set-card-profile&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; alsa_card.pci-0000_01_00.1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; pro-audio&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The specific profile name varies by system — &lt;code&gt;pro-audio&lt;/code&gt; is common for HDMI
cards on PipeWire, but yours might be different.&lt;/p&gt;
&lt;h2&gt;Automating It&lt;/h2&gt;
&lt;p&gt;I wrote
&lt;a href=&quot;https://github.com/mwolson/audio-profile-manager&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;audio-profile-manager&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; to
automate this. It’s a bash script that monitors D-Bus for systemd-logind’s
&lt;code&gt;PrepareForSleep&lt;/code&gt; signal and runs the profile cycle automatically on wake.&lt;/p&gt;
&lt;p&gt;The core of it is a &lt;code&gt;dbus-monitor&lt;/code&gt; loop that watches for resume events:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;dbus-monitor&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --system&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --monitor&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;interface=org.freedesktop.login1.Manager,member=PrepareForSleep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;  |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; while&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; line&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;    # On wake (boolean false), wait for HDMI renegotiation, then cycle profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On startup it auto-detects both the HDMI audio card and the active profile, so
it knows what to restore to after cycling. Both can be overridden with &lt;code&gt;--card&lt;/code&gt;
and &lt;code&gt;--profile&lt;/code&gt; if needed. Then it sits in the background waiting for
suspend/resume events.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;The repo includes a systemd user service and an install script:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; https://github.com/mwolson/audio-profile-manager.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; audio-profile-manager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;./install.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;systemctl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --user&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; audio-profile-manager.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This installs the script to &lt;code&gt;~/.local/bin/&lt;/code&gt; and enables the systemd user service
(the install script handles &lt;code&gt;systemctl --user enable&lt;/code&gt; for you). It uses
&lt;code&gt;graphical-session.target&lt;/code&gt;, which is desktop-agnostic — it works with KDE,
GNOME, COSMIC, and any other desktop that properly integrates with systemd.&lt;/p&gt;
&lt;p&gt;You can verify it’s working by checking the journal before and after a suspend
cycle:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;journalctl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --user&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; audio-profile-manager.service&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see log lines like:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Waking from sleep, waiting 3s for HDMI to renegotiate...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cycling profile on alsa_card.pci-0000_01_00.1: pro-audio -&amp;gt; off -&amp;gt; pro-audio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Profile restored to pro-audio.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Options&lt;/h2&gt;
&lt;p&gt;The defaults work for most HDMI setups, but you can customize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--card CARD_NAME&lt;/code&gt; to target a specific audio card&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--profile PROFILE&lt;/code&gt; to override the target profile (default: auto-detected)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--wake-delay SECONDS&lt;/code&gt; to adjust the post-wake delay (default: 3 seconds)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Find your card name with &lt;code&gt;pactl list cards short&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/audio-profile-manager&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub repository&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.phoronix.com/news/AMD-HDMI-Audio-Fix-Linux-6.11&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AMD HDMI audio fix attempted in Linux 6.11&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(Phoronix)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mail-archive.com/amd-gfx@lists.freedesktop.org/msg128128.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Kernel regression report for AMD HDMI/DP audio&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(amd-gfx mailing list)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bbs.archlinux.org/viewtopic.php?id=306343&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;No sound via HDMI after resume: ELD info empty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(Arch Linux forums)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/linux/comments/1pn41yb/quick_tip_how_to_disable_audio_suspend_in_pipewire/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Quick tip: how to disable audio suspend in PipeWire&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(r/linux)&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>linux</category><author>Mike Olson</author></item><item><title>Markdown for Agents on the Cloudflare Free Plan</title><link>https://mwolson.org/blog/2026-02-14-markdown-for-agents-on-cloudflare-free-plan/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-02-14-markdown-for-agents-on-cloudflare-free-plan/</guid><description>How to serve clean Markdown to AI agents from your origin, with caching that works on Cloudflare&apos;s free plan.</description><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;In February 2026, Cloudflare announced
&lt;a href=&quot;https://blog.cloudflare.com/markdown-for-agents/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Markdown for Agents&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, a
feature that automatically converts HTML pages to Markdown when AI agents
request it via &lt;code&gt;Accept: text/markdown&lt;/code&gt;. It’s a great idea, but it requires a Pro
plan or higher. And even if you have one, the edge conversion includes your
navigation chrome, footers, and other layout elements that aren’t useful to an
agent.&lt;/p&gt;
&lt;p&gt;If you author content in Markdown already (as most static site generators do),
you can serve curated Markdown directly from your origin and get better results.
Here’s how I set this up for my Astro site behind Cloudflare’s free plan,
including a caching trick that took some digging to figure out.&lt;/p&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-idea&quot;&gt;The Idea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#build-generating-markdown&quot;&gt;Build: Generating Markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#serve-caddy-content-negotiation&quot;&gt;Serve: Caddy Content Negotiation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cache-the-cloudflare-free-plan-problem&quot;&gt;Cache: The Cloudflare Free Plan Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-fix-transform-rules&quot;&gt;The Fix: Transform Rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#results&quot;&gt;Results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#trade-offs-vs-edge-conversion&quot;&gt;Trade-offs vs. Edge Conversion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#whats-next&quot;&gt;What’s Next&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Idea&lt;/h2&gt;
&lt;p&gt;AI coding tools like Claude Code and OpenCode already send
&lt;code&gt;Accept: text/markdown&lt;/code&gt; when fetching URLs. The
&lt;a href=&quot;https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Markdown for Agents spec&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
defines the protocol: the client sends an &lt;code&gt;Accept&lt;/code&gt; header, and the server
responds with clean Markdown plus a &lt;code&gt;Content-Signal&lt;/code&gt; header indicating how the
content may be used.&lt;/p&gt;
&lt;p&gt;Since my blog posts and guides are authored in MDX, the source Markdown is
already available at build time. Rather than converting HTML back to Markdown at
the CDN edge, I can generate purpose-built Markdown files during the Astro build
and serve them from the origin when an agent asks for them.&lt;/p&gt;
&lt;h2&gt;Build: Generating Markdown&lt;/h2&gt;
&lt;p&gt;During &lt;code&gt;astro build&lt;/code&gt;, a post-build script walks the content collections (blog
posts and guides), strips MDX-specific syntax like JSX components and import
statements, and writes clean Markdown files to a &lt;code&gt;dist-md/&lt;/code&gt; directory that
mirrors the URL structure of the HTML site.&lt;/p&gt;
&lt;p&gt;Each generated file gets proper YAML frontmatter with the title, publication
date, and description, followed by the content body. For index pages (the blog
listing, guide listing, homepage), I maintain template files with an
&lt;code&gt;&amp;lt;!-- @listing --&amp;gt;&lt;/code&gt; marker that gets replaced with a sorted, linked listing of
entries during generation.&lt;/p&gt;
&lt;p&gt;The result is two parallel directory trees in the build output:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dist/&lt;/code&gt; — the regular HTML site&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dist-md/&lt;/code&gt; — Markdown variants for agents&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Serve: Caddy Content Negotiation&lt;/h2&gt;
&lt;p&gt;Both directories are served from the same container. Caddy handles the content
negotiation with a named matcher that checks for &lt;code&gt;Accept: text/markdown&lt;/code&gt; and
verifies a Markdown file exists for the requested path:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@wantsMarkdown {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header Accept *text/markdown*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    file {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        root /srv/site/dist-md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        try_files {path}index.md {path}/index.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;handle @wantsMarkdown {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    root * /srv/site/dist-md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header Content-Type &quot;text/markdown; charset=utf-8&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header Content-Signal &quot;ai-input=yes, search=yes&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header Cache-Control &quot;public, max-age=3600, s-maxage=86400&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    header Vary Accept&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    rewrite {file_match.relative}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    file_server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no Markdown file exists for a given path, the &lt;code&gt;file&lt;/code&gt; matcher fails and the
request falls through to the default HTML handler. This means agents get
Markdown for content pages and HTML for everything else, with no 404s or broken
behavior.&lt;/p&gt;
&lt;p&gt;You can test it with curl:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# HTML (default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -sI&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; https://mwolson.org/blog/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Markdown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -sI&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -H&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Accept: text/markdown&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; https://mwolson.org/blog/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cache: The Cloudflare Free Plan Problem&lt;/h2&gt;
&lt;p&gt;This is where it gets interesting. Caddy sends &lt;code&gt;Vary: Accept&lt;/code&gt; on Markdown
responses, which should tell any standards-compliant cache to store separate
variants keyed on the &lt;code&gt;Accept&lt;/code&gt; header. Cloudflare’s free plan doesn’t do this.
It caches a single variant per URL regardless of &lt;code&gt;Vary&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The practical consequence: if an HTML response gets cached first, subsequent
Markdown requests return the cached HTML. If a Markdown response gets cached
first, regular browsers get Markdown. Neither is acceptable.&lt;/p&gt;
&lt;p&gt;My first workaround was setting &lt;code&gt;s-maxage=0&lt;/code&gt; on Markdown responses to prevent
Cloudflare from caching them. This solved the correctness issue but meant every
Markdown request hit the origin, defeating the point of having a CDN.&lt;/p&gt;
&lt;h2&gt;The Fix: Transform Rules&lt;/h2&gt;
&lt;p&gt;The solution uses Cloudflare’s
&lt;a href=&quot;https://developers.cloudflare.com/rules/transform/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Transform Rules&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, which are
available on the free plan (up to 10 rules). Transform Rules run at phase 3 in
Cloudflare’s request pipeline, well before cache lookup at phase 19.&lt;/p&gt;
&lt;p&gt;The rule appends a query parameter to requests that include
&lt;code&gt;Accept: text/markdown&lt;/code&gt;, which gives them a different cache key from regular
HTML requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rule name: Markdown for Agents cache key&lt;/li&gt;
&lt;li&gt;Filter expression:
&lt;code&gt;any(http.request.headers[&quot;accept&quot;][*] contains &quot;text/markdown&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Query rewrite (static): &lt;code&gt;_fmt=md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this rule active, a request to &lt;code&gt;/blog/&lt;/code&gt; with &lt;code&gt;Accept: text/markdown&lt;/code&gt;
becomes &lt;code&gt;/blog/?_fmt=md&lt;/code&gt; internally before cache lookup. Cloudflare now stores
two cache entries: one for the HTML variant and one for the Markdown variant.
The origin may see the &lt;code&gt;?_fmt=md&lt;/code&gt; query parameter, but Caddy matches on the
&lt;code&gt;Accept&lt;/code&gt; header as before, so no origin changes are needed.&lt;/p&gt;
&lt;p&gt;Once I confirmed this was working, I raised &lt;code&gt;s-maxage&lt;/code&gt; on Markdown responses to
match the HTML caching policy. Both variants are now edge-cached with a 1-day
TTL.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;After deploying and purging the Cloudflare cache, both variants cache
independently:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# First HTML request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;content-type: text/html; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cf-cache-status: MISS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Second HTML request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;content-type: text/html; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cf-cache-status: HIT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# First Markdown request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;content-type: text/markdown; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cf-cache-status: MISS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Second Markdown request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;content-type: text/markdown; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cf-cache-status: HIT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Markdown responses include &lt;code&gt;Content-Signal: ai-input=yes, search=yes&lt;/code&gt; as
specified by the Markdown for Agents protocol, and the content is clean Markdown
without navigation elements, script tags, or layout chrome.&lt;/p&gt;
&lt;h2&gt;Trade-offs vs. Edge Conversion&lt;/h2&gt;
&lt;p&gt;There are real trade-offs to the origin approach compared to Cloudflare’s edge
conversion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have to maintain a build step that generates the Markdown files. If your
content changes frequently or comes from a CMS, this adds complexity.&lt;/li&gt;
&lt;li&gt;You need to handle content negotiation at the origin (Caddy, NGINX, or
similar).&lt;/li&gt;
&lt;li&gt;The free plan Transform Rule trick uses one of your 10 available rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the other hand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You control exactly what the Markdown output looks like. No navigation chrome,
no footer links, no cookie banners converted to Markdown.&lt;/li&gt;
&lt;li&gt;The Markdown can include proper YAML frontmatter with structured metadata that
agents can parse.&lt;/li&gt;
&lt;li&gt;It works on any Cloudflare plan, including free.&lt;/li&gt;
&lt;li&gt;For static sites where the content is already in Markdown, the build step is
straightforward.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What’s Next&lt;/h2&gt;
&lt;p&gt;I might extract the build script into a reusable Astro integration so that other
Astro sites can add origin-level Markdown for Agents support without writing
custom build scripts. If I do, that will be the subject of a future post.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>ai</category><category>webdev</category><author>Mike Olson</author></item><item><title>First Week with Jujutsu VCS</title><link>https://mwolson.org/blog/2026-01-22-first-week-with-jujutsu-vcs/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-22-first-week-with-jujutsu-vcs/</guid><description>Configuration tips and Emacs integration for Jujutsu after a week of use.</description><pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;I’ve spent my first week using &lt;a href=&quot;https://docs.jj-vcs.dev/latest/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jujutsu&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (jj),
a Git-compatible version control system that takes a different approach to
managing commits and branches. Here are my impressions and some configuration
tips I’ve picked up along the way.&lt;/p&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#configuring-immutable-bookmarks&quot;&gt;Configuring Immutable Bookmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#emacs-integration-with-majutsu&quot;&gt;Emacs Integration with Majutsu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rebasing-stacked-branches&quot;&gt;Rebasing Stacked Branches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cleaning-up-after-rebases&quot;&gt;Cleaning Up After Rebases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#magit-and-jj---conflict-resolution&quot;&gt;Magit and jj - Conflict Resolution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#magit-and-jj----branch&quot;&gt;Magit and jj - ’@’ Branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#overall-impressions&quot;&gt;Overall Impressions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Configuring Immutable Bookmarks&lt;/h2&gt;
&lt;p&gt;One of the first things I changed was the default immutability settings.
Jujutsu’s default configuration locks down remote bookmarks (which are
“branches” in Git terms) as immutable, which prevents accidental modifications
but can be overly restrictive.&lt;/p&gt;
&lt;p&gt;I added this to &lt;code&gt;~/.config/jj/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;revset-aliases&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;immutable_heads()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;present(trunk()) | tags()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This relaxes the immutability rules to only protect the &lt;code&gt;trunk&lt;/code&gt; (the default
branch, typically &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; from the backing Git repo) and tags, giving
me more flexibility to work with feature branches that have been pushed to
remotes.&lt;/p&gt;
&lt;h2&gt;Emacs Integration with Majutsu&lt;/h2&gt;
&lt;p&gt;For Emacs integration, I’m using &lt;a href=&quot;https://github.com/0WD0/majutsu&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;majutsu&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;,
which provides a Magit-like interface for Jujutsu. I configured it to start in a
log buffer showing the branches I care about, with the ability to hit &lt;kbd&gt;
Enter&lt;/kbd&gt; to visit a commit or &lt;kbd&gt;O&lt;/kbd&gt; to start a new commit at that
point.&lt;/p&gt;
&lt;p&gt;Here’s my configuration from my
&lt;a href=&quot;https://github.com/mwolson/emacs-shared/blob/master/init/shared-init.el#L2092&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared Emacs init&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-replace-cdrs-in-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;old-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; new-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Replace cdr instances of OLD-EL with NEW-EL in ALIST.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;mapc&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;eq&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; old-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;              (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;setcdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; el new-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;symbol-value&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; Set up majutsu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;majutsu&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;  ;; Replace pop-to-buffer with switch-to-buffer for same-window behavior&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-replace-cdrs-in-alist &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;pop-to-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;switch-to-buffer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                            &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;majutsu-display-functions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt majutsu-default-display-function &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;#&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;switch-to-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #9D7CD8);--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic;--shiki-light:#8839EF;--shiki-light-font-style:inherit&quot;&gt;interactive&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;majutsu-log &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-project-root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; default-directory&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; Set up keybinds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set project-prefix-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;j&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x V j&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key customizations here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same-window display&lt;/strong&gt;: I replaced &lt;code&gt;pop-to-buffer&lt;/code&gt; with &lt;code&gt;switch-to-buffer&lt;/code&gt; so
that majutsu buffers open in the current window rather than splitting. This
matches my preferred Magit workflow.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;project-aware log&lt;/strong&gt;: The &lt;code&gt;my-majutsu-log&lt;/code&gt; function opens the log at the
detected project root, which I bind to &lt;kbd&gt;C-x V j&lt;/kbd&gt; in my global keymap,
or &lt;kbd&gt;j&lt;/kbd&gt; when visiting a project. It will also offer to initialize jj
if I haven’t done so yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Rebasing Stacked Branches&lt;/h2&gt;
&lt;p&gt;Rebasing stacked branches onto an updated main branch is quite nice with
Jujutsu:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase all commits on feature-branch that aren&apos;t already in main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -b&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; feature-branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-b&lt;/code&gt; flag specifies the branch to rebase, and &lt;code&gt;-o&lt;/code&gt; specifies the new base.
Jujutsu handles the rebase intelligently, only moving commits that aren’t
already part of main, and automatically brings over multiple bookmarks that may
be on that branch.&lt;/p&gt;
&lt;p&gt;I also have a script fragment that can update all tracked bookmarks on all Git
submodules:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# excerpt - assume $d is current submodule&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$topdir&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/.gitmodules&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; submodule.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; track&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --remote=origin&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Check for bookmark conflicts after fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;conflict&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Warning: Bookmark conflicts detected in &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Run &apos;cd &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; &amp;amp;&amp;amp; jj bookmark list -t&apos; to see details&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Use &apos;jj bookmark track &amp;lt;bookmark&amp;gt; --remote=origin&apos; to resolve&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase all tracked bookmarks (except main) onto updated main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Get list of tracked bookmarks, excluding the main branch and @origin suffixes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;^  &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    awk&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;{print $1}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    sed&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;s/:$//&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\$&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; [[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ]];&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; $other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;        # Skip if bookmark name is empty or contains @origin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;        [[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; -z&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ]]&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;        # Check if bookmark has commits not in main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --limit&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;            echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Rebasing &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; onto &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -b&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;                echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Warning: Failed to rebase &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; - may have conflicts&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;                echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Run &apos;cd &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; &amp;amp;&amp;amp; jj log -r &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;conflict()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&apos; to see conflicted commits&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;            fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;        fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;    done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cleaning Up After Rebases&lt;/h2&gt;
&lt;p&gt;One minor annoyance: after rebasing, you may end up with orphaned bookmarks in
your &lt;code&gt;jj log&lt;/code&gt; output that clutter the view. You’ll need to manually clean these
up with &lt;code&gt;jj abandon&lt;/code&gt; to keep a tidy log. It can be a bit tedious, but so far I’m
finding that it’s worth the effort to maintain a clear picture of which stacks
haven’t been merged yet.&lt;/p&gt;
&lt;h2&gt;Magit and jj - Conflict Resolution&lt;/h2&gt;
&lt;p&gt;Here’s an important gotcha I discovered: if you’re in the middle of resolving a
Jujutsu merge and you run &lt;code&gt;magit-status&lt;/code&gt;, Emacs can slow to a crawl. Magit will
show hundreds of conflicting symlinks (which is how jj represents conflicts in
the working directory), overwhelming the buffer.&lt;/p&gt;
&lt;p&gt;The best practice is to stay in Jujutsu’s tooling once you start resolving
conflicts there. Don’t switch to Magit mid-resolution.&lt;/p&gt;
&lt;p&gt;This situation commonly arises when squash-merging PRs for a branch that is
stacked on another. GitHub’s squash merge creates a new commit that doesn’t
match the commits jj knows about, which can leave your local branch in a state
where jj thinks it needs to merge.&lt;/p&gt;
&lt;p&gt;When this happens, it can save significant time to rebase using a specific
commit ID rather than a bookmark name:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Find the commit ID of main after the squash merge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;bookmar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase your bookmark starting at that specific commit onto main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;earliest-commit-id-of-bookmar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This explicitly tells jj which commit to move and where, avoiding the merge
conflicts that arise from the squash merge mismatch. I do something similar with
subset-rebases in Magit when using Git.&lt;/p&gt;
&lt;h2&gt;Magit and jj - ’@’ Branch&lt;/h2&gt;
&lt;p&gt;Using jj and then using Magit can cause Magit to be confused about what branch
it’s on. Unfortunately because of the existence of &lt;code&gt;@&lt;/code&gt; as a branch, which comes
before the typical name of the branch when showing a revision, Magit treats &lt;code&gt;@&lt;/code&gt;
as the one that’s meaningful and doesn’t show the merge/push target for the
branch I actually care about. I haven’t yet found a good solution, other than
manually specifying where to push. Maybe Magit could be advised to ignore that
branch at some point.&lt;/p&gt;
&lt;h2&gt;Overall Impressions&lt;/h2&gt;
&lt;p&gt;After a week, I’m finding Jujutsu’s model fairly intuitive once you get past the
initial learning curve, though I’m still using Git most of the time. The
automatic tracking of the working copy as a commit, the powerful revset
language, and the Git compatibility does make jj an interesting alternative to
keep learning.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>Announcing eglot-python-preset</title><link>https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/</guid><description>A new Emacs package for Python LSP support with PEP-723 scripts.</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what-eglot-python-preset-does&quot;&gt;What eglot-python-preset Does&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#basic-setup&quot;&gt;Basic Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#key-commands&quot;&gt;Key Commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#future-directions&quot;&gt;Future Directions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#links&quot;&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I’m happy to announce the release of
&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, a new
Emacs package that simplifies Python LSP configuration with Eglot. It’s now
available on MELPA.&lt;/p&gt;
&lt;p&gt;The package was born from a
&lt;a href=&quot;https://www.reddit.com/r/emacs/comments/1py6v5z/using_emacs_for_python_development_with_uv_and/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;discussion on r/emacs&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
about using Emacs for Python development with uv and basedpyright. The original
poster had a common pain point: they were writing standalone Python scripts with
&lt;a href=&quot;https://peps.python.org/pep-0723/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PEP-723&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; inline dependencies, using
&lt;code&gt;uv run script.py&lt;/code&gt; to execute them. Everything worked fine from the command
line, but their LSP (basedpyright) couldn’t resolve the imports because it had
no awareness of uv’s cached environments.&lt;/p&gt;
&lt;p&gt;This is a gap in the tooling. uv manages isolated environments for PEP-723
scripts automatically, but there was no bridge between those environments and
the editor’s language server. You’d get &lt;code&gt;reportMissingImports&lt;/code&gt; errors for
packages that were clearly installed and working at runtime.&lt;/p&gt;
&lt;h2&gt;What eglot-python-preset Does&lt;/h2&gt;
&lt;p&gt;The package handles the glue between uv, PEP-723 scripts, and your LSP server.
When you open a Python file containing PEP-723 metadata:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It detects the script metadata automatically&lt;/li&gt;
&lt;li&gt;It locates the cached uv environment for that script (or prompts you to
create one)&lt;/li&gt;
&lt;li&gt;It configures the LSP server to use that environment for type checking&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For standard Python projects with &lt;code&gt;pyproject.toml&lt;/code&gt; or &lt;code&gt;requirements.txt&lt;/code&gt;, the
package handles project root detection so Eglot starts from the right directory.&lt;/p&gt;
&lt;h2&gt;Basic Setup&lt;/h2&gt;
&lt;p&gt;Installation from MELPA:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;after eglot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt; ; or &apos;basedpyright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The package supports both &lt;a href=&quot;https://github.com/astral-sh/ty&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (Astral’s new
Rust-based type checker) and
&lt;a href=&quot;https://github.com/DetachHead/basedpyright&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;basedpyright&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;. See my
&lt;a href=&quot;https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/&quot;&gt;earlier post on ty&lt;/a&gt;
for why you might want to try ty — it’s dramatically faster than the
alternatives.&lt;/p&gt;
&lt;h2&gt;Key Commands&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-sync-environment&lt;/code&gt;&lt;/strong&gt; - Sync dependencies for a
PEP-723 script using &lt;code&gt;uv sync --script&lt;/code&gt;, then restart Eglot. Use this after
adding or modifying dependencies in your script’s metadata block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-remove-environment&lt;/code&gt;&lt;/strong&gt; - Remove the cached
environment to force a clean reinstall.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-run-script&lt;/code&gt;&lt;/strong&gt; - Run the current script using
&lt;code&gt;uv run&lt;/code&gt; in a compilation buffer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Future Directions&lt;/h2&gt;
&lt;p&gt;There are two potential follow-ups I’m considering:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Autodetecting tools:&lt;/strong&gt; Currently you choose between ty and basedpyright
manually. It would be useful to detect the appropriate LSP server automatically
based on project configuration or installed tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;rass support:&lt;/strong&gt; The
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; project (an LSP
multiplexer by Eglot’s author) could integrate with eglot-python-preset to run
multiple servers like ty and Ruff simultaneously. rass might eventually handle
autodetection itself, or it could be done on the elisp side. Either way, better
integration with rass may be worth exploring.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub repository&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://melpa.org/#/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MELPA package&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/&quot;&gt;ty: A Fast Python Type Checker and LSP for Emacs&lt;/a&gt;
(earlier post)&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>Managing AI Like You Manage People</title><link>https://mwolson.org/blog/2026-01-04-managing-ai-like-you-manage-people/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-04-managing-ai-like-you-manage-people/</guid><description>Lessons from people management that apply to AI agent workflows.</description><pubDate>Sun, 04 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;With the release of Claude Opus 4.5 (and the pace of improvement in frontier
models), we’ve reached the point where AI coding assistants can handle tasks
that would previously require a team of skilled developers. The capabilities are
impressive enough that drawing parallels between managing AI agents and managing
human teams feels less like a thought experiment and more like practical advice.&lt;/p&gt;
&lt;p&gt;As someone who has spent years in engineering management and who has more
recently been working extensively with AI coding tools, I’ve noticed that the
skills transfer remarkably well in both directions. Here are some patterns that
work for both.&lt;/p&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#documentation-as-team-knowledge&quot;&gt;Documentation as Team Knowledge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#continuous-improvement-through-retrospectives&quot;&gt;Continuous Improvement Through Retrospectives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#automated-guardrails-and-the-dev-loop&quot;&gt;Automated Guardrails and the Dev Loop&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-dev-loop-autonomous-iteration&quot;&gt;The Dev Loop: Autonomous Iteration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-human-parallel-ways-of-work&quot;&gt;The Human Parallel: Ways of Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-note-on-code-review-and-desk-checks&quot;&gt;A Note on Code Review and Desk Checks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#technical-specifications-and-writing-things-down&quot;&gt;Technical Specifications and Writing Things Down&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#context-management-as-cognitive-load-reduction&quot;&gt;Context Management as Cognitive Load Reduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mental-flexibility-and-decision-making&quot;&gt;Mental Flexibility and Decision-Making&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#looking-forward&quot;&gt;Looking Forward&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Documentation as Team Knowledge&lt;/h2&gt;
&lt;p&gt;The most effective engineering teams I’ve managed have had copious (but
targeted) documentation: onboarding guides, coding standards, architecture
decision records (ADRs). For AI agents, this manifests as &lt;code&gt;AGENTS.md&lt;/code&gt; files that
live in your repository.&lt;/p&gt;
&lt;p&gt;The parallels are striking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Onboarding docs&lt;/strong&gt; tell new team members how the codebase works, what
conventions to follow, and where to find things. &lt;code&gt;AGENTS.md&lt;/code&gt; serves the exact
same purpose for AI agents. Since AI agents otherwise start each coding
session with a nearly blank slate about your codebase, it can’t be overstated
how helpful this is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Style guides&lt;/strong&gt; ensure consistency across human contributors. For AI, you
specify formatting preferences, naming conventions, and patterns to follow or
avoid.&lt;/li&gt;
&lt;li&gt;What we call &lt;strong&gt;tribal knowledge&lt;/strong&gt; often lives in people’s heads and gets
shared through pairing or code review. Writing it down in &lt;code&gt;AGENTS.md&lt;/code&gt; makes it
accessible to AI agents too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Meeting notes&lt;/strong&gt; help humans have more efficient meetings with less repeated
discussion of settled topics. They’re equally valuable for AI agents,
providing clarity on what the “North Star” is for projects being worked on,
and highlighting areas of most recent concern.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just as you wouldn’t expect a new hire to be productive without proper
documentation, you can’t expect an AI agent to work effectively without clear
instructions about your codebase’s conventions and constraints.&lt;/p&gt;
&lt;h2&gt;Continuous Improvement Through Retrospectives&lt;/h2&gt;
&lt;p&gt;Good teams run retrospectives. They ask: What worked? What didn’t? What should
we change? The output often becomes updated process documentation or new team
agreements.&lt;/p&gt;
&lt;p&gt;With AI agents, the same cycle applies:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You notice the AI making a repeated mistake or missing context&lt;/li&gt;
&lt;li&gt;You update &lt;code&gt;AGENTS.md&lt;/code&gt; with clearer instructions&lt;/li&gt;
&lt;li&gt;Future sessions benefit from that improvement&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This creates a virtuous cycle where your AI instructions get better over time,
much like how team processes improve through regular retrospectives. I’ve found
myself treating &lt;code&gt;AGENTS.md&lt;/code&gt; updates as a form of “retro action item” after most
AI coding sessions, and agents are capable of enough reflection to propose those
updates themselves when asked to do so.&lt;/p&gt;
&lt;p&gt;The key insight is that both processes require &lt;strong&gt;intentional reflection&lt;/strong&gt;. You
have to actually notice what’s not working and take the time to document the fix
rather than just working around it each time.&lt;/p&gt;
&lt;h2&gt;Automated Guardrails and the Dev Loop&lt;/h2&gt;
&lt;p&gt;One of the most effective ways to improve any team’s velocity is to automate the
boring stuff: linting, formatting, type checking, test running. These tools
catch issues before code review, reducing back-and-forth and cognitive load.&lt;/p&gt;
&lt;p&gt;For AI agents, these same tools provide even more value:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto-formatting&lt;/strong&gt; (Oxfmt, Biome, Prettier) means the AI doesn’t need to
worry about code style, and neither do you when reviewing its output&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linting&lt;/strong&gt; (Oxlint, Biome, ESLint) catches common mistakes that might
otherwise require a conversation round-trip to fix&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type checking&lt;/strong&gt; ensures the AI’s code actually compiles and integrates
correctly&lt;/li&gt;
&lt;li&gt;Running &lt;strong&gt;unit tests&lt;/strong&gt; provides immediate feedback on whether changes broke
existing functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each automated check that catches an issue is one fewer back-and-forth cycle
with the AI. Since AI interactions have latency costs (and sometimes token
costs), this directly translates to faster iteration.&lt;/p&gt;
&lt;h3&gt;The Dev Loop: Autonomous Iteration&lt;/h3&gt;
&lt;p&gt;What makes these guardrails truly powerful is when they enable what’s sometimes
called the “dev loop” or “closed-loop development”: an autonomous cycle where
the AI can write code, run validation, diagnose failures, fix issues, and retry
without human intervention between iterations.&lt;/p&gt;
&lt;p&gt;Consider a typical workflow without a dev loop: you ask the AI to implement a
feature. It writes code. You run the tests. They fail. You paste the error back.
The AI proposes a fix. You run the tests again. Still failing. More
back-and-forth. Each iteration requires you to be present, running commands,
copying output, and providing feedback.&lt;/p&gt;
&lt;p&gt;With a well-configured dev loop, the AI can execute that entire cycle
autonomously. It writes code, runs &lt;code&gt;bun run check&lt;/code&gt;, sees type errors, fixes
them, runs again, sees test failures, reads the test output, adjusts the
implementation, runs once more, and finally succeeds, all without waiting for
you.&lt;/p&gt;
&lt;p&gt;This is transformative for longer tasks. An AI agent can iterate dozens of times
in the span of minutes, exploring approaches, hitting dead ends, backtracking,
and eventually converging on a solution. Without the dev loop, each of those
iterations would require your attention.&lt;/p&gt;
&lt;h3&gt;The Human Parallel: Ways of Work&lt;/h3&gt;
&lt;p&gt;Teams have their own version of this: established “ways of work” that let
engineers operate autonomously within known boundaries. A senior engineer
doesn’t need to ask permission for every decision. They know the coding
standards, understand the testing requirements, and can iterate independently
until they have something worth reviewing.&lt;/p&gt;
&lt;p&gt;Good team process creates the same kind of autonomous loop:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Engineer picks up a task&lt;/li&gt;
&lt;li&gt;They implement, test locally, and refine&lt;/li&gt;
&lt;li&gt;CI runs automated checks&lt;/li&gt;
&lt;li&gt;If checks fail, they fix and retry&lt;/li&gt;
&lt;li&gt;When everything passes, they open a PR for review&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The human isn’t blocked waiting for feedback at every step. They have the tools
and knowledge to iterate independently. The same principle applies to AI agents:
give them the guardrails and validation tools to iterate autonomously, and
they’ll be dramatically more effective.&lt;/p&gt;
&lt;p&gt;I’ve noticed that projects with strong automated guardrails tend to have much
smoother AI-assisted development. The AI can focus on the actual problem rather
than getting tripped up by style inconsistencies or type errors, and it can
self-correct when the tools tell it something’s wrong.&lt;/p&gt;
&lt;h3&gt;A Note on Code Review and Desk Checks&lt;/h3&gt;
&lt;p&gt;There’s more to say here that I may expand on in a future post, but briefly:
code review remains the final guardrail for both human and AI-generated code.
Automated checks catch many problems, but the judgment of a human (potentially
augmented by a different AI model with enough iterations) is still essential for
evaluating design decisions, identifying subtle bugs, and ensuring the code
actually solves the right problem.&lt;/p&gt;
&lt;p&gt;Similarly, the traditional “desk check” (where you manually walk through
scenarios before calling something done) has an AI parallel. Describing
integration scenarios or one-off test cases in markdown can be surprisingly
effective. Sometimes this works better than reaching for a prescriptive
automation framework like Playwright, especially for exploratory validation or
scenarios that don’t justify the overhead of formal test automation.&lt;/p&gt;
&lt;h2&gt;Technical Specifications and Writing Things Down&lt;/h2&gt;
&lt;p&gt;There’s a reason senior engineers emphasize writing technical specs before
diving into implementation. The act of writing clarifies thinking, surfaces edge
cases, and creates a shared understanding of what’s being built.&lt;/p&gt;
&lt;p&gt;I do have to caveat that there’s certainly a time and a place to do exploratory
work with minimal documentation. But if that work turns into a more established
product, then it absolutely starts to be worth the time to create documentation.&lt;/p&gt;
&lt;p&gt;This applies doubly when working with AI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API schemas&lt;/strong&gt; (OpenAPI, GraphQL schemas, TypeScript types) give the AI
precise contracts to work with&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical specs&lt;/strong&gt; help the AI understand not just what to build, but why,
and what constraints exist&lt;/li&gt;
&lt;li&gt;Having &lt;strong&gt;architecture diagrams&lt;/strong&gt; and/or explanations provides context that
might otherwise require extensive codebase exploration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I give an AI agent a well-defined spec, the results are dramatically better
than when I give vague instructions. This mirrors my experience with human
teams: clear requirements lead to better outcomes, and the discipline of writing
them down forces you to think through the problem properly.&lt;/p&gt;
&lt;p&gt;While AI certainly can reproduce some of this knowledge on demand, it’s very
important to keep in mind that usable (and useful) context sizes are currently
quite small. As such, concise and informative docs are very imporant to help
increase the amount of useful work that can be completed before running out of
context.&lt;/p&gt;
&lt;h2&gt;Context Management as Cognitive Load Reduction&lt;/h2&gt;
&lt;p&gt;Good managers protect their teams from unnecessary distractions. They filter
information, provide relevant context, and shield the team from organizational
noise that would reduce focus.&lt;/p&gt;
&lt;p&gt;With AI agents, context management is even more critical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trimming context&lt;/strong&gt; to relevant files and information improves response
quality and reduces costs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focused problem statements&lt;/strong&gt; rather than dumping entire codebases lead to
better solutions&lt;/li&gt;
&lt;li&gt;Large tasks should be &lt;strong&gt;broken into smaller chunks&lt;/strong&gt; to prevent the AI from
getting overwhelmed (and prevents you from getting overwhelmed reviewing
massive changes), and allows for better continuity when AI context limits are
reached and you have to start a new session or undergo compaction for the
current one&lt;/li&gt;
&lt;li&gt;Offloading longer &lt;code&gt;AGENTS.md&lt;/code&gt; sections into separate &lt;strong&gt;runbooks&lt;/strong&gt; can help
keep baseline context short; with an explicit instruction to consult those
runbooks when needed, the AI can still solve situational problems (like
working on a particular submodule) effectively&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just as you wouldn’t CC your entire team on every email thread, you shouldn’t
feed your AI agent every file in your repository. Curate the context
thoughtfully.&lt;/p&gt;
&lt;h2&gt;Mental Flexibility and Decision-Making&lt;/h2&gt;
&lt;p&gt;Effective managers balance openness to new information with the ability to make
decisions and move forward. They’re willing to be proven wrong but don’t
endlessly waffle. They examine data, consider options, and then commit to a
direction.&lt;/p&gt;
&lt;p&gt;Working with AI requires the same disposition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Be exploratory&lt;/strong&gt;: The AI might suggest approaches you hadn’t considered.
Sometimes they’re better than your original plan.&lt;/li&gt;
&lt;li&gt;Be willing to &lt;strong&gt;be proven wrong&lt;/strong&gt;: Your initial assumptions about the problem
might be incorrect. Let the AI’s analysis (especially when validated with
follow-up questions) inform your understanding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;But still make calls&lt;/strong&gt;: At some point you need to decide whether to accept
the AI’s suggestion, modify it, or reject it. Don’t get stuck in endless
iteration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As they used to say at one of my previous jobs, the sweet spot is confident
humility: strong opinions, weakly held.&lt;/p&gt;
&lt;h2&gt;Looking Forward&lt;/h2&gt;
&lt;p&gt;We’re at an interesting inflection point. The management practices that work
well for human teams are proving to be directly applicable to AI workflows. This
suggests that as AI capabilities continue to improve, the skills gap between
“good at managing people” and “good at working with AI” will continue to narrow.&lt;/p&gt;
&lt;p&gt;It also suggests that investing in these foundational practices pays double
dividends: they make your human team more effective today, and they prepare you
for increasingly AI-augmented workflows tomorrow.&lt;/p&gt;
&lt;p&gt;The engineers and managers who thrive will likely be those who recognize that
clear communication, good documentation, automated guardrails, and thoughtful
context management aren’t just soft skills or process overhead. They’re the
infrastructure that makes both human and artificial intelligence work better.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>ai</category><author>Mike Olson</author></item><item><title>New CachyOS Guides for Gaming and Server Setup</title><link>https://mwolson.org/blog/2026-01-03-new-cachyos-guides-for-gaming-and-server-setup/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-03-new-cachyos-guides-for-gaming-and-server-setup/</guid><description>Two new guides for CachyOS covering gaming setup and server installation.</description><pubDate>Sat, 03 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;I’ve published two new guides for &lt;a href=&quot;https://cachyos.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CachyOS&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, the
performance-focused Arch Linux distribution that I’ve been using for over a year
now on both desktop and server workloads.&lt;/p&gt;
&lt;h2&gt;CachyOS Gaming Quick Start&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://mwolson.org/guides/cachyos-gaming-quick-start/&quot;&gt;CachyOS Gaming Quick Start&lt;/a&gt; guide
covers everything you need to get a gaming-ready CachyOS installation up and
running. It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Installation and dual-boot setup&lt;/strong&gt; with Windows, including tips for avoiding
sleep issues with the Windows Fast Startup feature&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-installation configuration&lt;/strong&gt; covering the CachyOS Hello welcome app,
Ghostty as a terminal, and KDE Plasma desktop tweaks&lt;/li&gt;
&lt;li&gt;Using Paru for &lt;strong&gt;package management&lt;/strong&gt;, including how to find and install
packages from the official repos and the AUR&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gaming-specific optimizations&lt;/strong&gt; for sound latency (pipewire with
realtime-privileges), process priorities, and keyboard bindings that won’t
interrupt gameplay&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Game setup instructions&lt;/strong&gt; for WoW (via Lutris with proton-cachyos-slr) and
FFXIV (via xivlauncher-rb), including DPI scaling tips for 4k monitors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Companion app installation&lt;/strong&gt; for CurseForge, Warcraft Logs Uploader, WowUp,
and Discord&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;CachyOS Server Setup Guide&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://mwolson.org/guides/cachyos-server-setup/&quot;&gt;CachyOS Server Setup Guide&lt;/a&gt; walks through
setting up a secure CachyOS server from scratch, targeting Hetzner dedicated
servers but applicable to other VPS providers with minor adjustments. It covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Why CachyOS for servers&lt;/strong&gt; - the linux-cachyos-server kernel variant with
300Hz tickrate, no preemption, and optimized package repositories compiled for
modern CPU architectures (x86-64-v3, x86-64-v4, Zen4)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Installation from an Arch ISO&lt;/strong&gt; since CachyOS doesn’t yet have a dedicated
server edition, including both direct ISO boot and rescue system bootstrap
methods&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;btrfs with subvolumes&lt;/strong&gt; matching the CachyOS desktop installer layout
for Timeshift snapshot compatibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH key-based authentication&lt;/strong&gt; with passwords disabled for security hygiene&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network configuration&lt;/strong&gt; for both IPv4 and IPv6, with Hetzner-specific
instructions and notes for other providers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UFW firewall setup&lt;/strong&gt; for controlling incoming connections and avoiding
open-port surprises when package upgrades are done&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both guides reflect my actual configurations and workflows, and are a fairly
complete and low-friction way to get started with CachyOS in gaming and server
settings.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>cachyos</category><author>Mike Olson</author></item><item><title>ty: A Fast Python Type Checker and LSP for Emacs</title><link>https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/</guid><description>Fast Python type checking and LSP with Eglot and Astral&apos;s ty in Emacs.</description><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-ty-with-eglot&quot;&gt;Setting Up ty with Eglot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-multi-lsp-question&quot;&gt;The Multi-LSP Question&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#how-other-editors-handle-this&quot;&gt;How Other Editors Handle This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#eglots-approach&quot;&gt;Eglot’s Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#aside---is-an-external-multiplexer-needed&quot;&gt;Aside - Is an External Multiplexer Needed?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#current-status&quot;&gt;Current Status&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Recently &lt;a href=&quot;https://astral.sh/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Astral&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (the team behind
&lt;a href=&quot;https://github.com/astral-sh/ruff&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ruff&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/astral-sh/uv&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;uv&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;) announced the beta release of
&lt;a href=&quot;https://github.com/astral-sh/ty&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, an extremely fast Python type checker and
language server written in Rust.&lt;/p&gt;
&lt;p&gt;The performance numbers are impressive. Without caching, ty is consistently
10-60x faster than mypy and Pyright. In an editor context, the gap is even more
dramatic: after editing a load-bearing file in the PyTorch repository, ty
recomputes diagnostics in 4.7ms, which is 80x faster than Pyright (386ms) and
500x faster than Pyrefly (2.38s).&lt;/p&gt;
&lt;p&gt;Beyond raw speed, ty includes some sophisticated type system features like
first-class intersection types, advanced type narrowing, and best-in-class
diagnostic messages inspired by Rust’s compiler. The diagnostics can pull
context from multiple files to explain not just what’s wrong, but why.&lt;/p&gt;
&lt;h2&gt;Setting Up ty with Eglot&lt;/h2&gt;
&lt;p&gt;Since ty implements the Language Server Protocol, it’s straightforward to use
with Emacs. Here’s my configuration using Eglot, the built-in LSP client:&lt;/p&gt;
&lt;aside aria-label=&quot;Update (Jan 11 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Jan 11 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;This is now more easily accomplished with my
&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; package,
which is available on MELPA and has support for inline PEP-723 dependencies.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-project-find-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;root &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;locate-dominating-file&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; dir &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;pyproject.toml&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cons&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;cl-defmethod&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; project-root &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;project &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;head python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;project-find-functions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-project-find-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;auto-mode-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/uv&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.lock&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; toml-ts-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;major-mode-remap-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; python-ts-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;python-ts-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-ensure&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;project-find-functions&lt;/code&gt; hook ensures that Eglot starts ty from the correct
directory in monorepos, picking up the right paths for code references. This is
particularly important when you have multiple Python projects with different
virtual environments.&lt;/p&gt;
&lt;p&gt;If you’re using uv to manage your tools, you can use &lt;code&gt;uvx&lt;/code&gt; instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;uvx&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Multi-LSP Question&lt;/h2&gt;
&lt;p&gt;A question that sometimes comes up in the Emacs community is whether multi-LSP
support is needed. For Python specifically, you might want both a type checker
(ty, Pyright) and a linter/formatter (Ruff) running simultaneously on the same
file.&lt;/p&gt;
&lt;h3&gt;How Other Editors Handle This&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;VSCode&lt;/strong&gt; handles this transparently. Multiple language servers can attach to
the same file type, and the editor merges their capabilities. This is largely
invisible to users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Neovim&lt;/strong&gt; also has built-in support for multiple LSP clients per buffer. You
can attach as many language servers as you want to a buffer, and Neovim will
aggregate their diagnostics, completions, and other features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zed&lt;/strong&gt; has built-in support for multiple language servers per language. You can
enable or disable servers in your settings using a simple array syntax, and Zed
already includes ty as a built-in option alongside basedpyright, Pyright, Ruff,
and PyLSP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lsp-mode&lt;/strong&gt; (for Emacs) supports running multiple servers for the same file
type. You can register a server with the &lt;code&gt;:add-on? t&lt;/code&gt; flag to start it in
parallel with other servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenCode&lt;/strong&gt; (the AI coding agent) also handles multiple LSP servers natively.
When a file is opened, OpenCode checks the file extension against all enabled
LSP servers and starts each applicable one. Diagnostics from all servers are
aggregated into a single collection, which is then provided to the LLM as
context. The implementation is straightforward: each server runs in its own
process, and diagnostics are merged by file path.&lt;/p&gt;
&lt;h3&gt;Eglot’s Approach&lt;/h3&gt;
&lt;p&gt;Eglot currently doesn’t support multiple language servers per buffer out of the
box. The primary solution being developed by Eglot’s author is
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, an external LSP
multiplexer that presents multiple LSP servers as a single server to the client.&lt;/p&gt;
&lt;p&gt;rassumfrassum works by spawning multiple LSP server subprocesses and merging
their capabilities, diagnostics, and responses. For Python with ty and Ruff, you
can run it directly from the command line:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then configure Eglot to use that command:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the ty + Ruff use case specifically: these tools are complementary, not
overlapping. ty is a type checker (replacing mypy/Pyright), while Ruff is a
linter and formatter (replacing Flake8/Black/isort). If you want both type
checking and linting/formatting, you’d need both servers running. That said, ty
does include some features that overlap with linting, like dead code elimination
and unused dependency detection, so the boundary may blur somewhat as ty
matures.&lt;/p&gt;
&lt;h3&gt;Aside - Is an External Multiplexer Needed?&lt;/h3&gt;
&lt;p&gt;It’s worth asking whether an external tool like rassumfrassum is the right
approach when most other editors have built-in multi-LSP support.&lt;/p&gt;
&lt;p&gt;While this may keep Eglot’s codebase simpler and promote reusability, it’s also
very much not aligned with what other popular editors are doing, as we’ve shown
earlier.&lt;/p&gt;
&lt;p&gt;One could argue that rass makes it easier to customize server combinations
per-project via its Python preset files. But this is really just trading one
config format for another - Eglot already supports per-project configuration via
&lt;code&gt;.dir-locals.el&lt;/code&gt;. I also like the competing idea of autodetecting what’s needed:
my &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;emacs-shared&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; configuration has
this, albeit just for JS/TS projects currently.&lt;/p&gt;
&lt;aside aria-label=&quot;Update (Jan 11 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Jan 11 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;I discussed
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum#performance&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rass’s performance&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
with its author, who noted that Python is not currently a bottleneck in
practice, as rass spends roughly 90% of its time waiting for I/O from the
underlying LSP servers, with only 10% on actual processing. The documentation
has also been improved to mention how to use rass without presets.&lt;/p&gt;&lt;p&gt;It’s also possible in theory to have rass perform autodetection of which tool to
use for each workspace. That might not be implemented yet at the time of this
update.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Further pain points with rass:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extra complexity for users (another tool to install and configure)&lt;/li&gt;
&lt;li&gt;Potential performance overhead from the proxy layer, especially given the
implementation in Python&lt;/li&gt;
&lt;li&gt;I’d expect multi-LSP to “just work” like it does in other editors&lt;/li&gt;
&lt;li&gt;lsp-mode already handles this natively&lt;/li&gt;
&lt;li&gt;The documentation emphasizes Python config files (&lt;code&gt;.py&lt;/code&gt; presets) over
command-line arguments, which makes the tool feel more complex than it needs
to be for simple use cases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One caveat: the approach of splitting up multiple LSP servers using a &lt;code&gt;--&lt;/code&gt;
separator seems reasonably ergonomic to use with &lt;code&gt;eglot-server-programs&lt;/code&gt;, but I
wish that it were brought forward a bit more in the docs.&lt;/p&gt;
&lt;p&gt;I think I have to conclude that if rass continues to be the way forward with
Eglot, I may well end up switching to lsp-mode in the future. This may
especially be the case for JS/TS projects, which will likely have fun
combinations of &lt;code&gt;eslint&lt;/code&gt;, &lt;code&gt;oxlint&lt;/code&gt;, and &lt;code&gt;biome&lt;/code&gt; for a while due to the evolving
nature of those tools and the performance vs functionality trade-offs.&lt;/p&gt;
&lt;h2&gt;Current Status&lt;/h2&gt;
&lt;p&gt;ty is in beta, so some LSP features may not be fully implemented yet. The
&lt;a href=&quot;https://docs.astral.sh/ty/features/language-server/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;official docs&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; list
supported capabilities including Go to Definition, Symbol Rename, Auto-Complete,
Auto-Import, Semantic Syntax Highlighting, and Inlay Hints.&lt;/p&gt;
&lt;p&gt;A patch to add ty to Eglot’s default server list has already been submitted by
Steve Purcell (the MELPA maintainer), so it should be included in a future Emacs
release.&lt;/p&gt;
&lt;p&gt;If you’re doing Python development in Emacs, ty is worth trying out. The speed
improvements alone make it compelling, and the diagnostic quality is excellent.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>My Emacs AI Setup</title><link>https://mwolson.org/blog/2025-12-03-my-emacs-ai-setup/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-12-03-my-emacs-ai-setup/</guid><description>How I use AI tools in Emacs: minuet for completions, gptel-fn-complete for rewrites, OpenCode for most else.</description><pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;I’ve been evolving my Emacs AI workflow over the past year since releasing
&lt;code&gt;gptel-fn-complete&lt;/code&gt;, and I’m happy with where it’s currently landed. This post
describes most of the AI tools that I use with Emacs and how they fit together.&lt;/p&gt;
&lt;p&gt;A high-level overview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I pare down and simplify
&lt;a href=&quot;https://github.com/milanglacier/minuet-ai.el&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;minuet&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;’s functionality quite a
bit, and control which files and kinds of buffers it can act on.&lt;/li&gt;
&lt;li&gt;I tend to use &lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; as the source of
truth for which providers and models to use, as well as any common arguments
like temperature or amount of thinking.&lt;/li&gt;
&lt;li&gt;I tend to use &lt;a href=&quot;https://opencode.ai&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; outside of Emacs for most AI
work, and then rely on &lt;code&gt;auto-revert-mode&lt;/code&gt; (as implicitly enabled by
&lt;a href=&quot;https://magit.vc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Magit&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;) to have Emacs catch up and review the changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Inline completions with minuet&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/milanglacier/minuet-ai.el&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;minuet&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; provides automatic inline
AI auto-completions in Emacs. I like that Minuet includes just the right amount
of buffer context with the completions API, which improves the quality of the
results.&lt;/p&gt;
&lt;p&gt;By default, it shows multiple suggestions that you can cycle through, but I
prefer the Cursor-style approach of showing just one suggestion at a time. This
keeps the experience very simple and low-friction.&lt;/p&gt;
&lt;p&gt;I’ll explain how to achieve that over the next few sections.&lt;/p&gt;
&lt;h3&gt;Basic configuration&lt;/h3&gt;
&lt;p&gt;After installing &lt;code&gt;minuet&lt;/code&gt; and &lt;code&gt;gptel&lt;/code&gt; from MELPA, I configure Minuet like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;minuet&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt minuet-add-single-line-entry &lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;          minuet-auto-suggestion-debounce-delay &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;          minuet-n-completions &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-accept-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-next-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-previous-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-g&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-dismiss-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&amp;lt;tab&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-accept-suggestion-line&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The settings are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;minuet-add-single-line-entry&lt;/code&gt;: Don’t clutter the completion menu too much
with extra single-line versions of the suggestions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;minuet-auto-suggestion-debounce-delay&lt;/code&gt;: How long to wait before requesting a
suggestion&lt;/li&gt;
&lt;li&gt;&lt;code&gt;minuet-n-completions&lt;/code&gt;: Set to &lt;code&gt;1&lt;/code&gt; for Cursor-style single suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once a code complete suggestion is presented, the following keys are available,
in order of importance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;TAB&lt;/kbd&gt;: Complete just the first line of the suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c C-c&lt;/kbd&gt;: Insert the entire multi-line suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-g&lt;/kbd&gt;: Remove the suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-n&lt;/kbd&gt; and &lt;kbd&gt;C-p&lt;/kbd&gt;: Cycle through the next or previous
suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll now describe a few other customizations that I think make Minuet a bit more
ergonomic.&lt;/p&gt;
&lt;h3&gt;Blocking some suggestions within buffers&lt;/h3&gt;
&lt;p&gt;I make sure that suggestions only happen at the end of non-empty lines, to avoid
having them trigger in cases where I’m reading through the file quickly or
editing existing code. It’s also important to not attempt suggestions when the
buffer is read-only, since that will never be helpful.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-block-suggestions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Return nil if we should show suggestions, t (blocked) otherwise.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Criteria:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- File must be writable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- Cursor must not be at beginning of line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- Cursor must be at the end of line (ignoring whitespace).&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buffer-read-only&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bolp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;looking-at-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;*$&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;minuet&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-auto-suggestion-block-predicates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;            #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-minuet-block-suggestions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; -100&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Enabling Minuet at file-level&lt;/h3&gt;
&lt;p&gt;I enable Minuet’s auto-suggestion mode in programming buffers, with some
exclusions:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; kill switch for minuet functionality, currently allowed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-minuet-auto-suggest-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; my shared Emacs settings have this (disabled on every file by default):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-minuet-exclude-file-regexps&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; my personal configuration has something like this:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setq my-minuet-exclude-file-regexps &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.emacs.d/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.gnupg/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.ssh/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-exclude&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let* &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;filename &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; filename&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;lst my-minuet-exclude-file-regexps&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;          (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;string-match-p&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;           (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;string-join&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;mapcar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;##&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;concat &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;(?:&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; %&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; lst&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;           filename&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-maybe-turn-on-auto-suggest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-auto-suggest-p &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-minuet-exclude&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;minuet-auto-suggestion-mode &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;prog-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-minuet-maybe-turn-on-auto-suggest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll want to customize &lt;code&gt;my-minuet-exclude-file-regexps&lt;/code&gt; to make sure any other
sensitive directories are excluded from having their buffers sent to AI.&lt;/p&gt;
&lt;h3&gt;Provider configuration&lt;/h3&gt;
&lt;p&gt;I sync minuet’s provider settings from my
&lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; configuration, which lets me use the
same API keys and model settings across both tools. This part is a bit complex,
but I switch between models often enough that it ended up being useful to have.&lt;/p&gt;
&lt;p&gt;The code for this is available separately on my
&lt;a href=&quot;https://mwolson.org/guides/emacs-ai-setup/&quot;&gt;Emacs AI Setup Guide&lt;/a&gt; since it was getting a bit too
long for a blog post. The gist is that I only configure API keys once in my
&lt;code&gt;auth-source&lt;/code&gt; file, and both gptel and minuet can use them.&lt;/p&gt;
&lt;p&gt;For AI models within Emacs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I currently use Kimi K2 for Minuet suggestions.&lt;/li&gt;
&lt;li&gt;I use Claude Opus 4.5 for one-off gptel prompts and &lt;code&gt;gptel-fn-complete&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I currently use &lt;a href=&quot;https://opencode.ai/docs/zen&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode Zen&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; exclusively, for
several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They have a tasteful selection of models.&lt;/li&gt;
&lt;li&gt;It’s easy to switch between different models with the same API credit pool,
which can be configured to automatically reload when below a threshold.&lt;/li&gt;
&lt;li&gt;When they run a model, it’s almost certain to be stable, unlike my prior
experience with some Openrouter providers.&lt;/li&gt;
&lt;li&gt;They have models that have been no-cost for a while, and they recently started
offering GPT 5.1 (one of my primary writing, coding, and task-following
models) at a lower price than OpenAI does.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Keybinds&lt;/h3&gt;
&lt;p&gt;I have the following other keybinds to manually trigger &lt;code&gt;minuet&lt;/code&gt; or
&lt;code&gt;gptel-fn-complete&lt;/code&gt;, in addition to some other useful &lt;code&gt;xref&lt;/code&gt; bindings:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;make-sparse-keymap&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-show-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;gptel-fn-complete&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-definitions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-go-back&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-references&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;RET&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;embark-act&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;My key customizations for AI and xref.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x C-.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since these get called somewhat frequently, I like to bind a few adjacent keys
to the same thing, in case I miss a keypress or hold down &lt;kbd&gt;Ctrl&lt;/kbd&gt; for
too long.&lt;/p&gt;
&lt;h2&gt;Completing functions with gptel-fn-complete&lt;/h2&gt;
&lt;p&gt;Earlier in the year I wrote
&lt;a href=&quot;https://github.com/mwolson/gptel-fn-complete&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel-fn-complete&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;. It uses
&lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; under the hood, and is helpful for
completing entire functions or regions. It makes a reasonable attempt to
automatically find the bounds of the function at point, and if you’re using a
treesit-compatible mode, it will always succeed at that. This was nice to pair
with local AI, since the context sent to the AI did not have to be very large.&lt;/p&gt;
&lt;p&gt;Now that the tooling and remote AI models have improved to such a large degree,
I find myself using OpenCode almost exclusively instead.&lt;/p&gt;
&lt;h2&gt;Agentic workflows with OpenCode&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https://opencode.ai/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; for larger tasks that benefit from an
agentic approach, such as exploring codebases, making multi-file changes, or
debugging complex issues.&lt;/p&gt;
&lt;p&gt;OpenCode runs in the terminal and has access to tools for reading files,
searching code, and making edits. I typically run it in a
&lt;a href=&quot;https://ghostty.org/docs&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ghostty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; terminal rather than inside Emacs; if I
change my mind about that, I’d likely pick up
&lt;a href=&quot;https://github.com/xenodium/agent-shell&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;agent-shell&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; to manage OpenCode.&lt;/p&gt;
&lt;p&gt;One helpful (and perhaps unexpected) side effect of using
&lt;a href=&quot;https://magit.vc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Magit&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; is that it has an automatically activated major mode
named &lt;code&gt;magit-auto-revert-mode&lt;/code&gt; that automatically turns on &lt;code&gt;auto-revert-mode&lt;/code&gt;
when an open Emacs buffer is tracked using Git.&lt;/p&gt;
&lt;p&gt;In practice this means that if OpenCode is making changes, Emacs will pick those
up and refresh the buffer contents automatically. This ends up being a great
workflow for me, since I can review in Emacs what the AI model did, without
having to bring in any specialized Emacs modes to help. I especially don’t want
to review diffs in any tool other than Magit.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This three-layer approach covers my AI needs well:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;minuet&lt;/strong&gt; for seamless inline completions while typing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gptel-fn-complete&lt;/strong&gt; for quick, targeted code actions at point&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenCode&lt;/strong&gt; for almost every kind of other task&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In case any of the above doesn’t work as-is (which is quite possible), it may
also be helpful to refer to my
&lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared Emacs settings&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; repo.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>ai</category><category>emacs</category><author>Mike Olson</author></item><item><title>Improving Performance by Reducing API Data Size</title><link>https://mwolson.org/blog/2025-11-28-performance-reducing-api-data-size/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-11-28-performance-reducing-api-data-size/</guid><description>How tailored API responses can prevent 5+ second load times.</description><pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;In a recent consulting engagement, I worked on improving performance for a
ticketing e-commerce website. This is the first of a series of posts about what
helped. I’ll discuss what led to the decision to reduce data size, the degree to
which that change helped, and other adjacent ideas like &lt;strong&gt;Backend-for-Frontend
(BFF)&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#diagnosis&quot;&gt;Diagnosis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#solution&quot;&gt;Solution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#opportunity-bff-endpoints&quot;&gt;Opportunity: BFF Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#whats-next&quot;&gt;What’s Next&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Diagnosis&lt;/h2&gt;
&lt;p&gt;One of the first things I did to diagnose the website’s performance was to use
Chrome DevTools to observe a page load with the Network tab open. I noticed that
one particular API call took 5+ seconds, with the page being blocked from a
contentful paint until that call completed. The API included multiple megabytes
of content for one month of an ongoing event series - for example: river cruises
and museum tours.&lt;/p&gt;
&lt;p&gt;Because the payload was so large, the browser was forced to tie up the main
thread while parsing JSON, causing “micro-stutters” where the UI could briefly
look like it’s frozen and not respond to user input. This is especially
noticeable on underpowered devices such as kiosks.&lt;/p&gt;
&lt;p&gt;Further, there’s an interesting performance opportunity that happens when you
can fit the payload size
&lt;a href=&quot;https://endtimes.dev/why-your-website-should-be-under-14kb-in-size/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;in under 14–15kB&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
that can significantly help page load and API performance, due to the
implementation details of the TCP slow start algorithm. Without getting into too
much technical detail, this happens because the initial TCP congestion window
(and the convention for the number of TCP packets that a server will send at one
time) limits how much data can be sent in the first round trip.&lt;/p&gt;
&lt;p&gt;In practice, staying under this window can often make the first response arrive
up to roughly twice as fast compared to slightly larger payloads. Missing the
~14kB window by even 1kB can increase the response time of that network call by
600ms or more, especially on higher-latency connections. Relatedly, for webpage
content that can’t be made that small, it can be helpful to stream in meaningful
“above-the-fold” content within the first 14kB, focusing on what is most
important for the user to see when they visit your website. This can include
headers, titles, and key CSS styling.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Returning to the earlier example: since we control both the frontend and the
backend code, and the product requirements only necessitate showing the complete
details of one day at a time, it made sense to provide different API calls where
we segmented the data based on the part that applied to each day, with the
&lt;code&gt;YYYY-MM-DD&lt;/code&gt; date value being part of the path of the new API calls. Requesting
these only took around 900ms, which significantly sped up the page load.&lt;/p&gt;
&lt;p&gt;In general, the split point doesn’t necessarily have to be by date - other
criteria could make sense depending on the use case. Reducing data might even
require multiple split points, in which case the API path could be modified to
include both of those (either in clear text or hashed form).&lt;/p&gt;
&lt;h2&gt;Opportunity: BFF Endpoints&lt;/h2&gt;
&lt;p&gt;One architectural idea that biases toward implementing these ideas is
&lt;strong&gt;Backend-for-Frontend (BFF)&lt;/strong&gt;. A Backend-for-Frontend is a backend whose only
consumer is a single frontend product. It sits between the client and other
backend services, and is expected to be developed alongside the frontend.
Ideally, the BFF and the frontend are even developed within the same monorepo,
where a single pull request can span changes to both.&lt;/p&gt;
&lt;p&gt;This pattern can be especially helpful when there are dependencies between
different individual existing backend API calls. Rather than waiting for one API
to complete in the frontend before making the next, bundle all that into one
combined BFF API call that will return all of the needed attributes and/or
related entities instead of just IDs.&lt;/p&gt;
&lt;p&gt;One can imagine a progression where gradual improvements are made to the
existing architecture.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;As a first step, implement a single BFF API call for the data needed on the
page, or perhaps for one section of a complex page. Within that API, include
each kind of data as a top-level (or nearly top-level) payload, ideally
scoping that data appropriately to keep API response payload size under
control.&lt;/p&gt;
&lt;p&gt;Remember: Don’t worry about having perfect levels of abstraction, as the only
consumer of this call is the frontend.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Later, implement other optimizations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Potentially allow the BFF to automatically render HTML server-side for the
part of the page that it controls instead of returning JSON response
payloads, leveraging tools like
&lt;a href=&quot;https://nextjs.org/docs/app/guides/backend-for-frontend&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Next.js&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;,
&lt;a href=&quot;https://tanstack.com/start/latest/docs/framework/react/guide/selective-ssr&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TanStack Start&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;,
and &lt;a href=&quot;https://react.dev/learn/react-compiler&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;React Compiler&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For the backend APIs that the BFF calls, potentially adjust these to
provide smaller data payloads and/or more ways to filter response data
using query parameters or POST parameters.&lt;/li&gt;
&lt;li&gt;Potentially allow the BFF to have a bit more direct access to the data
sources to skip a few layers, as long as rate limits and other controls are
properly implemented.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;APIs that are designed for general consumption by any kind of frontend or
backend provide helpful flexibility, but they can lead to poor webpage
performance without further data segmentation. When your API calls start
dragging past the 1-second mark, look at the payload size. Are you sending the
entire history when the page only needs a smaller subset?&lt;/p&gt;
&lt;p&gt;A BFF can be an interesting further idea that allows treating your API payload
as part of the UI design. By restricting data to the current context (specific
dates, active items, or relevant categories), your application can, as a general
rule, remain performant and flexible.&lt;/p&gt;
&lt;h2&gt;What’s Next&lt;/h2&gt;
&lt;p&gt;Our segmented API calls in this example now take 900ms each instead of 5+
seconds. But there’s more to be done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What can be done to reduce that 900ms even further?&lt;/li&gt;
&lt;li&gt;What can cause temporary mysterious latency spikes of 10+ seconds?&lt;/li&gt;
&lt;li&gt;And what if users in other regions are noticing slowdowns that seem much worse
than what the primary region experiences?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned to future blog posts in this series for answers to those questions.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>performance</category><author>Mike Olson</author></item><item><title>Automatic Arrow Characters in Emacs</title><link>https://mwolson.org/blog/2025-11-23-automatic-arrow-characters-in-emacs/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-11-23-automatic-arrow-characters-in-emacs/</guid><description>How to set up automatic conversion of &quot;-&quot; and &quot;&gt;&quot; to &quot;→&quot; in Emacs.</description><pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#intro&quot;&gt;Intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#quail-setup&quot;&gt;Quail Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#markdown-mode-activation&quot;&gt;Markdown Mode Activation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#handling-indirect-buffers&quot;&gt;Handling Indirect Buffers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-not-use-abbrevs&quot;&gt;Why Not Use Abbrevs?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#alternative-abbrev-mode&quot;&gt;Alternative: abbrev-mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#alternative-auto-activating-snippets&quot;&gt;Alternative: auto-activating-snippets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;While adding technical writing to &lt;a href=&quot;https://mwolson.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;my new website&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; using
Emacs, I realized I was used to working with software that automatically
converted the character &lt;code&gt;-&lt;/code&gt; followed by &lt;code&gt;&amp;gt;&lt;/code&gt; to the Unicode arrow character &lt;code&gt;→&lt;/code&gt;.
I decided to add the same behavior to my Emacs setup, and I’d like to describe
how I did it.&lt;/p&gt;
&lt;p&gt;Emacs has a library called &lt;code&gt;quail&lt;/code&gt; that allows defining custom character
mappings. If the left side of the mapping has multiple characters, and you type
them consecutively, Emacs replaces them with the right side.&lt;/p&gt;
&lt;h2&gt;Quail Setup&lt;/h2&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;quail-define-package&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Arrows&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;UTF-8&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Arrow input mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t t &lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt;nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;quail-define-rules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a new input method called “Arrows” that converts &lt;code&gt;-&amp;gt;&lt;/code&gt; to &lt;code&gt;→&lt;/code&gt; as you
type. I have to caveat that the arguments to &lt;code&gt;quail-define-package&lt;/code&gt; are a bit
unergonomic, and having &lt;code&gt;quail-define-rules&lt;/code&gt; not refer to the “Arrows” method
explicitly is a bit odd (it applies its rules to the most recently defined quail
input method/package), but it gets the job done.&lt;/p&gt;
&lt;h2&gt;Markdown Mode Activation&lt;/h2&gt;
&lt;p&gt;To use this input method specifically in Markdown mode, I have the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-turn-on-arrow-input&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Turn on arrow input mode.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #9D7CD8);--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic;--shiki-light:#8839EF;--shiki-light-font-style:inherit&quot;&gt;interactive&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;set-input-method&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;Arrows&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-turn-on-arrow-input&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Indirect Buffers&lt;/h2&gt;
&lt;p&gt;The above is already enough to accomplish what’s needed, but I’ll explain one
other edge case that I cover for Markdown specifically.&lt;/p&gt;
&lt;p&gt;I use &lt;code&gt;(setopt markdown-fontify-code-blocks-natively t)&lt;/code&gt; to fontify code blocks
within Markdown buffers using their native major modes. Previously I used
&lt;code&gt;poly-markdown-mode&lt;/code&gt; for that, but the built-in &lt;code&gt;markdown-mode&lt;/code&gt; approach seems
more robust lately.&lt;/p&gt;
&lt;p&gt;One precaution that I ported over from &lt;code&gt;polymode&lt;/code&gt; is the ability to exclude
certain hooks from running in the indirect buffers associated with those code
blocks, in case they cause slowdowns or problems. I’ll present those here:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-in-indirect-md-buffer-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Return non-nil if the current buffer is an indirect buffer created from a Markdown buffer.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;buf &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-base-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-live-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-current-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;           (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;derived-mode-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-inhibit-in-indirect-md-buffers&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;orig-fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; &amp;amp;rest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; args&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Don&apos;t run ORIG-FUN (with ARGS) in indirect markdown buffers.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Use this to advise functions that could be problematic.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;unless&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-in-indirect-md-buffer-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;apply&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; orig-fun args&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-around-advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Apply around ADVICE to FUN.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;If FUN is a list, apply ADVICE to each element of it.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cond&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;listp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;dolist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;el fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-around-advice el advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;symbolp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;              (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;advice-member-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; advice fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;advice-add&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun &lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;around advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I then ensure that the &lt;code&gt;Arrows&lt;/code&gt; input method only activates in the main Markdown
buffers, not indirect ones:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-around-advice &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;#&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-turn-on-arrow-input&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                  #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-inhibit-in-indirect-md-buffers&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation, along with quite a few other customizations, is also
available in my &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared .emacs setup&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some inspiration was taken from
&lt;a href=&quot;https://www.masteringemacs.org/article/inserting-emoji-input-methods&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this Mastering Emacs post&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
which describes how to make your own emoji input method with Quail.&lt;/p&gt;
&lt;h2&gt;Why Not Use Abbrevs?&lt;/h2&gt;
&lt;p&gt;After publishing this post, someone suggested using a local abbrev instead,
which would remove the need to inhibit activation in non-Markdown buffers. This
seemed like a reasonable alternative, so I investigated.&lt;/p&gt;
&lt;h3&gt;Alternative: abbrev-mode&lt;/h3&gt;
&lt;p&gt;It turns out that abbrevs aren’t quite as ergonomic for this use case. The
reason comes down to how Emacs categorizes characters into
&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Class-Table.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;syntax classes&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.
Abbrevs expand when you type a non-word-constituent character after a sequence
of word-constituent characters. The problem is that &lt;code&gt;-&lt;/code&gt; is typically a
punctuation character in most modes, not a word constituent. This means:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can’t define an abbrev &lt;code&gt;-&amp;gt;&lt;/code&gt; that works normally, because &lt;code&gt;-&lt;/code&gt; itself would
trigger expansion of whatever word came before it.&lt;/li&gt;
&lt;li&gt;To make abbrevs work, you’d need to either modify the syntax table to make
&lt;code&gt;-&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; word constituents, or implement a custom &lt;code&gt;abbrev-expand-function&lt;/code&gt;
to handle symbol syntax.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you wanted to pursue the abbrev approach anyway, here’s a working example
that modifies the syntax table:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-abbrev&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; gfm-mode-abbrev-table &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-markdown-abbrev-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Make &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; and &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; word constituents so that `-&amp;gt;` abbrev works.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;modify-syntax-entry&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;modify-syntax-entry&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;abbrev-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-markdown-abbrev-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: the &lt;code&gt;gfm-mode-abbrev-table&lt;/code&gt; is specific to &lt;code&gt;gfm-mode&lt;/code&gt;. If you use plain
&lt;code&gt;markdown-mode&lt;/code&gt;, substitute &lt;code&gt;markdown-mode-abbrev-table&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;However, this approach has several trade-offs compared to Quail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changing the syntax class of &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; affects word motion commands (&lt;code&gt;M-f&lt;/code&gt;,
&lt;code&gt;M-b&lt;/code&gt;), so &lt;code&gt;foo-bar&lt;/code&gt; would now be treated as a single word rather than two.&lt;/li&gt;
&lt;li&gt;You must type a space (or other non-word character) after &lt;code&gt;-&amp;gt;&lt;/code&gt; to trigger
expansion, rather than having it expand immediately as you type.&lt;/li&gt;
&lt;li&gt;There’s no visual feedback while typing. Quail highlights the first character
when a potential expansion is in progress, but abbrev-mode has no equivalent
preview feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Quail input method approach avoids these trade-offs. It operates at a
different level, matching literal character sequences regardless of syntax
class, and expands immediately as you type - which matches the Notion-like
behavior I was after.&lt;/p&gt;
&lt;h3&gt;Alternative: auto-activating-snippets&lt;/h3&gt;
&lt;p&gt;If you’d prefer a package-based solution that behaves more like Quail,
&lt;a href=&quot;https://github.com/ymarco/auto-activating-snippets&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;auto-activating-snippets&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(available on MELPA as &lt;code&gt;aas&lt;/code&gt;) expands snippets as you type without requiring a
trigger key.&lt;/p&gt;
&lt;p&gt;This gives you immediate expansion like Quail, without syntax table
modifications. The main difference from Quail is that &lt;code&gt;aas&lt;/code&gt; doesn’t provide
visual feedback during typing - you won’t see highlighting on the first
character when an expansion is possible.&lt;/p&gt;
&lt;p&gt;Also at the time of this writing, it hasn’t been updated since 2023-03-03 and
may have byte-compilation warnings.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>Ghostty + tmux uniform copy and paste</title><link>https://mwolson.org/blog/2025-11-12-ghostty-tmux-uniform-copy-paste/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-11-12-ghostty-tmux-uniform-copy-paste/</guid><description>Configure Ghostty and tmux for consistent copy/paste behavior across terminal sessions.</description><pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;This set of configuration changes achieves the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Disable automatic copying to clipboard while using the mouse to select text.
Instead, the selection is copied when the user types either
&lt;kbd&gt;Ctrl+Shift+c&lt;/kbd&gt; or
&lt;kbd&gt;Super+c&lt;/kbd&gt; (on macOS only) after using the mouse to select text.&lt;/li&gt;
&lt;li&gt;The &lt;kbd&gt;Super+c&lt;/kbd&gt; keybinding works by remapping it to
&lt;kbd&gt;Ctrl+Shift+c&lt;/kbd&gt; in ghostty.&lt;/li&gt;
&lt;li&gt;Ghostty will ignore those keybindings so that tmux can handle them. This
allows tmux to copy text cleanly even when scrolling through history and using
split panes.&lt;/li&gt;
&lt;li&gt;Ghostty will still handle pasting with &lt;kbd&gt;Ctrl+Shift+v&lt;/kbd&gt; or
&lt;kbd&gt;Super+v&lt;/kbd&gt; (on macOS only).&lt;/li&gt;
&lt;li&gt;The &lt;kbd&gt;r&lt;/kbd&gt; key can be pressed while selecting text to toggle rectangular
selection on or off.&lt;/li&gt;
&lt;li&gt;Clicking outside the selection will clear the selection.&lt;/li&gt;
&lt;li&gt;The selection will be displayed as inverse text on both tmux and ghostty for
consistency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Files&lt;/h2&gt;
&lt;h3&gt;ghostty-mac.conf&lt;/h3&gt;
&lt;p&gt;Apply these changes to &lt;code&gt;~/.config/ghostty/ghostty.conf&lt;/code&gt; if you’re on macOS:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;copy-on-select&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;keybind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; performable:ctrl+shift+c=copy_to_clipboard&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# remap super+c to ctrl+shift+c, use `bash -i -c cat` to see keycodes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# as you type them&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;keybind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; super+c=csi:99&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;6u&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;selection-invert-fg-bg&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;shell-integration-features&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; no-cursor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ghostty-linux.conf&lt;/h3&gt;
&lt;p&gt;Apply these changes to &lt;code&gt;~/.config/ghostty/ghostty.conf&lt;/code&gt; if you’re on Linux:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;copy-on-select&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;keybind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; performable:ctrl+shift+c=copy_to_clipboard&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;selection-invert-fg-bg&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;shell-integration-features&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; no-cursor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;tmux.conf.local&lt;/h3&gt;
&lt;p&gt;Add these changes to &lt;code&gt;~/.tmux.conf.local&lt;/code&gt;, assuming that you’re using
&lt;a href=&quot;https://github.com/gpakosz/.tmux&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;oh-my-tmux&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# increase history size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; history-limit&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 10000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# start with mouse mode enabled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; mouse&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# don&apos;t yank mouse selection on release; instead use alt+w&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# or ctrl+shift+c&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;unbind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; MouseDragEnd1Pane&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;unbind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; MouseDown1Pane&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; DoubleClick1Pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  if-shell&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;#{||:#{pane_in_mode},#{mouse_any_flag}}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;    send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -M&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  }&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;    copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -H&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-word&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; TripleClick1Pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  if-shell&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;#{||:#{pane_in_mode},#{mouse_any_flag}}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;    send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -M&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  }&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;    copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -H&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-line&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; MouseDown1Pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; clear-selection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; DoubleClick1Pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-word&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; TripleClick1Pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-pane&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; select-line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; C-S-c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-pipe&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; R&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; refresh-from-pane&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; copy-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; send-keys&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rectangle-toggle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# selection style&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;setw&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; mode-style&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;reverse&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#EA76CB;--shiki-light-font-style:italic&quot;&gt; #!important&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the location of &lt;code&gt;.tmux.conf.local&lt;/code&gt; might be different depending on how
you’ve configured oh-my-tmux.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><author>Mike Olson</author></item><item><title>gptel-manual-complete: AI function completion in Emacs</title><link>https://mwolson.org/blog/2025-05-19-gptel-manual-complete/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-05-19-gptel-manual-complete/</guid><description>Complete entire functions in Emacs using AI with gptel-manual-complete.</description><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;This is an example of how the existing &lt;code&gt;gptel-rewrite.el&lt;/code&gt; file can be used to
perform completion on an entire function, replacing what’s already written so
far in that function.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;To use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, configure it, and provide
the appropriate API keys&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://github.com/mwolson/gptel-fn-complete&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel-fn-complete&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now choose which key you’d like to bind it to. I typically add something like
this to my Emacs config:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;make-sparse-keymap&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;gptel-manual-complete&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-definitions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-go-back&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-references&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;My key customizations for AI and xref.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;global-set-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart Emacs&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;If you’ve used the above keybinds, they work like this (the only with AI is the
first one):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;C-c . c&lt;/kbd&gt; to complete the code at point using Claude AI; if you have
a comment near the end, that will better inform the completion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . .&lt;/kbd&gt; to visit the definition of the thing at point&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . ,&lt;/kbd&gt; to return to the original point after visiting something&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . /&lt;/kbd&gt; to find references to the thing at point&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;When I write this code in a &lt;code&gt;sample.el&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-code&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;AI should not modify this.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Sample 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-say-hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; print a welcoming message in a window off to the right&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-other-code&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;AI should not modify this either.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Sample 2&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move the cursor into the body of &lt;code&gt;my-hello&lt;/code&gt; and hit &lt;kbd&gt;C-c . c&lt;/kbd&gt; then
gptel will rewrite that &lt;code&gt;my-hello&lt;/code&gt; function to something like this, without
touching the other functions or deleting lines around it (results may vary, I
used Claude 3.5 Sonnet in this example):&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-hello&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Print a welcoming message in a window off to the right.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;buf &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;get-buffer-create&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;*Hello*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-current-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;erase-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Welcome to Emacs!&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Have a productive session.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;display-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                    &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;display-buffer-reuse-window&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;                       display-buffer-in-side-window&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;                      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;side &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;                      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;window-width&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 40&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, you can use the standard &lt;code&gt;gptel-rewrite&lt;/code&gt; keys like &lt;kbd&gt;C-c C-a&lt;/kbd&gt;
on that code to accept it and remove the overlay on it.&lt;/p&gt;
&lt;p&gt;Note that the function must have balanced parentheses, otherwise the code will
throw an error. This is to make it easier to locate the beginning and end of the
function to send to gptel’s context.&lt;/p&gt;
&lt;h2&gt;Inspiration&lt;/h2&gt;
&lt;p&gt;After adding a function to gptel’s context, I was using &lt;code&gt;gptel-rewrite&lt;/code&gt; and
accidentally hit Enter twice. This resulted in just the basic “Rewrite: ” text
being sent, and to my surprise that was very effective at having Claude fix the
problem I was going to ask about.&lt;/p&gt;
&lt;p&gt;I decided to see if Claude could also do code completions this way, with a very
terse kind of prompt on top of the standard &lt;code&gt;gptel-rewrite&lt;/code&gt; prompt, and it turns
out that it can!&lt;/p&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;For automatically identifying the entire current function to complete, you may
have the best luck with either Emacs Lisp or files with major modes that have
a tree-sitter grammar installed, as otherwise we have to guess. In general it
should err on the side of sending too little rather than too much.&lt;/li&gt;
&lt;li&gt;My Emacs setup is available at &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/mwolson/emacs-shared&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; which
has this and other features
&lt;ul&gt;
&lt;li&gt;Note that the install doc might take a while to get through, and may have
opinionated settings&lt;/li&gt;
&lt;li&gt;The additional AI features which have more bindings on &lt;kbd&gt;C-c .&lt;/kbd&gt; than
in the above example
&lt;a href=&quot;https://github.com/mwolson/emacs-shared/blob/master/doc/tips.md#using-ai-and-finding-definitions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;are described here&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item></channel></rss>