Welcome to the lab.

Bunch 1.4.8

Bunch 1.4.8 was officially released this morning. It consists entirely of bug fixes that have been going through beta testing for a while now. Nothing groundbreaking, but some stuff that you probably didn’t know wasn’t working1 is now working.

Some variable handling stuff:

  • Variable keys passed from URL handler were running into case sensitivity issues
  • Variables defined in a snippet call file line now work with conditional logic within the snippet (if var...)

Tagging stuff:

  • When launching or quitting Bunches based on tags, %\tag was not ignoring the action on close
  • Don’t inherit tags when nesting Bunches

Some general fixes:

  • Allow retries when sending AppleScript commands for things like Hide Desktop and Hide Dock
  • Ensure relaunch of Finder after hiding/showing desktop icons
  • Modifier key mismatch in Select Editor tooltip

Grab the latest version from the download page (or just use Bunch->Check for Updates). Please keep the bug reports coming, and feel free to join the discussions if there are features you’d like to see.

  1. Because they mostly relate to obscure features that, statistically, you likely don’t know exist. 

Terminal output with ALL the colors

I’ve always relied on 2-bit coloring for terminal output, sticking to eight foreground and eight background colors. It’s nice because it conforms to any user’s terminal theme. But there’s a whole world of colors out there (in most modern terminals) and I figured it was about time I started expanding my horizons.

I write most of my command line tools and scripts in Ruby. Call me old fashioned, but it really is a beautiful, readable language with plenty of room for elegant solutions. So this little snippet is in Ruby, but the core of it is simple enough that I can’t imagine it would take much work to port.

Like all terminal color output, it relies on ANSI escape codes. 2-bit escape codes look like \033[0;30;41m, which would be regular black text on a red background. But you can use RGB values in most modern terminals with an escape code like \033[38;2;RRR;GGG;BBBm (for a foreground color). So all you have to do is get RGB values for a specific color and insert them into that code.

The following Ruby snippet (gist here) will take a CSS hex color, e.g. #FA380E, and turn it into an ANSI escape sequence for you. It splits the hex value and uses the #hex method in Ruby to convert a 2-digit hex into a value between 1 and 256. With it you can run something like print "text to color".color256('#FA380E') and get some bright red text. The snippet itself is designed to be included in a script. If you run it directly you can test by using ./256color.rb "TEXT TO COLOR" "FG" "BG" where FG and BG are 3 or 6-digit hex codes. The octothorp (#) is always optional.

#!/usr/bin/env ruby
# frozen_string_literal: true

# 256-color hex interpretation for terminal colorization
# Usage
# "text".color256(foreground, background)
# print "Colorize this".color256('#f7921e', '#666')

# String category
module Color256
  def fg(fgc)

  def bg(bgc)

  def reset

  def rgb
    hex_string = sub(/^#?/, '')

    hex_string = hex_string.split(//).map { |a| a * 2 }.join('') if hex_string =~ /^#?[a-f0-9]{3}$/i

    parts = hex_string.match(/#?(?<r>..)(?<g>..)(?<b>..)/)
    t = []
    %w[r g b].each do |e|
      t << parts[e].hex

  def color256(fgc, bgc = nil)
    out = ''
    out += fg(fgc)
    out += bg(bgc) if bgc
    out += self
    out + reset

class ::String
  include Color256

def test
  256.times do |r|
    256.times do |g|
      256.times do |b|
        c = ''
        [r, g, b].each { |comp| c += format('%02x', comp) }
        print ' '.color256('000', c)

if __FILE__ == $0
  print ARGV[0].color256(ARGV[1], ARGV[2])

Hope that’s of use to somebody!

By the way, this is incorporated into the latest version of Doing such that you can use %#RRGGBB instead of a color name to set colors in a template. And use %b#RRGGBB to set a background color. Customize away!

I’ve missed you, too

I haven’t had a lot of time for personal projects lately. Partly because I switched my ADHD meds to Vyvanse, which isn’t great for my attention but does keep my bipolar in check, meaning I’ve had moderate concentration abilities combined with a complete lack of all-night manic coding binges. Which, by and large, is better for my productivity overall, but definitely means less time spent on side projects. I’m at a point in my life where that’s the preferable outcome.

That said, I’ve pushed some updates to Doing recently, and have been working on a Bunch bug or two. Marked has gotten a couple of bugfixes pushed, and we’re so close to getting nvUltra out the door. Between Fletcher and I, time for it has been tight, but we’ve put a feature freeze on it and are just finalizing the MAS testing for the in-app purchase and subscription setup we’re going to use to allow us to offer a free trial and a sustainable pricing structure. More soon.

In the meantime, I’ve gotten some new hardware recently that I thought warranted a quick post.

First, I got my new Mac Studio. I didn’t go all out on it; I got the Max with 64G of RAM, but it leaves my M1 mini in the dust. Compiler tasks that took about 30 seconds on the mini now take 5 seconds on the Studio. And the test suite for Doing used to take about 72 seconds to run, and now takes 30 — which is impressive mostly because what slows those tests down is a ton of file read/writes that processor speed can’t improve anyway. So I’m very happy with the purchase, and if you’re looking for a Mac with top performance, the Studio is an excellent choice.

Second, I got an OWC Express 4M2 for work with a couple of NME SSD cards and I’m so blown away by the speed that I’ll soon be purchasing a second one for myself. 1345 MB/s read and 1436 MB/s writes. It’s crazy fast for an external drive, and you can put together terrabytes worth of storage for less than buying individual external SSD drives. I also got a G-RAID for 36TB of backup space, and over a Thunderbolt 4 connection that one gets 482/514 MB/s even with platter drives. Now I just need a faster Synology…

Well, that’s my update for now. I know my posting pace has slowed a bit, but I’m grateful to all of the BrettTerpstra.com readers who support my work with monthly donations. I’ll keep putting out (and supporting) new stuff, and I’ll be working to post a bit more often.

Bunch: Focus Modes and other tips

I put some time into Bunch this weekend. It’s been a little while since I’ve worked on it — I haven’t needed it to do anything new lately, and the current release hasn’t had any major bug reports. So it’s been happily humming along. But there was one feature request that I did keep receiving and decided to take another look at.

The request is for support for Monterey’s focus modes. Bunch does a decent job of enabling and disabling Do Not Disturb, but the process is a hack that involves nested settings in PLIST data and toggling system daemons. I don’t love that I have to do that, but Apple provides no API for such settings. And I haven’t found a way (yet) to extend that hack to work with Focus Modes. So I offer a workaround: you can turn Focus Modes on and off using Shortcuts, and you can easily run Shortcuts in a Bunch using the shortcuts CLI.

  1. Create two shortcuts for each focus mode you want to control from Bunch, one for turning on, and one for turning off.

  2. Then, in a Bunch, you can use $ /usr/bin/shortcuts run "SHORTCUT_ON_NAME" (replacing SHORTCUT_ON_NAME with the name of the shortcut that turns the focus mode on), and !$ /usr/bin/shortcuts run "SHORTCUT_OFF_NAME". The combination of these two will turn the focus mode on when the Bunch opens, and off when it closes. You can, of course, reverse these as needed.

    $ /usr/bin/shortcuts run "Work Mode"
    !$ /usr/bin/shortcuts run "Work Mode Off"
  3. There is no step 3.

Now when you open and close the Bunch, your desired Focus Mode will toggle on and off with it. It’s a couple extra steps beyond having a Bunch command to handle it, but I don’t foresee being able to incorporate them directly. Of course, I may just be missing something in the API that would make it possible without deep hacks, in which case I will definitely incorporate it.

Single Bunch Mode Grouping

Another request I got led to a couple of bug fixes. The following currently only works in the 2.4.8 beta (download here), but if testing goes well, it will be released to the stable version in short order.

What came up was the idea of having Single Bunch Mode apply to subsets of Bunches.

Single Bunch Mode can be enabled in preferences and simply means that when a Bunch opens, all other Bunches close, so you’re only running one at a time. Because Bunch is built for “context switching,” this makes switching your working context a single step. You can have any Bunch ignore Single Bunch Mode using frontmatter, so Bunches that should always be running can be easily excluded.

However, you might want to have multiple groups of Bunches where only one Bunch within the group is running at any given time. This can be accomplished with Bunch’s tagging features.

To tag a bunch, you just add frontmatter:

tags: work

Tag all of the Bunches in this subgroup with the same tag. Then, in each Bunch in the group, use the \tag syntax to act on the tag. Use ! to indicate you want to close the tagged Bunches, and include a % sign to ignore the directive when closing (so it doesn’t reverse it and reopen all of the tagged Bunches).


This effectively gives you Single Bunch Mode for subsets of Bunches. If you ever want to do that, give it a shot.

Web Excursions for March 15, 2022

Web excursions brought to you in partnership with CleanMyMac X, all the tools to speed up your Mac, in one app.

I’ve started a collection of scripts for Hook. It’s small right now, but should grow in the future. Worth a bookmark or a star if you’re a Hook user.
1Password for SSH & Git (Beta)

Introducing 1Password for SSH & Git (Beta), the single source of truth for all your SSH keys.

Zettel Composer: a tool for combining notes — Zettelkasten Forum

After about a year of personal experiments, I present you the “Zettel Composer”, a script for combining Zettelkasten notes in a variety of situations: browsing annotations, publishing a book or a paper, assembling a handout for lectures or conferences etc.

Kaleidoscope Developer Tools for Safari
The good folks behind Kaleidoscope have added a Safari extension. It adds a ksdiff() function to the debugger that you can call from the console or breakpoint actions. It can also diff the source and CSS of any page or element, which is amazing for web development and debugging. Nice work!
cedstrom/powermate-osx: Griffin Powermate Driver for macOS
A little driver for the Bluetooth Griffin Powermate, perfect for advanced scripting and integration with a tool like Hammerspoon.

CleanMyMac X

He’s Doing it again…

I’ve added some stuff to Doing lately. Like, a lot of stuff. To be fair, I’ve also done a lot of jobby job stuff. And worked with Fletcher on nvUltra. And started a rewrite of Gather as a menu bar clipboard web Markdownifier with integration with multiple apps (that one’s going to be cool if I finish it). And some time on Bunch. But as I’ve said before, the one thing I use when working on all of these is Doing. And I always want it to do something new or better, so it gets some attention.

Oh, and looking at my doing recent output reminds me that I also tried my hand at creating my first oh-my-fish plugin yesterday. It has some dependencies and I don’t think it’s right for the main packages repo, so you have to add the repository manually to try it out, but I think it’s fully functional. If you want intelligent directory navigation with bookmarks, fuzzy matching, and fasd/fzf integration in Fish, all in your cd command, check it out.

Anyway, what were we talking about? Hang on:

doing last...

Right. Doing updates. To get the latest version, just run gem install doing, which as of this writing should get you at least version 2.1.34. (If you get an error, run the same command with sudo.)