My configuration files... These files are synced across multiple dev machines using chezmoi.
Control-Shift-Command-I (capture) and Control-Shift-Command-O (Bob Refs) are owned by the native Bob Mac Capture app. Hammerspoon no longer registers the capture shortcut or carries the retired WebView capture workflow and Lua capture grammar.
The last known-good pre-cutover chezmoi revision is
3d841c1e9c6dac9f558709a6ba6ef36082c2c4d4. To roll back, first turn off Use
production Control-Shift-Command-I in Bob Mac Capture Settings so the app returns to
the Control-Shift-Command-R development capture shortcut. Then restore and deploy the
old Hammerspoon feature:
git restore --source=3d841c1e9c6dac9f558709a6ba6ef36082c2c4d4 -- \
home/dot_hammerspoon/init.lua \
home/dot_hammerspoon/task_capture.lua \
tests/hammerspoon/task_capture_spec.lua
chezmoi apply ~/.hammerspoonNever leave both production hotkeys active during rollback. A targeted
chezmoi apply ~/.hammerspoon does not run chezmoi scripts, so follow it with
Hammerspoon's menu-bar Reload Config or a full apply.
A full chezmoi apply/chezmoi update on macOS restarts Hammerspoon when any Lua file
under home/dot_hammerspoon/ changed. The hook is
home/.chezmoiscripts/run_onchange_after_restart_hammerspoon.tmpl. It does nothing when
Hammerspoon is not running, since the new files load on its next launch. A new
Hammerspoon Lua module must be added to the hook's hash list:
tests/bash/hammerspoon_restart_hook_test.sh fails until it is. A targeted
chezmoi apply ~/.hammerspoon does not run scripts, so use Hammerspoon's menu-bar
Reload Config or run a full apply instead.
Hammerspoon shows the current Bob Pomodoro as one status item in the fixed order theme
(duration) · 🍅 status, with → stop time inserted once overdue. The 🍅 sits
immediately before the countdown, separated by exactly one ordinary space (plus the
padded overdue status's own no-break-space margin), so it identifies the digits to its
right. The 🍅 accompanies current-session countdowns and overdue status only; the idle
item has no tomato. While running, the duration names the session's scheduled length;
the stop time returns once the session is overdue, when the scheduled endpoint is the
useful fact. The stop time is zero-padded 24-hour HH:MM, matching the ledger
convention.
| State | Example title |
|---|---|
| Running | DEEP WORK (50m) · 🍅 12:34 |
| At the stop time | DEEP WORK (50m) · 🍅 00:00 |
| Recently overdue | DEEP WORK (50m) → 10:15 · 🍅 +00:01 |
| Last second before escalation | DEEP WORK (50m) → 10:15 · 🍅 +09:59 |
| Ten minutes overdue and later | DEEP WORK (50m) → 10:15 · 🍅 OVERDUE |
| Session without a name | UNTITLED (50m) · 🍅 12:34 |
| Duration unknown (degenerate range) | DEEP WORK → 10:15 · 🍅 12:34 |
| No current session | 1h35m |
| No session, reminder step | 1h35m φ 5m |
The duration is the scheduled session length in whole minutes (5m, 25m, 50m,
90m, 120m), derived from the ledger range with midnight wrap (2330-0020 → 50m).
It renders in the ordinary menu-bar foreground, matching the surrounding context. A
zero-length range or invalid input means unknown: the duration is omitted and the
running title falls back to → HH:MM, so every current-session title keeps at least one
time anchor plus the status.
The running countdown digits stay in the ordinary menu-bar foreground for the whole
session, including 00:00 and a session whose duration is unknown. They use a bold
monospaced face, falling back to the regular monospaced face when bold is unavailable.
From +00:01 through +09:59, the +MM:SS countdown is alert red (#E3413B). For the
first ten seconds of every overdue minute — +00:01–+00:10, then +01:00–+01:10,
+02:00–+02:10, …, +09:00–+09:10 — it flashes at 1 Hz like the OVERDUE badge:
red text alternating with white (#FFFFFF) on the same red, in the countdown's
monospaced face. For the rest of each minute (from :11) it holds steady red. The count
carries one no-break space on each side in every overdue frame, the same padding as the
OVERDUE badge, so the item keeps one width from +00:01 through +09:59 and never
shifts when a pulse starts or stops. At and after ten minutes overdue, the status is
OVERDUE and that badge flashes between red text and white on the same red. Without a
session, the item shows today's worked total as <H>h<M>m, supplied by bob's
[WORKED <minutes>m] line from today's closed Pomodoros. The label is green (#30d158)
with no tomato; for example, 95m is 1h35m. Between each 10-second flash step it
rests for a Fibonacci number of minutes — 1m, 1m, 2m, 3m, 5m, 8m, … uncapped — so the
steps fall at 1:00, 2:10, 4:20, 7:30, 12:40, 20:50, … counted from when it appeared
while it stays up: bold green text alternates at 1 Hz with a filled pill of dark
forest-green #062E14 on #30d158, the same cadence as OVERDUE. Each step names the
rest it just took as φ Nm (1h35m φ 5m), shown through the whole step in both frames
and gone between steps. The whole label is one pill with a regular-weight φ separator.
Its width changes at reminder-step boundaries and when today's worked total changes,
which only happens when the ledger changes. The sequence re-anchors when the label
appears (after a session ends, after a command or parse failure hides the item, or after
a Hammerspoon reload) and when the Mac wakes or unlocks while it is showing, always
restarting at φ 1m. Both idle frames pad the pill with a no-break space at its outer
edges so the item never changes width mid-step. Theme, duration, separator, arrow, stop
time, and the tomato stay in the ordinary foreground. A mid-tone wallpaper directly
under a transparent menu bar can undercut any fixed color, including the system's own
labels; turning on Accessibility › Display › Reduce transparency restores a uniform
surface.
Long themes are bounded to 24 Unicode code points including the final ellipsis; the full
theme stays in the tooltip and dropdown. The tooltip leads with full theme (duration)
and Stops at HH:MM, and the dropdown first item is full theme (duration) → HH:MM;
without a known duration they stay as full theme and full theme → HH:MM. The tooltip
and dropdown carry no tomato and keep the raw bob pomodoro line and Last sync
details. Without a session, the dropdown's first row names today's worked total and it
also shows a disabled Next reminder at HH:MM · φ Nm row previewing the next flash
step. At and after ten minutes overdue, the OVERDUE badge flashes between red text and
white-on-red; in every state only the status run ever flashes, and the theme, duration,
arrow, stop time, separator, and tomato never do. The menu re-syncs within about a
second when today's daily note changes on disk (an Obsidian edit, a bob command, or a
vault-sync pull), polls bob pomodoro --show-stale --worked every 60 seconds as a
safety net (every 15 seconds if the file watcher cannot start), refreshes on wake and
unlock, offers a manual Refresh item, and re-syncs once when crossing zero. A
[WORKED <minutes>m] result shows the worked total, an empty result falls back to
NO POMODORO, and command or parse failures hide the item. The dropdown shows a
snapshot taken when it opens, stays open while the title keeps updating, and shows fresh
details when reopened. The Mac's installed bob must support --worked before this
config is applied; an older bob exits 2 on the unknown option and Hammerspoon hides
the item.
Hammerspoon shows internet connectivity as one status item beside the Pomodoro item. The
title is a glyph plus a successes/total count over the last 30 pings (✓ 27/30),
padded to a fixed width whenever it shows. When every one of the last 30 pings answered,
the title is just a green ✓ and the item narrows. The count comes back as soon as a
miss enters the window, and it also shows while a fresh window is still filling (after
start or a sleep reset). The tooltip and dropdown still give the score. The count uses
Menlo regular; the glyph uses the default menu bar face, and nothing is bold. The
tooltip names the score and the last round-trip time. Opening the item shows the tier, a
30-cell history strip, the score over its span, the last ping, and a Network Settings
shortcut that opens System Settings → Network. If the shortcut cannot open Settings, it
shows a brief alert naming the manual fallback and logs the failure. The dropdown shows
a snapshot taken when it opens, stays open while the title keeps updating, and shows
fresh details when reopened.
One ping stream feeds both this item and the tmux status bar. Hammerspoon is the
preferred producer: it pings 8.8.8.8 every 2 s, and both displays render the same shared
30-sample window, so they always agree. tmux_ping only reads while a fresh Hammerspoon
heartbeat exists and falls back to pinging when Hammerspoon is not running, broken, or
unable to write. Handover is self-healing in both directions: Hammerspoon claims the
stream by writing its heartbeat and tmux backs off on its next redraw, while tmux
resumes pinging within about 6 s when Hammerspoon stops. At most one extra ping happens
during a handover. Nobody pings while the Mac is locked: Hammerspoon stops pinging but
keeps refreshing its heartbeat, so tmux does not take over either. With Hammerspoon not
running, tmux keeps working on its own.
To change the size, edit window_size in ~/.config/ping_window/config (chezmoi source
home/dot_config/ping_window/config), keep it in 3–99, and both displays switch within
one 2 s tick with no reload. A window older than the new span is not reset by a size
change.
| Situation | Traffic |
|---|---|
| Unlocked, tmux client attached | 1 ping / 2 s |
| Locked, tmux client attached | none |
| Unlocked, no tmux client | 1 ping / 2 s |
| Hammerspoon not running | tmux pings as before |
| Mac asleep | none |
Health tiers, checked in this order from the newest sample backward:
| Tier | Rule | Menu bar | tmux status |
|---|---|---|---|
stale |
empty window, or no sample for > 6 s | ◌ 27/30 in gray |
◌ 27/30 in blue-gray |
offline |
3 or more trailing misses | white ✗ 0/30 on red |
white ✗ 0/30 on red |
down |
newest ping missed, under 3 trailing | red ✗ 29/30 |
red ✗ 29/30 |
lossy |
newest answered, under 90% answered | orange ✓ 26/30 |
amber ✓ 26/30 |
online |
newest answered, 90%+ answered | green ✓ when all 30 answered, otherwise green ✓ 29/30 |
green ✓ when all 30 answered, otherwise green ✓ 29/30 |
The shared window lives in ~/tmp/tmux_ping_state: one LF-terminated line of
<heartbeat> <producer> <sampled> <results>, where heartbeat is the producer's latest
write, producer is hammerspoon or tmux, sampled is when the newest sample was
sent (0 when empty), and results is 1–99 0 / 1 characters, oldest first (-
when empty). Writers replace the file atomically, so readers never lock. A window older
than the window span (size × 2 s, 60 s by default) is dropped on the next sample, so no
window spans a sleep or a long pause.
On athena, /tmp is a 32G tmpfs — RAM-backed, shared by every numbered sase
workspace, and it has hit ENOSPC in practice. Two rules follow from that, and they
matter most for agents, whose shells are initialized from this repo's ~/.profile and
~/.config/aliases.sh.
1. A cleanup aimed at /tmp must reach the real rm. The XDG trash spec requires
the trash to live on the same filesystem as the file, so "trashing" something under
/tmp just moves it to /tmp/.Trash-$UID and frees zero bytes. There is no longer an
rm → trash alias in aliases.sh, so a plain rm -rf /tmp/… is fine today. But
trash, trash-put, and nvim's file-delete mapping still trash things, and any future
shell that re-aliases rm would silently turn every /tmp cleanup into a no-op for
space. When in doubt, use /usr/bin/rm or command rm so no alias or function can
intercept it. alias r='/bin/rm' is the shorthand for this.
2. Trash that does land on a temp filesystem gets purged automatically.
~/bin/tmp_trash_empty empties the trash directories under /tmp and $TMPDIR;
~/.profile runs it in the background with --periodic (at most once every 24h). Run
it by hand — tmp_trash_empty -n -v to preview, tmp_trash_empty to purge — if you
need the space back now.